Skip to content

Commit

Permalink
Fix bug and use dynamicModal for BeaconNotification
Browse files Browse the repository at this point in the history
  • Loading branch information
ryutamago committed Sep 15, 2023
1 parent b81ab3b commit 6e3d0f4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 115 deletions.
34 changes: 17 additions & 17 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { withSideMenu } from "./views/withSideMenu";
import HelpView from "./views/help/HelpView";
import AddressBookView from "./views/addressBook/AddressBookView";
import BatchPage from "./views/batch/BatchPage";
import { resetBeacon, useBeaconInit } from "./utils/beacon/beacon";
import { BeaconProvider, resetBeacon } from "./utils/beacon/beacon";
import TokensView from "./views/tokens/TokensView";
import { useDeeplinkHandler } from "./utils/useDeeplinkHandler";
import { AnnouncementBanner } from "./components/AnnouncementBanner";
Expand All @@ -39,27 +39,27 @@ const loggedOutRouter = createHashRouter([
]);

const MemoizedRouter = React.memo(() => {
const beaconNotificationModal = useBeaconInit();
const dynamicModal = useDynamicModal();

return (
<HashRouter>
<DynamicModalContext.Provider value={dynamicModal}>
<AnnouncementBanner />
<Routes>
<Route path="/home" element={withSideMenu(<HomeView />)} />
<Route path="/nfts" element={withSideMenu(<NFTsView />)} />
<Route path="/nfts/:ownerPkh/:nftId" element={withSideMenu(<NFTsView />)} />
<Route path="/operations" element={withSideMenu(<OperationsView />)} />
<Route path="/tokens" element={withSideMenu(<TokensView />)} />
<Route path="/address-book" element={withSideMenu(<AddressBookView />)} />
<Route path="/settings" element={withSideMenu(<SettingsView />)} />
<Route path="/help" element={withSideMenu(<HelpView />)} />
<Route path="/batch" element={withSideMenu(<BatchPage />)} />
<Route path="/*" element={<Navigate to="/home" />} />
</Routes>
{dynamicModal.content}
{beaconNotificationModal}
<BeaconProvider>
<AnnouncementBanner />
<Routes>
<Route path="/home" element={withSideMenu(<HomeView />)} />
<Route path="/nfts" element={withSideMenu(<NFTsView />)} />
<Route path="/nfts/:ownerPkh/:nftId" element={withSideMenu(<NFTsView />)} />
<Route path="/operations" element={withSideMenu(<OperationsView />)} />
<Route path="/tokens" element={withSideMenu(<TokensView />)} />
<Route path="/address-book" element={withSideMenu(<AddressBookView />)} />
<Route path="/settings" element={withSideMenu(<SettingsView />)} />
<Route path="/help" element={withSideMenu(<HelpView />)} />
<Route path="/batch" element={withSideMenu(<BatchPage />)} />
<Route path="/*" element={<Navigate to="/home" />} />
</Routes>
{dynamicModal.content}
</BeaconProvider>
</DynamicModalContext.Provider>
</HashRouter>
);
Expand Down
1 change: 0 additions & 1 deletion src/components/SendFlow/Beacon/BeaconSignPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const BeaconSignPage: React.FC<BeaconSignPageProps> = ({ operation, onBeaconSucc
case "fa1.2":
case "fa2":
case "contract_origination":
// this line will not reach, but better safe than sorry
throw new Error("Unsupported operation type");
}
};
Expand Down
23 changes: 14 additions & 9 deletions src/components/SendFlow/Beacon/useSignWithBeacon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export const useSignWithBeacon = (
operation: ImplicitOperations,
onBeaconSuccess: (hash: string) => Promise<void>
) => {
const { onClose } = useContext(DynamicModalContext);
const [fee, setFee] = useState<BigNumber | null>(null);
const network = useSelectedNetwork();
const { isLoading: isSigning, handleAsyncAction } = useAsyncActionHandler();
const { handleAsyncAction: handleFeeEstimation } = useAsyncActionHandler();
const { openWith } = useContext(DynamicModalContext);
const form = useForm<{ sender: string; signer: string }>({
mode: "onBlur",
Expand All @@ -27,20 +27,25 @@ export const useSignWithBeacon = (
});

useEffect(() => {
const estimateFee = () =>
handleFeeEstimation(
const estimateFee = () => {
handleAsyncAction(
async () => {
const fee = await estimate(operation, network);
setFee(fee);
},
err => ({
title: "Error",
description: `Error while processing beacon request: ${err.message}`,
status: "error",
})
err => {
onClose();
return {
title: "Error",
description: `Error while processing beacon request: ${err.message}`,
status: "error",
};
}
);
};
estimateFee();
}, [network, operation, handleFeeEstimation]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [network, operation]);

const onSign = async (tezosToolkit: TezosToolkit) =>
handleAsyncAction(async () => {
Expand Down
82 changes: 46 additions & 36 deletions src/utils/beacon/BeaconNotification/BeaconRequestNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ import {
BeaconRequestOutputMessage,
OperationRequestOutput,
OperationResponseInput,
PartialTezosOperation,
TezosOperationType,
} from "@airgap/beacon-wallet";
import { useToast } from "@chakra-ui/react";
import React from "react";
import { ImplicitOperations } from "../../../components/sendForm/types";
import {
isValidContractPkh,
parseContractPkh,
parseImplicitPkh,
parsePkh,
} from "../../../types/Address";
import { isValidContractPkh, parseContractPkh, parseImplicitPkh } from "../../../types/Address";
import { Operation } from "../../../types/Operation";
import { useGetImplicitAccountSafe } from "../../hooks/accountHooks";
import { walletClient } from "../beacon";
Expand Down Expand Up @@ -78,8 +74,50 @@ export const BeaconNotification: React.FC<{
}
};

const partialOperationToOperation = (
partialOperation: PartialTezosOperation,
signer: ImplicitAccount
): Operation | null => {
switch (partialOperation.kind) {
case TezosOperationType.TRANSACTION: {
const { destination, amount, parameters } = partialOperation;
const isContractCall = isValidContractPkh(destination) && parameters;
if (isContractCall) {
return {
type: "contract_call",
amount,
contract: parseContractPkh(destination),
entrypoint: parameters.entrypoint,
args: parameters.value,
};
} else {
return {
type: "tez",
amount,
recipient: parseImplicitPkh(partialOperation.destination),
};
}
}
case TezosOperationType.DELEGATION: {
const { delegate } = partialOperation;

if (delegate) {
return {
type: "delegation",
sender: signer.address,
recipient: parseImplicitPkh(delegate),
};
} else {
return { type: "undelegation", sender: signer.address };
}
}
default:
return null;
}
};

const toOperation = (
{ operationDetails, sourceAddress }: OperationRequestOutput,
{ operationDetails }: OperationRequestOutput,
signer: ImplicitAccount
): ImplicitOperations => {
if (operationDetails.length === 0) {
Expand All @@ -92,35 +130,7 @@ const toOperation = (

const partialOperation = operationDetails[0];

let operation: Operation | undefined = undefined;

if (partialOperation.kind === TezosOperationType.TRANSACTION) {
const { destination, amount, parameters } = partialOperation;
operation =
isValidContractPkh(destination) && parameters
? {
type: "contract_call",
amount,
contract: parseContractPkh(destination),
entrypoint: parameters.entrypoint,
args: parameters.value,
}
: {
type: "tez",
amount,
recipient: parseImplicitPkh(partialOperation.destination),
};
} else if (partialOperation.kind === TezosOperationType.DELEGATION) {
const sender = parsePkh(sourceAddress);
operation = partialOperation.delegate
? {
type: "delegation",
sender,
recipient: parseImplicitPkh(partialOperation.delegate),
}
: { type: "undelegation", sender };
}

const operation = partialOperationToOperation(operationDetails[0], signer);
if (!operation) {
throw new Error(`Unsupported operation: ${partialOperation.kind}`);
}
Expand Down
69 changes: 17 additions & 52 deletions src/utils/beacon/beacon.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import {
BeaconRequestOutputMessage,
ConnectionContext,
ExtendedP2PPairingResponse,
Serializer,
WalletClient,
} from "@airgap/beacon-wallet";
import { Modal, useDisclosure, useToast } from "@chakra-ui/react";
import { useEffect, useRef } from "react";
import { ExtendedP2PPairingResponse, Serializer, WalletClient } from "@airgap/beacon-wallet";
import { useToast } from "@chakra-ui/react";
import { useContext, useEffect } from "react";
import { useQuery, useQueryClient } from "react-query";
import { BeaconNotification } from "./BeaconNotification";
import { makePeerInfo, PeerInfo } from "./types";
import { DynamicModalContext } from "../../components/DynamicModal";

const makeClient = () =>
new WalletClient({
Expand Down Expand Up @@ -58,52 +53,22 @@ export const useAddPeer = () => {
};
};

export const useBeaconModalNotification = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const beaconMessage = useRef<BeaconRequestOutputMessage>();

return {
modalElement: (
<Modal isOpen={isOpen} onClose={onClose}>
{beaconMessage.current && (
<BeaconNotification message={beaconMessage.current} onClose={onClose} />
)}
</Modal>
),

onOpen: (message: BeaconRequestOutputMessage, _: ConnectionContext) => {
beaconMessage.current = message;
onOpen();
},
};
};

// Need this ignore BS because useEffect runs twice in development:
// https://react.dev/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development
export const useBeaconInit = () => {
const { modalElement: beaconModal, onOpen } = useBeaconModalNotification();
const ignore = useRef(false);
const handleBeaconMessage = useRef(onOpen);

export const BeaconProvider: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
const { openWith, onClose } = useContext(DynamicModalContext);
useEffect(() => {
if (!ignore.current) {
walletClient
.init()
.then(() => {
walletClient.connect(handleBeaconMessage.current);
})
.catch(console.error)
.finally(() => {
ignore.current = false;
walletClient
.init()
.then(() => {
walletClient.connect(message => {
openWith(<BeaconNotification message={message} onClose={onClose} />);
});
}

return () => {
ignore.current = true;
};
}, []);
})
.catch(console.error);
}, [onClose, openWith]);

return beaconModal;
return <>{children}</>;
};

export const resetBeacon = async () => {
Expand Down

0 comments on commit 6e3d0f4

Please sign in to comment.