From dfb11c0912ce3046a135f7f3290279fb0124dc0f Mon Sep 17 00:00:00 2001 From: Isha Date: Wed, 6 Nov 2024 15:47:56 +0530 Subject: [PATCH 01/27] Add iframe component --- .../components/projects/create/Configure.tsx | 5 +- .../src/components/projects/create/IFrame.tsx | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 packages/frontend/src/components/projects/create/IFrame.tsx diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 4dae836b..9e212b5a 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -24,6 +24,7 @@ import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/Enviro import { EnvironmentVariablesFormValues } from 'types/types'; import ConnectWallet from './ConnectWallet'; import { useWalletConnectClient } from 'context/WalletConnectContext'; +import IframeComponent from './IFrame'; type ConfigureDeploymentFormValues = { option: string; @@ -534,10 +535,8 @@ const Configure = () => { ) : ( <> - - Connect to your wallet - + {accounts.length > 0 && (
+ - {localStorageData ? ( -

LocalStorage Data: {localStorageData}

- ) : ( -

Loading localStorage data...

- )} ); }; diff --git a/packages/frontend/src/pages/org-slug/index.tsx b/packages/frontend/src/pages/org-slug/index.tsx index a496998b..a7125b79 100644 --- a/packages/frontend/src/pages/org-slug/index.tsx +++ b/packages/frontend/src/pages/org-slug/index.tsx @@ -23,48 +23,6 @@ const Projects = () => { fetchProjects(); }, [orgSlug]); - useEffect(() => { - const getDataFromWallet = () => { - const iframe = document.getElementById('walletIframe') as HTMLIFrameElement; - - if (!iframe.contentWindow) { - console.error('Iframe not found or not loaded'); - return; - } - - // Request data from wallet - iframe.contentWindow.postMessage({ - type: 'REQUEST_WALLET_ACCOUNTS' - }, 'http://localhost:3001'); - }; - - // Listen for response from wallet - const handleMessage = (event: MessageEvent) => { - // Always verify origin for security - if (event.origin !== 'http://localhost:3001') return; - - if (event.data.type === 'WALLET_ACCOUNTS_DATA') { - console.log('Received data:', event.data.data); - // Handle the received data here - } else if (event.data.type === 'ERROR') { - console.error('Error from wallet:', event.data.message); - } - }; - - window.addEventListener('message', handleMessage); - - // Request data once iframe is loaded - const iframe = document.getElementById('walletIframe'); - if (iframe) { - iframe.onload = getDataFromWallet; - } - - // Cleanup - return () => { - window.removeEventListener('message', handleMessage); - }; - }, []); - return (
{/* Header */} @@ -91,13 +49,6 @@ const Projects = () => { return ; })} - - + + + )} ); }; -export default IframeComponent; +export default IFrame; -- 2.45.2 From c74b7c597f21c13a83d154c1e7dec8c156c886b1 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 7 Nov 2024 10:58:16 +0530 Subject: [PATCH 05/27] Display wallet component in UI --- packages/frontend/src/components/projects/create/IFrame.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/projects/create/IFrame.tsx b/packages/frontend/src/components/projects/create/IFrame.tsx index 95c4e0dd..1329a43d 100644 --- a/packages/frontend/src/components/projects/create/IFrame.tsx +++ b/packages/frontend/src/components/projects/create/IFrame.tsx @@ -68,10 +68,9 @@ const IFrame = ({
+ > { - value && onAccountChange(value); - }} + onChange={(value) => value && onAccountChange(value)} className="dark:bg-overlay2 dark:text-foreground" + aria-label="Wallet Account Selector" > {accounts.map((account, index) => (
)} + ); }; -- 2.45.2 From f3645ab48792a9f52d69057fbb0bf84d3a0af53d Mon Sep 17 00:00:00 2001 From: Isha Date: Thu, 7 Nov 2024 13:04:04 +0530 Subject: [PATCH 07/27] Set iframe visiblity conditionally --- .../components/projects/create/Configure.tsx | 7 ++++-- .../src/components/projects/create/IFrame.tsx | 24 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 85207d62..7729b5a5 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -38,14 +38,16 @@ type ConfigureFormValues = ConfigureDeploymentFormValues & const DEFAULT_MAX_PRICE = '10000'; const Configure = () => { - const { signClient, session, accounts } = useWalletConnectClient(); + const { signClient, session } = useWalletConnectClient(); const [isLoading, setIsLoading] = useState(false); const [deployers, setDeployers] = useState([]); const [selectedAccount, setSelectedAccount] = useState(); + const [accounts, setAccounts] = useState([]); const [selectedDeployer, setSelectedDeployer] = useState(); const [isPaymentLoading, setIsPaymentLoading] = useState(false); const [isPaymentDone, setIsPaymentDone] = useState(false); + const [isFrameVisible, setIsFrameVisible] = useState(false); const [searchParams] = useSearchParams(); const templateId = searchParams.get('templateId'); @@ -175,6 +177,7 @@ const Configure = () => { const handleFormSubmit = useCallback( async (createFormData: FieldValues) => { try { + setIsFrameVisible(true); const deployerLrn = createFormData.lrn; const deployer = deployers.find( (deployer) => deployer.deployerLrn === deployerLrn, @@ -534,7 +537,7 @@ const Configure = () => { ) : ( <> - ); -- 2.45.2 From d184fbaeeddb86d11415096495f6ca65a80e2767 Mon Sep 17 00:00:00 2001 From: Nabarun Date: Thu, 7 Nov 2024 13:22:22 +0530 Subject: [PATCH 08/27] Remove console log project from frontend --- packages/frontend/src/components/projects/create/IFrame.tsx | 1 + .../frontend/src/pages/org-slug/projects/id/settings/General.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/projects/create/IFrame.tsx b/packages/frontend/src/components/projects/create/IFrame.tsx index 1d30010b..271e72b0 100644 --- a/packages/frontend/src/components/projects/create/IFrame.tsx +++ b/packages/frontend/src/components/projects/create/IFrame.tsx @@ -15,6 +15,7 @@ const IFrame = ({ }) => { useEffect(() => { const handleMessage = (event: MessageEvent) => { + // TODO: Use env for origin URL if (event.origin !== 'http://localhost:3001') return; if (event.data.type === 'WALLET_ACCOUNTS_DATA') { diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/General.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/General.tsx index 4b886151..7a257d8d 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/General.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/General.tsx @@ -18,7 +18,6 @@ const GeneralTabPanel = () => { const client = useGQLClient(); const { toast } = useToast(); const { project, onUpdate } = useOutletContext(); - console.log(project); const [transferOrganizations, setTransferOrganizations] = useState< SelectOption[] -- 2.45.2 From b5a109fdb28b287927c3660388d50ceee37539a4 Mon Sep 17 00:00:00 2001 From: Isha Date: Thu, 7 Nov 2024 14:46:01 +0530 Subject: [PATCH 09/27] Display IFrame in modal --- .../components/projects/create/Configure.tsx | 12 ++++- .../src/components/projects/create/IFrame.tsx | 49 +++++++++++++++---- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 7729b5a5..36661f53 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -286,6 +286,10 @@ const Configure = () => { [client, createProject, dismiss, toast], ); + const toggleModal = () => { + setIsFrameVisible((prev) => !prev); + }; + const fetchDeployers = useCallback(async () => { const res = await client.getDeployers(); setDeployers(res.deployers); @@ -537,7 +541,13 @@ const Configure = () => { ) : ( <> - + + ); -- 2.45.2 From 3a087cbf8dbbb2448f270ca402a34ce05880b179 Mon Sep 17 00:00:00 2001 From: Isha Date: Thu, 7 Nov 2024 19:54:11 +0530 Subject: [PATCH 10/27] Add methods for sending tx and receiving tx hash --- .../components/projects/create/Configure.tsx | 104 ++++++++++++------ .../src/components/projects/create/IFrame.tsx | 6 +- 2 files changed, 73 insertions(+), 37 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 36661f53..67f7bc53 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -24,6 +24,7 @@ import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/Enviro import { EnvironmentVariablesFormValues } from 'types/types'; import IFrame from './IFrame'; import { useWalletConnectClient } from 'context/WalletConnectContext'; +import { VITE_LACONICD_CHAIN_ID } from 'utils/constants'; type ConfigureDeploymentFormValues = { option: string; @@ -48,6 +49,8 @@ const Configure = () => { const [isPaymentLoading, setIsPaymentLoading] = useState(false); const [isPaymentDone, setIsPaymentDone] = useState(false); const [isFrameVisible, setIsFrameVisible] = useState(false); + const [txHashr, setTxHash] = useState(null); + const [isTransactionPending, setIsTransactionPending] = useState(false); const [searchParams] = useSearchParams(); const templateId = searchParams.get('templateId'); @@ -211,17 +214,19 @@ const Configure = () => { const amountToBePaid = amount.replace(/\D/g, '').toString(); - const txHashResponse = await cosmosSendTokensHandler( + await cosmosSendTokensHandler( selectedAccount, amountToBePaid, ); - if (!txHashResponse) { + await new Promise((resolve) => setTimeout(resolve, 3000)); + + if (!txHashr) { console.error('Tx not successful'); return; } - txHash = txHashResponse; + txHash = txHashr; const isTxHashValid = await verifyTx( senderAddress, @@ -309,12 +314,11 @@ const Configure = () => { const cosmosSendTokensHandler = useCallback( async (selectedAccount: string, amount: string) => { - if (!signClient || !session || !selectedAccount) { + if (!selectedAccount) { return; } - const chainId = selectedAccount.split(':')[1]; - const senderAddress = selectedAccount.split(':')[2]; + const senderAddress = selectedAccount; const snowballAddress = await client.getAddress(); try { @@ -328,35 +332,10 @@ const Configure = () => { onDismiss: dismiss, }); - const result: { signature: string } = await signClient.request({ - topic: session.topic, - chainId: `cosmos:${chainId}`, - request: { - method: 'cosmos_sendTokens', - params: [ - { - from: senderAddress, - to: snowballAddress, - value: amount, - }, - ], - }, - }); + await requestTx(senderAddress, snowballAddress, amount) - if (!result) { - throw new Error('Error completing transaction'); - } - - toast({ - id: 'payment-successful', - title: 'Payment successful', - variant: 'success', - onDismiss: dismiss, - }); - - setIsPaymentDone(true); - - return result.signature; + await new Promise((resolve) => setTimeout(resolve, 10000)); + return; } catch (error: any) { console.error('Error sending tokens', error); @@ -368,6 +347,7 @@ const Configure = () => { }); setIsPaymentDone(false); + // should return the txhash but we arw checking for it in the gettxstatus useeffect.. how do i return it after it is found? } finally { setIsPaymentLoading(false); } @@ -375,6 +355,62 @@ const Configure = () => { [session, signClient, toast], ); + const requestTx = async (sender: string, recipient: string, amount: string,) => { + const iframe = document.getElementById('walletIframe') as HTMLIFrameElement; + + if (!iframe.contentWindow) { + console.error('Iframe not found or not loaded'); + return; + } + + iframe.contentWindow.postMessage( + { + type: 'REQUEST_TX', + chainId: VITE_LACONICD_CHAIN_ID, + fromAddress: sender, + toAddress: recipient, + amount, + }, + 'http://localhost:3001' + ); + }; + + useEffect(() => { + // Define the event listener to listen for transaction status + const getTxStatus = (event: MessageEvent) => { + // Ensure you're handling the message only from the correct origin + if (event.origin !== 'http://localhost:3001') return; + + if (event.data.type === 'TRANSACTION_SUCCESS') { + console.log('Transaction Success:', event.data.transactionHash); + setTxHash(event.data.transactionHash); + console.log(event.data.transactionHash) + setIsTransactionPending(false); + + console.log({txHashr}) + toast({ + id: 'payment-successful', + title: 'Payment successful', + variant: 'success', + onDismiss: dismiss, + }); + + setIsPaymentDone(true); + } else if (event.data.type === 'ERROR') { + console.error('Error from wallet:', event.data.message); + setIsTransactionPending(false); // Mark the transaction as failed + } + }; + + // Add the event listener to listen for 'message' events + window.addEventListener('message', getTxStatus); + + // Cleanup: Remove the event listener when the component unmounts + return () => { + window.removeEventListener('message', getTxStatus); + }; + }, []); + useEffect(() => { fetchDeployers(); }, []); diff --git a/packages/frontend/src/components/projects/create/IFrame.tsx b/packages/frontend/src/components/projects/create/IFrame.tsx index f854e7e3..a6764ea2 100644 --- a/packages/frontend/src/components/projects/create/IFrame.tsx +++ b/packages/frontend/src/components/projects/create/IFrame.tsx @@ -88,7 +88,7 @@ const IFrame = ({ )} - + - + > */} ); }; -- 2.45.2 From 6746b6879335956dc28a6c121766714aa4707206 Mon Sep 17 00:00:00 2001 From: Isha Date: Fri, 8 Nov 2024 09:57:54 +0530 Subject: [PATCH 11/27] Add listener for successful tx --- .../components/projects/create/Configure.tsx | 162 ++++++++---------- 1 file changed, 73 insertions(+), 89 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 67f7bc53..5a13f54d 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -20,10 +20,9 @@ import { Button } from '../../shared/Button'; import { Input } from 'components/shared/Input'; import { useToast } from 'components/shared/Toast'; import { useGQLClient } from '../../../context/GQLClientContext'; +import IFrame from './IFrame'; import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm'; import { EnvironmentVariablesFormValues } from 'types/types'; -import IFrame from './IFrame'; -import { useWalletConnectClient } from 'context/WalletConnectContext'; import { VITE_LACONICD_CHAIN_ID } from 'utils/constants'; type ConfigureDeploymentFormValues = { @@ -39,8 +38,6 @@ type ConfigureFormValues = ConfigureDeploymentFormValues & const DEFAULT_MAX_PRICE = '10000'; const Configure = () => { - const { signClient, session } = useWalletConnectClient(); - const [isLoading, setIsLoading] = useState(false); const [deployers, setDeployers] = useState([]); const [selectedAccount, setSelectedAccount] = useState(); @@ -49,8 +46,6 @@ const Configure = () => { const [isPaymentLoading, setIsPaymentLoading] = useState(false); const [isPaymentDone, setIsPaymentDone] = useState(false); const [isFrameVisible, setIsFrameVisible] = useState(false); - const [txHashr, setTxHash] = useState(null); - const [isTransactionPending, setIsTransactionPending] = useState(false); const [searchParams] = useSearchParams(); const templateId = searchParams.get('templateId'); @@ -188,7 +183,7 @@ const Configure = () => { let amount: string; let senderAddress: string; - let txHash: string; + let txHash; if (createFormData.option === 'LRN' && !deployer?.minimumPayment) { toast({ id: 'no-payment-required', @@ -202,7 +197,7 @@ const Configure = () => { } else { if (!selectedAccount) return; - senderAddress = selectedAccount.split(':')[2]; + senderAddress = selectedAccount; if (createFormData.option === 'LRN') { amount = deployer?.minimumPayment!; @@ -214,30 +209,10 @@ const Configure = () => { const amountToBePaid = amount.replace(/\D/g, '').toString(); - await cosmosSendTokensHandler( - selectedAccount, + txHash = await cosmosSendTokensHandler( + senderAddress, amountToBePaid, ); - - await new Promise((resolve) => setTimeout(resolve, 3000)); - - if (!txHashr) { - console.error('Tx not successful'); - return; - } - - txHash = txHashr; - - const isTxHashValid = await verifyTx( - senderAddress, - txHash, - amountToBePaid.toString(), - ); - - if (isTxHashValid === false) { - console.error('Invalid Tx hash', txHash); - return; - } } const environmentVariables = createFormData.variables.map( @@ -256,7 +231,7 @@ const Configure = () => { createFormData, environmentVariables, senderAddress, - txHash, + txHash!, ); await client.getEnvironmentVariables(projectId); @@ -264,19 +239,19 @@ const Configure = () => { if (templateId) { createFormData.option === 'Auction' ? navigate( - `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, - ) + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, + ) : navigate( - `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}`, - ); + `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}`, + ); } else { createFormData.option === 'Auction' ? navigate( - `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, - ) + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, + ) : navigate( - `/${orgSlug}/projects/create/deploy?projectId=${projectId}`, - ); + `/${orgSlug}/projects/create/deploy?projectId=${projectId}`, + ); } } catch (error) { console.error(error); @@ -332,27 +307,72 @@ const Configure = () => { onDismiss: dismiss, }); - await requestTx(senderAddress, snowballAddress, amount) + await requestTx(senderAddress, snowballAddress, amount); - await new Promise((resolve) => setTimeout(resolve, 10000)); - return; - } catch (error: any) { - console.error('Error sending tokens', error); + const txHash = await new Promise((resolve, reject) => { + const handleTxStatus = async (event: MessageEvent) => { + if (event.origin !== 'http://localhost:3001') return; - toast({ - id: 'error-sending-tokens', - title: 'Error sending tokens', - variant: 'error', - onDismiss: dismiss, + if (event.data.type === 'TRANSACTION_SUCCESS') { + const txResponse = event.data.data; + + setIsFrameVisible(false); + + // Validate transaction hash + const isTxHashValid = await verifyTx( + senderAddress, + txResponse, + amount, + ); + + if (isTxHashValid) { + resolve(txResponse); + toast({ + id: 'payment-successful', + title: 'Payment successful', + variant: 'success', + onDismiss: dismiss, + }); + } else { + reject(new Error('Invalid transaction hash')); + toast({ + id: 'invalid-tx-hash', + title: 'Transaction validation failed', + variant: 'error', + onDismiss: dismiss, + }); + } + } else if (event.data.type === 'ERROR') { + console.error('Error from wallet:', event.data.message); + reject(new Error('Transaction failed')); + toast({ + id: 'error-transaction', + title: 'Error during transaction', + variant: 'error', + onDismiss: dismiss, + }); + } + + window.removeEventListener('message', handleTxStatus); + }; + + window.addEventListener('message', handleTxStatus); + + // Set a timeout, consider unsuccessful after 1 min + setTimeout(() => { + reject(new Error('Transaction timeout')); + window.removeEventListener('message', handleTxStatus); + }, 60000); }); - - setIsPaymentDone(false); - // should return the txhash but we arw checking for it in the gettxstatus useeffect.. how do i return it after it is found? + return txHash; + } catch (error) { + console.error('Error in transaction:', error); + return; } finally { setIsPaymentLoading(false); } }, - [session, signClient, toast], + [client, dismiss, toast], ); const requestTx = async (sender: string, recipient: string, amount: string,) => { @@ -375,42 +395,6 @@ const Configure = () => { ); }; - useEffect(() => { - // Define the event listener to listen for transaction status - const getTxStatus = (event: MessageEvent) => { - // Ensure you're handling the message only from the correct origin - if (event.origin !== 'http://localhost:3001') return; - - if (event.data.type === 'TRANSACTION_SUCCESS') { - console.log('Transaction Success:', event.data.transactionHash); - setTxHash(event.data.transactionHash); - console.log(event.data.transactionHash) - setIsTransactionPending(false); - - console.log({txHashr}) - toast({ - id: 'payment-successful', - title: 'Payment successful', - variant: 'success', - onDismiss: dismiss, - }); - - setIsPaymentDone(true); - } else if (event.data.type === 'ERROR') { - console.error('Error from wallet:', event.data.message); - setIsTransactionPending(false); // Mark the transaction as failed - } - }; - - // Add the event listener to listen for 'message' events - window.addEventListener('message', getTxStatus); - - // Cleanup: Remove the event listener when the component unmounts - return () => { - window.removeEventListener('message', getTxStatus); - }; - }, []); - useEffect(() => { fetchDeployers(); }, []); -- 2.45.2 From 51d7dd7c57507ceec72c371cdc415a830c2735d3 Mon Sep 17 00:00:00 2001 From: Isha Date: Fri, 8 Nov 2024 10:33:33 +0530 Subject: [PATCH 12/27] Fix payment request modal UI --- .../components/projects/create/Configure.tsx | 2 +- .../src/components/projects/create/IFrame.tsx | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 5a13f54d..6d29d1de 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -302,7 +302,7 @@ const Configure = () => { toast({ id: 'sending-payment-request', - title: 'Check your wallet and approve payment request', + title: 'Check and approve payment request', variant: 'loading', onDismiss: dismiss, }); diff --git a/packages/frontend/src/components/projects/create/IFrame.tsx b/packages/frontend/src/components/projects/create/IFrame.tsx index a6764ea2..e31d8baa 100644 --- a/packages/frontend/src/components/projects/create/IFrame.tsx +++ b/packages/frontend/src/components/projects/create/IFrame.tsx @@ -95,10 +95,15 @@ const IFrame = ({ top: '50%', left: '50%', transform: 'translate(-50%, -50%)', - width: '800px', + width: '90%', + maxWidth: '1200px', + height: '600px', + maxHeight: '80vh', + overflow: 'auto', boxShadow: 24, borderRadius: '8px', outline: 'none', + bgcolor: 'background.paper', }} > - {/* */} ); }; -- 2.45.2 From 97fa1988fbe6c10e40a4210556240307b2942623 Mon Sep 17 00:00:00 2001 From: Isha Date: Fri, 8 Nov 2024 12:26:04 +0530 Subject: [PATCH 13/27] Keep iframe modal in separate component --- packages/frontend/.env.example | 1 + .../projects/create/AccountsDropdown.tsx | 49 ++++ .../components/projects/create/Configure.tsx | 87 ++++---- .../src/components/projects/create/IFrame.tsx | 124 ----------- .../projects/create/IFrameModal.tsx | 85 +++++++ .../src/context/WalletConnectContext.tsx | 210 ------------------ packages/frontend/src/index.tsx | 23 +- packages/frontend/src/utils/constants.ts | 1 + 8 files changed, 191 insertions(+), 389 deletions(-) create mode 100644 packages/frontend/src/components/projects/create/AccountsDropdown.tsx delete mode 100644 packages/frontend/src/components/projects/create/IFrame.tsx create mode 100644 packages/frontend/src/components/projects/create/IFrameModal.tsx delete mode 100644 packages/frontend/src/context/WalletConnectContext.tsx diff --git a/packages/frontend/.env.example b/packages/frontend/.env.example index 88f6bc1f..75671697 100644 --- a/packages/frontend/.env.example +++ b/packages/frontend/.env.example @@ -15,3 +15,4 @@ VITE_PASSKEY_WALLET_RPID= VITE_TURNKEY_API_BASE_URL= VITE_LACONICD_CHAIN_ID= +VITE_IFRAME_ORIGIN_URL= diff --git a/packages/frontend/src/components/projects/create/AccountsDropdown.tsx b/packages/frontend/src/components/projects/create/AccountsDropdown.tsx new file mode 100644 index 00000000..e404b093 --- /dev/null +++ b/packages/frontend/src/components/projects/create/AccountsDropdown.tsx @@ -0,0 +1,49 @@ +import { Select, Option } from '@snowballtools/material-tailwind-react-fork'; + +const AccountsDropdown = ({ + accounts, + onAccountChange, +}: { + accounts: string[]; + onAccountChange: (selectedAccount: string) => void; +}) => { + + return ( +
+ {!accounts.length ? ( +
+

+ No accounts found. Please visit{' '} + + store.laconic.com + {' '} + to create a wallet. +

+
+ ) : ( +
+ +
+ )} +
+ ); +}; + +export default AccountsDropdown; diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 6d29d1de..a8731ba5 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -20,10 +20,11 @@ import { Button } from '../../shared/Button'; import { Input } from 'components/shared/Input'; import { useToast } from 'components/shared/Toast'; import { useGQLClient } from '../../../context/GQLClientContext'; -import IFrame from './IFrame'; +import IFrameModal from './IFrameModal'; import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm'; import { EnvironmentVariablesFormValues } from 'types/types'; -import { VITE_LACONICD_CHAIN_ID } from 'utils/constants'; +import { VITE_LACONICD_CHAIN_ID, VITE_IFRAME_ORIGIN_URL } from 'utils/constants'; +import AccountsDropdown from './AccountsDropdown'; type ConfigureDeploymentFormValues = { option: string; @@ -36,6 +37,7 @@ type ConfigureFormValues = ConfigureDeploymentFormValues & EnvironmentVariablesFormValues; const DEFAULT_MAX_PRICE = '10000'; +const TX_APPROVAL_TIMEOUT = 60000; const Configure = () => { const [isLoading, setIsLoading] = useState(false); @@ -175,7 +177,6 @@ const Configure = () => { const handleFormSubmit = useCallback( async (createFormData: FieldValues) => { try { - setIsFrameVisible(true); const deployerLrn = createFormData.lrn; const deployer = deployers.find( (deployer) => deployer.deployerLrn === deployerLrn, @@ -213,6 +214,33 @@ const Configure = () => { senderAddress, amountToBePaid, ); + + if (!txHash) { + return; + } + + // Validate transaction hash + const isTxHashValid = await verifyTx( + senderAddress, + txHash, + amount, + ); + + if (isTxHashValid) { + toast({ + id: 'payment-successful', + title: 'Payment successful', + variant: 'success', + onDismiss: dismiss, + }); + } else { + toast({ + id: 'invalid-tx-hash', + title: 'Transaction validation failed', + variant: 'error', + onDismiss: dismiss, + }); + } } const environmentVariables = createFormData.variables.map( @@ -300,48 +328,16 @@ const Configure = () => { setIsPaymentDone(false); setIsPaymentLoading(true); - toast({ - id: 'sending-payment-request', - title: 'Check and approve payment request', - variant: 'loading', - onDismiss: dismiss, - }); - await requestTx(senderAddress, snowballAddress, amount); const txHash = await new Promise((resolve, reject) => { const handleTxStatus = async (event: MessageEvent) => { - if (event.origin !== 'http://localhost:3001') return; + if (event.origin !== VITE_IFRAME_ORIGIN_URL) return; if (event.data.type === 'TRANSACTION_SUCCESS') { const txResponse = event.data.data; - + resolve(txResponse); setIsFrameVisible(false); - - // Validate transaction hash - const isTxHashValid = await verifyTx( - senderAddress, - txResponse, - amount, - ); - - if (isTxHashValid) { - resolve(txResponse); - toast({ - id: 'payment-successful', - title: 'Payment successful', - variant: 'success', - onDismiss: dismiss, - }); - } else { - reject(new Error('Invalid transaction hash')); - toast({ - id: 'invalid-tx-hash', - title: 'Transaction validation failed', - variant: 'error', - onDismiss: dismiss, - }); - } } else if (event.data.type === 'ERROR') { console.error('Error from wallet:', event.data.message); reject(new Error('Transaction failed')); @@ -362,7 +358,13 @@ const Configure = () => { setTimeout(() => { reject(new Error('Transaction timeout')); window.removeEventListener('message', handleTxStatus); - }, 60000); + toast({ + id: 'transaction-timeout', + title: 'The transaction request timed out. Please try again', + variant: 'error', + onDismiss: dismiss, + }); + }, TX_APPROVAL_TIMEOUT); }); return txHash; } catch (error) { @@ -391,8 +393,10 @@ const Configure = () => { toAddress: recipient, amount, }, - 'http://localhost:3001' + VITE_IFRAME_ORIGIN_URL ); + + setIsFrameVisible(true); }; useEffect(() => { @@ -561,10 +565,9 @@ const Configure = () => { ) : ( <> - - - - - ); -}; - -export default IFrame; diff --git a/packages/frontend/src/components/projects/create/IFrameModal.tsx b/packages/frontend/src/components/projects/create/IFrameModal.tsx new file mode 100644 index 00000000..b7db68e5 --- /dev/null +++ b/packages/frontend/src/components/projects/create/IFrameModal.tsx @@ -0,0 +1,85 @@ +import { useCallback, useEffect } from 'react'; + +import { Box, Modal } from '@mui/material'; + +import { VITE_LACONICD_CHAIN_ID, VITE_IFRAME_ORIGIN_URL } from 'utils/constants'; + +const IFrameModal = ({ + setAccounts, + isVisible, + toggleModal, +}: { + setAccounts: (accounts: string[]) => void; + isVisible: boolean; + toggleModal: () => void; +}) => { + useEffect(() => { + const handleMessage = (event: MessageEvent) => { + // TODO: Use env for origin URL + if (event.origin !== VITE_IFRAME_ORIGIN_URL) return; + + if (event.data.type === 'WALLET_ACCOUNTS_DATA') { + setAccounts(event.data.data); + } else if (event.data.type === 'ERROR') { + console.error('Error from wallet:', event.data.message); + } + }; + + window.addEventListener('message', handleMessage); + + return () => { + window.removeEventListener('message', handleMessage); + }; + }, []); + + const getDataFromWallet = useCallback(() => { + const iframe = document.getElementById('walletIframe') as HTMLIFrameElement; + + if (!iframe.contentWindow) { + console.error('Iframe not found or not loaded'); + return; + } + + iframe.contentWindow.postMessage( + { + type: 'REQUEST_WALLET_ACCOUNTS', + chainId: VITE_LACONICD_CHAIN_ID, + }, + VITE_IFRAME_ORIGIN_URL + ); + }, []); + + return ( + + + + + + ) +}; + +export default IFrameModal; diff --git a/packages/frontend/src/context/WalletConnectContext.tsx b/packages/frontend/src/context/WalletConnectContext.tsx deleted file mode 100644 index e5dc4fc9..00000000 --- a/packages/frontend/src/context/WalletConnectContext.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { - createContext, - useCallback, - useContext, - useEffect, - useRef, - useState, -} from 'react'; - -import SignClient from '@walletconnect/sign-client'; -import { getSdkError } from '@walletconnect/utils'; -import { SessionTypes } from '@walletconnect/types'; - -import { walletConnectModal } from '../utils/web3modal'; -import { - VITE_LACONICD_CHAIN_ID, - VITE_WALLET_CONNECT_ID, -} from 'utils/constants'; - -interface ClientInterface { - signClient: SignClient | undefined; - session: SessionTypes.Struct | undefined; - loadingSession: boolean; - onConnect: () => Promise; - onDisconnect: () => Promise; - onSessionDelete: () => void; - accounts: { address: string }[]; -} - -const ClientContext = createContext({} as ClientInterface); - -export const useWalletConnectClient = () => { - return useContext(ClientContext); -}; - -export const WalletConnectClientProvider = ({ - children, -}: { - children: JSX.Element; -}) => { - const [signClient, setSignClient] = useState(); - const [session, setSession] = useState(); - const [loadingSession, setLoadingSession] = useState(true); - const [accounts, setAccounts] = useState<{ address: string }[]>([]); - - const isSignClientInitializing = useRef(false); - - const onSessionConnect = useCallback(async (session: SessionTypes.Struct) => { - setSession(session); - }, []); - - const subscribeToEvents = useCallback( - async (client: SignClient) => { - client.on('session_update', ({ topic, params }) => { - const { namespaces } = params; - const currentSession = client.session.get(topic); - const updatedSession = { ...currentSession, namespaces }; - setSession(updatedSession); - }); - }, - [setSession], - ); - - const onConnect = async () => { - const proposalNamespace = { - cosmos: { - methods: ['cosmos_sendTokens'], - chains: [`cosmos:${VITE_LACONICD_CHAIN_ID}`], - events: [], - }, - }; - - try { - const { uri, approval } = await signClient!.connect({ - requiredNamespaces: proposalNamespace, - }); - - if (uri) { - walletConnectModal.openModal({ uri }); - const session = await approval(); - onSessionConnect(session); - walletConnectModal.closeModal(); - } - } catch (e) { - console.error(e); - } - }; - - const onDisconnect = useCallback(async () => { - if (typeof signClient === 'undefined') { - throw new Error('WalletConnect is not initialized'); - } - if (typeof session === 'undefined') { - throw new Error('Session is not connected'); - } - - await signClient.disconnect({ - topic: session.topic, - reason: getSdkError('USER_DISCONNECTED'), - }); - - onSessionDelete(); - }, [signClient, session]); - - const onSessionDelete = () => { - setAccounts([]); - setSession(undefined); - }; - - const checkPersistedState = useCallback( - async (signClient: SignClient) => { - if (typeof signClient === 'undefined') { - throw new Error('WalletConnect is not initialized'); - } - - if (typeof session !== 'undefined') return; - if (signClient.session.length) { - const lastKeyIndex = signClient.session.keys.length - 1; - const previousSsession = signClient.session.get( - signClient.session.keys[lastKeyIndex], - ); - - await onSessionConnect(previousSsession); - return previousSsession; - } - }, - [session, onSessionConnect], - ); - - const createClient = useCallback(async () => { - isSignClientInitializing.current = true; - try { - const signClient = await SignClient.init({ - projectId: VITE_WALLET_CONNECT_ID, - metadata: { - name: 'Deploy App', - description: '', - url: window.location.href, - icons: ['https://avatars.githubusercontent.com/u/92608123'], - }, - }); - - setSignClient(signClient); - await checkPersistedState(signClient); - await subscribeToEvents(signClient); - setLoadingSession(false); - } catch (e) { - console.error('error in createClient', e); - } - isSignClientInitializing.current = false; - }, [setSignClient, checkPersistedState, subscribeToEvents]); - - useEffect(() => { - if (!signClient && !isSignClientInitializing.current) { - createClient(); - } - }, [signClient, createClient]); - - useEffect(() => { - const populateAccounts = async () => { - if (!session) { - return; - } - if (!session.namespaces['cosmos']) { - console.log('Accounts for cosmos namespace not found'); - return; - } - - const cosmosAddresses = session.namespaces['cosmos'].accounts; - - const cosmosAccounts = cosmosAddresses.map((address) => ({ - address, - })); - - const allAccounts = cosmosAccounts; - - setAccounts(allAccounts); - }; - - populateAccounts(); - }, [session]); - - useEffect(() => { - if (!signClient) { - return; - } - - signClient.on('session_delete', onSessionDelete); - - return () => { - signClient.off('session_delete', onSessionDelete); - }; - }); - - return ( - - {children} - - ); -}; diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx index 787cef61..862eaf67 100644 --- a/packages/frontend/src/index.tsx +++ b/packages/frontend/src/index.tsx @@ -4,10 +4,9 @@ import assert from 'assert'; import { GQLClient } from 'gql-client'; import { ThemeProvider } from '@snowballtools/material-tailwind-react-fork'; - -import './index.css'; import '@fontsource/inter'; import '@fontsource-variable/jetbrains-mono'; + import App from './App'; import reportWebVitals from './reportWebVitals'; import { GQLClientProvider } from './context/GQLClientContext'; @@ -16,7 +15,7 @@ import { Toaster } from 'components/shared/Toast'; import { LogErrorBoundary } from 'utils/log-error'; import { BASE_URL } from 'utils/constants'; import Web3ModalProvider from './context/Web3Provider'; -import { WalletConnectClientProvider } from 'context/WalletConnectContext'; +import './index.css'; console.log(`v-0.0.9`); @@ -32,16 +31,14 @@ const gqlClient = new GQLClient({ gqlEndpoint }); root.render( - - - - - - - - - - + + + + + + + + , ); diff --git a/packages/frontend/src/utils/constants.ts b/packages/frontend/src/utils/constants.ts index 446d9d58..5a0d23ff 100644 --- a/packages/frontend/src/utils/constants.ts +++ b/packages/frontend/src/utils/constants.ts @@ -12,3 +12,4 @@ export const VITE_WALLET_CONNECT_ID = import.meta.env.VITE_WALLET_CONNECT_ID; export const VITE_BUGSNAG_API_KEY = import.meta.env.VITE_BUGSNAG_API_KEY; export const VITE_LIT_RELAY_API_KEY = import.meta.env.VITE_LIT_RELAY_API_KEY; export const VITE_LACONICD_CHAIN_ID = import.meta.env.VITE_LACONICD_CHAIN_ID; +export const VITE_IFRAME_ORIGIN_URL = import.meta.env.VITE_IFRAME_ORIGIN_URL; -- 2.45.2 From 49c204affd3faffd7cb76533952615a62e6f7f07 Mon Sep 17 00:00:00 2001 From: Isha Date: Fri, 8 Nov 2024 14:40:12 +0530 Subject: [PATCH 14/27] Add toast for failed txs --- build-webapp.sh | 1 + .../application-deployment-request.yml | 2 ++ packages/frontend/.env.example | 2 +- .../projects/create/AccountsDropdown.tsx | 1 - .../components/projects/create/Configure.tsx | 28 ++++++++++++------- .../projects/create/IFrameModal.tsx | 8 +++--- packages/frontend/src/utils/constants.ts | 2 +- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/build-webapp.sh b/build-webapp.sh index e6476e6f..b289f28f 100755 --- a/build-webapp.sh +++ b/build-webapp.sh @@ -17,6 +17,7 @@ VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO = 'LACONIC_HOSTED_CONFIG_github_image VITE_GITHUB_NEXT_APP_TEMPLATE_REPO = 'LACONIC_HOSTED_CONFIG_github_next_app_templaterepo' VITE_WALLET_CONNECT_ID = 'LACONIC_HOSTED_CONFIG_wallet_connect_id' VITE_LACONICD_CHAIN_ID = 'LACONIC_HOSTED_CONFIG_laconicd_chain_id' +VITE_WALLET_IFRAME_URL = 'LACONIC_HOSTED_CONFIG_wallet_iframe_url' VITE_LIT_RELAY_API_KEY = 'LACONIC_HOSTED_CONFIG_lit_relay_api_key' VITE_BUGSNAG_API_KEY = 'LACONIC_HOSTED_CONFIG_bugsnag_api_key' VITE_PASSKEY_WALLET_RPID = 'LACONIC_HOSTED_CONFIG_passkey_wallet_rpid' diff --git a/packages/deployer/staging-records/application-deployment-request.yml b/packages/deployer/staging-records/application-deployment-request.yml index fd7ee288..897c47a4 100644 --- a/packages/deployer/staging-records/application-deployment-request.yml +++ b/packages/deployer/staging-records/application-deployment-request.yml @@ -18,6 +18,8 @@ record: LACONIC_HOSTED_CONFIG_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073 LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: dashboard.staging.apps.snowballtools.com LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com + LACONIC_HOSTED_CONFIG_laconicd_chain_id: + LACONIC_HOSTED_CONFIG_wallet_iframe_url: LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591 meta: note: Added by Snowball @ Mon Jun 24 23:51:48 UTC 2024 diff --git a/packages/frontend/.env.example b/packages/frontend/.env.example index 75671697..62241552 100644 --- a/packages/frontend/.env.example +++ b/packages/frontend/.env.example @@ -15,4 +15,4 @@ VITE_PASSKEY_WALLET_RPID= VITE_TURNKEY_API_BASE_URL= VITE_LACONICD_CHAIN_ID= -VITE_IFRAME_ORIGIN_URL= +VITE_WALLET_IFRAME_URL= diff --git a/packages/frontend/src/components/projects/create/AccountsDropdown.tsx b/packages/frontend/src/components/projects/create/AccountsDropdown.tsx index e404b093..d885a018 100644 --- a/packages/frontend/src/components/projects/create/AccountsDropdown.tsx +++ b/packages/frontend/src/components/projects/create/AccountsDropdown.tsx @@ -7,7 +7,6 @@ const AccountsDropdown = ({ accounts: string[]; onAccountChange: (selectedAccount: string) => void; }) => { - return (
{!accounts.length ? ( diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index a8731ba5..47cc2c04 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -23,7 +23,7 @@ import { useGQLClient } from '../../../context/GQLClientContext'; import IFrameModal from './IFrameModal'; import EnvironmentVariablesForm from 'pages/org-slug/projects/id/settings/EnvironmentVariablesForm'; import { EnvironmentVariablesFormValues } from 'types/types'; -import { VITE_LACONICD_CHAIN_ID, VITE_IFRAME_ORIGIN_URL } from 'utils/constants'; +import { VITE_LACONICD_CHAIN_ID, VITE_WALLET_IFRAME_URL } from 'utils/constants'; import AccountsDropdown from './AccountsDropdown'; type ConfigureDeploymentFormValues = { @@ -37,7 +37,7 @@ type ConfigureFormValues = ConfigureDeploymentFormValues & EnvironmentVariablesFormValues; const DEFAULT_MAX_PRICE = '10000'; -const TX_APPROVAL_TIMEOUT = 60000; +const TX_APPROVAL_TIMEOUT_MS = 60000; const Configure = () => { const [isLoading, setIsLoading] = useState(false); @@ -216,6 +216,13 @@ const Configure = () => { ); if (!txHash) { + console.error('Tx not successful'); + toast({ + id: 'unsuccessful-tx', + title: 'Transaction rejected', + variant: 'error', + onDismiss: dismiss, + }); return; } @@ -289,6 +296,7 @@ const Configure = () => { variant: 'error', onDismiss: dismiss, }); + throw new Error('Error deploying app'); } }, [client, createProject, dismiss, toast], @@ -318,7 +326,7 @@ const Configure = () => { const cosmosSendTokensHandler = useCallback( async (selectedAccount: string, amount: string) => { if (!selectedAccount) { - return; + throw new Error('Account not selected'); } const senderAddress = selectedAccount; @@ -330,14 +338,13 @@ const Configure = () => { await requestTx(senderAddress, snowballAddress, amount); - const txHash = await new Promise((resolve, reject) => { + const txHash = await new Promise((resolve, reject) => { const handleTxStatus = async (event: MessageEvent) => { - if (event.origin !== VITE_IFRAME_ORIGIN_URL) return; + if (event.origin !== VITE_WALLET_IFRAME_URL) return; - if (event.data.type === 'TRANSACTION_SUCCESS') { + if (event.data.type === 'TRANSACTION_RESPONSE') { const txResponse = event.data.data; resolve(txResponse); - setIsFrameVisible(false); } else if (event.data.type === 'ERROR') { console.error('Error from wallet:', event.data.message); reject(new Error('Transaction failed')); @@ -348,6 +355,7 @@ const Configure = () => { onDismiss: dismiss, }); } + setIsFrameVisible(false); window.removeEventListener('message', handleTxStatus); }; @@ -364,12 +372,12 @@ const Configure = () => { variant: 'error', onDismiss: dismiss, }); - }, TX_APPROVAL_TIMEOUT); + }, TX_APPROVAL_TIMEOUT_MS); }); return txHash; } catch (error) { console.error('Error in transaction:', error); - return; + throw new Error('Error in transaction:'); } finally { setIsPaymentLoading(false); } @@ -393,7 +401,7 @@ const Configure = () => { toAddress: recipient, amount, }, - VITE_IFRAME_ORIGIN_URL + VITE_WALLET_IFRAME_URL ); setIsFrameVisible(true); diff --git a/packages/frontend/src/components/projects/create/IFrameModal.tsx b/packages/frontend/src/components/projects/create/IFrameModal.tsx index b7db68e5..d912438f 100644 --- a/packages/frontend/src/components/projects/create/IFrameModal.tsx +++ b/packages/frontend/src/components/projects/create/IFrameModal.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect } from 'react'; import { Box, Modal } from '@mui/material'; -import { VITE_LACONICD_CHAIN_ID, VITE_IFRAME_ORIGIN_URL } from 'utils/constants'; +import { VITE_LACONICD_CHAIN_ID, VITE_WALLET_IFRAME_URL } from 'utils/constants'; const IFrameModal = ({ setAccounts, @@ -16,7 +16,7 @@ const IFrameModal = ({ useEffect(() => { const handleMessage = (event: MessageEvent) => { // TODO: Use env for origin URL - if (event.origin !== VITE_IFRAME_ORIGIN_URL) return; + if (event.origin !== VITE_WALLET_IFRAME_URL) return; if (event.data.type === 'WALLET_ACCOUNTS_DATA') { setAccounts(event.data.data); @@ -45,7 +45,7 @@ const IFrameModal = ({ type: 'REQUEST_WALLET_ACCOUNTS', chainId: VITE_LACONICD_CHAIN_ID, }, - VITE_IFRAME_ORIGIN_URL + VITE_WALLET_IFRAME_URL ); }, []); @@ -71,7 +71,7 @@ const IFrameModal = ({ - ) + ); }; export default IFrameModal; diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx index 43879fcf..9cf799c2 100644 --- a/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx +++ b/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx @@ -4,10 +4,7 @@ import { cloneIcon } from 'utils/cloneIcon'; import { PWAIcon } from './PWAIcon'; import { WebAppIcon } from './WebAppIcon'; -const TEMPLATE_ICONS = [ - 'pwa', - 'web' -] as const; +const TEMPLATE_ICONS = ['pwa', 'web'] as const; export type TemplateIconType = (typeof TEMPLATE_ICONS)[number]; export interface TemplateIconProps extends CustomIconProps { diff --git a/packages/frontend/src/pages/org-slug/projects/Id.tsx b/packages/frontend/src/pages/org-slug/projects/Id.tsx index 9abce252..fd8ce0e7 100644 --- a/packages/frontend/src/pages/org-slug/projects/Id.tsx +++ b/packages/frontend/src/pages/org-slug/projects/Id.tsx @@ -60,9 +60,9 @@ const Id = () => { fetchProject(id); }, [id]); - const onUpdate = async () => { + const onUpdate = useCallback(async () => { await fetchProject(id); - }; + }, [fetchProject, id]); return (
diff --git a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx index a129250f..84b1e9fd 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { Link, useNavigate, useOutletContext } from 'react-router-dom'; import { RequestError } from 'octokit'; @@ -24,6 +24,7 @@ import { Domain, DomainStatus } from 'gql-client'; import { AuctionCard } from 'components/projects/project/overview/Activity/AuctionCard'; const COMMITS_PER_PAGE = 4; +const WAIT_DURATION = 5000; const OverviewTabPanel = () => { const { octokit } = useOctokit(); @@ -33,8 +34,7 @@ const OverviewTabPanel = () => { const [liveDomain, setLiveDomain] = useState(); const client = useGQLClient(); - - const { project } = useOutletContext(); + const { project, onUpdate } = useOutletContext(); useEffect(() => { setFetchingActivities(true); @@ -96,7 +96,20 @@ const OverviewTabPanel = () => { }; fetchRepoActivity(); - }, [octokit, project]); + }, []); + + const fetchUpdatedProject = useCallback(async () => { + await onUpdate(); + }, [onUpdate]); + + useEffect(() => { + fetchUpdatedProject(); + const timerId = setInterval(() => { + fetchUpdatedProject(); + }, WAIT_DURATION); + + return () => clearInterval(timerId); + }, [fetchUpdatedProject]); useEffect(() => { const fetchLiveProdDomain = async () => { -- 2.45.2 From 1e7b65a0c37d1fa068adacfdeb7f2cb7bb8cf0e9 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Mon, 11 Nov 2024 11:59:54 +0530 Subject: [PATCH 21/27] Remove toggleModal from iFrame --- .../frontend/src/components/projects/create/Configure.tsx | 6 +----- .../frontend/src/components/projects/create/IFrameModal.tsx | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index f2e8c3b4..f10432a9 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -224,6 +224,7 @@ const Configure = () => { onDismiss: dismiss, }); setIsFrameVisible(false); + setIsPaymentLoading(false); throw new Error('Transaction rejected'); } @@ -301,10 +302,6 @@ const Configure = () => { [client, createProject, dismiss, toast], ); - const toggleModal = () => { - setIsFrameVisible((prev) => !prev); - }; - const fetchDeployers = useCallback(async () => { const res = await client.getDeployers(); setDeployers(res.deployers); @@ -586,7 +583,6 @@ const Configure = () => { setAccounts={setAccounts} setIsDataReceived={setIsAccountsDataReceived} isVisible={isFrameVisible} - toggleModal={toggleModal} /> {accounts.length > 0 && (
diff --git a/packages/frontend/src/components/projects/create/IFrameModal.tsx b/packages/frontend/src/components/projects/create/IFrameModal.tsx index 25cecc30..51b1afab 100644 --- a/packages/frontend/src/components/projects/create/IFrameModal.tsx +++ b/packages/frontend/src/components/projects/create/IFrameModal.tsx @@ -11,16 +11,13 @@ const IFrameModal = ({ setAccounts, setIsDataReceived, isVisible, - toggleModal, }: { setAccounts: (accounts: string[]) => void; setIsDataReceived: (isReceived: boolean) => void; isVisible: boolean; - toggleModal: () => void; }) => { useEffect(() => { const handleMessage = (event: MessageEvent) => { - // TODO: Use env for origin URL if (event.origin !== VITE_WALLET_IFRAME_URL) return; setIsDataReceived(true); @@ -56,7 +53,7 @@ const IFrameModal = ({ }, []); return ( - + Date: Mon, 11 Nov 2024 14:07:42 +0530 Subject: [PATCH 22/27] Remove generated files --- packages/deployer/.gitignore | 2 ++ .../application-deployment-request.yml | 18 ------------- .../deployer/records/application-record.yml | 8 ------ .../application-deployment-request.yml | 27 ------------------- .../staging-records/application-record.yml | 8 ------ .../components/projects/create/Configure.tsx | 8 +++--- .../pages/org-slug/projects/id/Overview.tsx | 18 +++++-------- 7 files changed, 14 insertions(+), 75 deletions(-) create mode 100644 packages/deployer/.gitignore delete mode 100644 packages/deployer/records/application-deployment-request.yml delete mode 100644 packages/deployer/records/application-record.yml delete mode 100644 packages/deployer/staging-records/application-deployment-request.yml delete mode 100644 packages/deployer/staging-records/application-record.yml diff --git a/packages/deployer/.gitignore b/packages/deployer/.gitignore new file mode 100644 index 00000000..ffaf3b27 --- /dev/null +++ b/packages/deployer/.gitignore @@ -0,0 +1,2 @@ +records/* +staging-records/* diff --git a/packages/deployer/records/application-deployment-request.yml b/packages/deployer/records/application-deployment-request.yml deleted file mode 100644 index 2b53d579..00000000 --- a/packages/deployer/records/application-deployment-request.yml +++ /dev/null @@ -1,18 +0,0 @@ -record: - type: ApplicationDeploymentRequest - version: '1.0.0' - name: deploy-frontend@1.0.0 - application: lrn://vaasl/applications/deploy-frontend@1.0.0 - dns: deploy - config: - env: - LACONIC_HOSTED_CONFIG_server_url: https://deploy-backend.apps.vaasl.io - LACONIC_HOSTED_CONFIG_github_clientid: Ov23liaet4yc0KX0iM1c - LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: laconic-templates/test-progressive-web-app - LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: laconic-templates/image-upload-pwa-example - LACONIC_HOSTED_CONFIG_github_next_app_templaterepo: laconic-templates/starter.nextjs-react-tailwind - LACONIC_HOSTED_CONFIG_wallet_connect_id: 63cad7ba97391f63652161f484670e15 - meta: - note: Added by Snowball @ Thu Apr 4 14:49:41 UTC 2024 - repository: "https://git.vdb.to/cerc-io/snowballtools-base" - repository_ref: 351db16336eacc3e1f9119ceb8d1282b8e27a27e diff --git a/packages/deployer/records/application-record.yml b/packages/deployer/records/application-record.yml deleted file mode 100644 index 9e23075f..00000000 --- a/packages/deployer/records/application-record.yml +++ /dev/null @@ -1,8 +0,0 @@ -record: - type: ApplicationRecord - version: 0.0.2 - repository_ref: 351db16336eacc3e1f9119ceb8d1282b8e27a27e - repository: ["https://git.vdb.to/cerc-io/snowballtools-base"] - app_type: webapp - name: deploy-frontend - app_version: 1.0.0 diff --git a/packages/deployer/staging-records/application-deployment-request.yml b/packages/deployer/staging-records/application-deployment-request.yml deleted file mode 100644 index 897c47a4..00000000 --- a/packages/deployer/staging-records/application-deployment-request.yml +++ /dev/null @@ -1,27 +0,0 @@ -record: - type: ApplicationDeploymentRequest - version: '1.0.0' - name: staging-snowballtools-base-frontend@0.0.0 - application: crn://staging-snowballtools/applications/staging-snowballtools-base-frontend@0.0.0 - dns: dashboard.staging.apps.snowballtools.com - config: - env: - LACONIC_HOSTED_CONFIG_server_url: https://snowballtools-base-api.staging.apps.snowballtools.com - LACONIC_HOSTED_CONFIG_github_clientid: Ov23liOaoahRTYd4nSCV - LACONIC_HOSTED_CONFIG_github_templaterepo: snowball-tools/test-progressive-web-app - LACONIC_HOSTED_CONFIG_github_pwa_templaterepo: snowball-tools/test-progressive-web-app - LACONIC_HOSTED_CONFIG_github_image_upload_templaterepo: snowball-tools/image-upload-pwa-example - LACONIC_HOSTED_CONFIG_github_next_app_templaterepo: snowball-tools/starter.nextjs-react-tailwind - LACONIC_HOSTED_CONFIG_wallet_connect_id: eda9ba18042a5ea500f358194611ece2 - LACONIC_HOSTED_CONFIG_lit_relay_api_key: 15DDD969-E75F-404D-AAD9-58A37C4FD354_snowball - LACONIC_HOSTED_CONFIG_aplchemy_api_key: THvPart_gqI5x02RNYSBntlmwA66I_qc - LACONIC_HOSTED_CONFIG_bugsnag_api_key: 8c480cd5386079f9dd44f9581264a073 - LACONIC_HOSTED_CONFIG_passkey_wallet_rpid: dashboard.staging.apps.snowballtools.com - LACONIC_HOSTED_CONFIG_turnkey_api_base_url: https://api.turnkey.com - LACONIC_HOSTED_CONFIG_laconicd_chain_id: - LACONIC_HOSTED_CONFIG_wallet_iframe_url: - LACONIC_HOSTED_CONFIG_turnkey_organization_id: 5049ae99-5bca-40b3-8317-504384d4e591 - meta: - note: Added by Snowball @ Mon Jun 24 23:51:48 UTC 2024 - repository: "https://git.vdb.to/cerc-io/snowballtools-base" - repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601 diff --git a/packages/deployer/staging-records/application-record.yml b/packages/deployer/staging-records/application-record.yml deleted file mode 100644 index fa9b4089..00000000 --- a/packages/deployer/staging-records/application-record.yml +++ /dev/null @@ -1,8 +0,0 @@ -record: - type: ApplicationRecord - version: 0.0.1 - repository_ref: 61e3e88a6c9d57e95441059369ee5a46f5c07601 - repository: ["https://git.vdb.to/cerc-io/snowballtools-base"] - app_type: webapp - name: staging-snowballtools-base-frontend - app_version: 0.0.0 diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index f10432a9..6c500d30 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -289,14 +289,14 @@ const Configure = () => { `/${orgSlug}/projects/create/deploy?projectId=${projectId}`, ); } - } catch (error) { + } catch (error: any) { toast({ id: 'error-deploying-app', title: 'Error deploying app', variant: 'error', onDismiss: dismiss, }); - throw new Error('Error deploying app'); + throw new Error(error); } }, [client, createProject, dismiss, toast], @@ -369,6 +369,8 @@ const Configure = () => { variant: 'error', onDismiss: dismiss, }); + setIsFrameVisible(false); + setIsPaymentLoading(false); }, TX_APPROVAL_TIMEOUT_MS); }); return txHash; @@ -391,7 +393,7 @@ const Configure = () => { if (!iframe.contentWindow) { console.error('Iframe not found or not loaded'); - return; + throw new Error('Iframe not found or not loaded'); } iframe.contentWindow.postMessage( diff --git a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx index 84b1e9fd..7732a6e6 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { Link, useNavigate, useOutletContext } from 'react-router-dom'; import { RequestError } from 'octokit'; @@ -24,7 +24,7 @@ import { Domain, DomainStatus } from 'gql-client'; import { AuctionCard } from 'components/projects/project/overview/Activity/AuctionCard'; const COMMITS_PER_PAGE = 4; -const WAIT_DURATION = 5000; +const PROJECT_UPDATE_WAIT_MS = 5000; const OverviewTabPanel = () => { const { octokit } = useOctokit(); @@ -96,20 +96,16 @@ const OverviewTabPanel = () => { }; fetchRepoActivity(); - }, []); - - const fetchUpdatedProject = useCallback(async () => { - await onUpdate(); - }, [onUpdate]); + }, [project.repository]); useEffect(() => { - fetchUpdatedProject(); + onUpdate(); const timerId = setInterval(() => { - fetchUpdatedProject(); - }, WAIT_DURATION); + onUpdate(); + }, PROJECT_UPDATE_WAIT_MS); return () => clearInterval(timerId); - }, [fetchUpdatedProject]); + }, [onUpdate]); useEffect(() => { const fetchLiveProdDomain = async () => { -- 2.45.2 From e0890a72b559946b4d1c256d0412f375403f95af Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Mon, 11 Nov 2024 14:28:25 +0530 Subject: [PATCH 23/27] Update redeploy button UI --- .../projects/Dialog/ChangeStateToProductionDialog.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/frontend/src/components/projects/Dialog/ChangeStateToProductionDialog.tsx b/packages/frontend/src/components/projects/Dialog/ChangeStateToProductionDialog.tsx index efb9c2fa..be68a445 100644 --- a/packages/frontend/src/components/projects/Dialog/ChangeStateToProductionDialog.tsx +++ b/packages/frontend/src/components/projects/Dialog/ChangeStateToProductionDialog.tsx @@ -47,6 +47,11 @@ export const ChangeStateToProductionDialog = ({ handleCancel={handleCancel} open={open} handleConfirm={handleConfirm} + confirmButtonTitle={ + isConfirmButtonLoading + ? 'Redeploying' + : 'Redeploy' + } confirmButtonProps={{ disabled: isConfirmButtonLoading, rightIcon: isConfirmButtonLoading ? ( -- 2.45.2 From 1ab3a837f96b75530c1eb40124ff1add45f8a866 Mon Sep 17 00:00:00 2001 From: Nabarun Date: Tue, 12 Nov 2024 11:32:53 +0530 Subject: [PATCH 24/27] Fix duplicate component rendering in project create flow --- .../components/projects/create/Configure.tsx | 11 +- .../pages/org-slug/projects/create/layout.tsx | 126 +++++++++--------- 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 6c500d30..a179ba92 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -581,11 +581,6 @@ const Configure = () => { onAccountChange={onAccountChange} isDataReceived={isAccountsDataReceived} /> - {accounts.length > 0 && (
); diff --git a/packages/frontend/src/pages/org-slug/projects/create/layout.tsx b/packages/frontend/src/pages/org-slug/projects/create/layout.tsx index 32c13b78..ab63c951 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/layout.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/layout.tsx @@ -1,12 +1,14 @@ import { ComponentPropsWithoutRef } from 'react'; import { Link, Outlet, useParams } from 'react-router-dom'; +import { useMediaQuery } from 'usehooks-ts'; + +import * as Dialog from '@radix-ui/react-dialog'; import { Heading } from 'components/shared/Heading'; import { WavyBorder } from 'components/shared/WavyBorder'; import { Button } from 'components/shared/Button'; import { CrossIcon } from 'components/shared/CustomIcon'; import { cn } from 'utils/classnames'; -import * as Dialog from '@radix-ui/react-dialog'; export interface CreateProjectLayoutProps extends ComponentPropsWithoutRef<'section'> {} @@ -16,6 +18,7 @@ export const CreateProjectLayout = ({ ...props }: CreateProjectLayoutProps) => { const { orgSlug } = useParams(); + const isDesktopView = useMediaQuery('(min-width: 720px)'); // md: const closeBtnLink = `/${orgSlug}`; @@ -28,72 +31,69 @@ export const CreateProjectLayout = ({ ); - return ( - <> - {/* Desktop */} - + ) : ( + // Mobile + // Setting modal={false} so even if modal is active on desktop, it doesn't block clicks + + + {/* Not using since modal={false} disables it and its content will not show */} +
+ + {/* Heading */} +
+ {heading} + + +
- {/* Mobile */} - {/* Setting modal={false} so even if modal is active on desktop, it doesn't block clicks */} - - - {/* Not using since modal={false} disables it and its content will not show */} -
- - {/* Heading */} -
- {heading} - - -
+ {/* Border */} + - {/* Border */} - - - {/* Page content */} -
- -
-
-
-
-
- + {/* Page content */} +
+ +
+
+
+
+
); }; -- 2.45.2 From b3c0d2e64c2ca3f434b5699465de9e5edadb9cb3 Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Tue, 12 Nov 2024 11:50:43 +0530 Subject: [PATCH 25/27] Fix amount passed for verifying tx --- packages/frontend/src/components/projects/create/Configure.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index a179ba92..c541cd6d 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -229,7 +229,7 @@ const Configure = () => { } // Validate transaction hash - const isTxHashValid = await verifyTx(senderAddress, txHash, amount); + const isTxHashValid = await verifyTx(senderAddress, txHash, amountToBePaid); setIsPaymentLoading(false); if (isTxHashValid) { -- 2.45.2 From 876642878873fbcd4f772a2cd959635c0e276b5d Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Tue, 12 Nov 2024 18:11:29 +0530 Subject: [PATCH 26/27] Update wallet iframe URL path --- .../frontend/src/components/projects/create/IFrameModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/projects/create/IFrameModal.tsx b/packages/frontend/src/components/projects/create/IFrameModal.tsx index 51b1afab..cb4d2d82 100644 --- a/packages/frontend/src/components/projects/create/IFrameModal.tsx +++ b/packages/frontend/src/components/projects/create/IFrameModal.tsx @@ -74,7 +74,7 @@ const IFrameModal = ({