Commit 04d087ef authored by Max Alekseenko's avatar Max Alekseenko

integrate dappscout-iframe

parent 5322799a
import type { TypedData } from 'abitype';
import { useCallback } from 'react';
import type { Account, SignTypedDataParameters } from 'viem';
import { useAccount, useSendTransaction, useSwitchNetwork, useNetwork, useSignMessage, useSignTypedData } from 'wagmi';
import config from 'configs/app';
type SendTransactionArgs = {
chainId?: number;
mode?: 'prepared';
to: string;
};
export type SignTypedDataArgs<
TTypedData extends
| TypedData
| {
[key: string]: unknown;
} = TypedData,
TPrimaryType extends string = string,
> = SignTypedDataParameters<TTypedData, TPrimaryType, Account>;
export default function useMarketplaceWallet() {
const { address } = useAccount();
const { chain } = useNetwork();
const { sendTransactionAsync } = useSendTransaction();
const { signMessageAsync } = useSignMessage();
const { signTypedDataAsync } = useSignTypedData();
const { switchNetworkAsync } = useSwitchNetwork({ chainId: Number(config.chain.id) });
const switchNetwork = useCallback(async() => {
if (Number(config.chain.id) !== chain?.id) {
await switchNetworkAsync?.();
}
}, [ chain, switchNetworkAsync ]);
const sendTransaction = useCallback(async(transaction: SendTransactionArgs) => {
await switchNetwork();
const tx = await sendTransactionAsync(transaction);
return tx.hash;
}, [ sendTransactionAsync, switchNetwork ]);
const signMessage = useCallback(async(message: string) => {
await switchNetwork();
const signature = await signMessageAsync({ message });
return signature;
}, [ signMessageAsync, switchNetwork ]);
const signTypedData = useCallback(async(typedData: SignTypedDataArgs) => {
await switchNetwork();
if (typedData.domain) {
typedData.domain.chainId = Number(typedData.domain.chainId);
}
const signature = await signTypedDataAsync(typedData);
return signature;
}, [ signTypedDataAsync, switchNetwork ]);
return {
address,
sendTransaction,
signMessage,
signTypedData,
};
}
import { Box, Center, useColorMode } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { DappscoutIframeProvider, useDappscoutIframe } from 'dappscout-iframe';
import { useRouter } from 'next/router';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import type { MarketplaceAppOverview } from 'types/client/marketplace';
......@@ -14,6 +15,8 @@ import * as metadata from 'lib/metadata';
import getQueryParamString from 'lib/router/getQueryParamString';
import ContentLoader from 'ui/shared/ContentLoader';
import useMarketplaceWallet from '../marketplace/useMarketplaceWallet';
const feature = config.features.marketplace;
const configUrl = feature.isEnabled ? feature.configUrl : '';
......@@ -24,34 +27,23 @@ const IFRAME_SANDBOX_ATTRIBUTE = 'allow-forms allow-orientation-lock ' +
const IFRAME_ALLOW_ATTRIBUTE = 'clipboard-read; clipboard-write;';
const MarketplaceApp = () => {
const ref = useRef<HTMLIFrameElement>(null);
const apiFetch = useApiFetch();
const router = useRouter();
const id = getQueryParamString(router.query.id);
const { isPending, isError, error, data } = useQuery<unknown, ResourceError<unknown>, MarketplaceAppOverview>({
queryKey: [ 'marketplace-apps', id ],
queryFn: async() => {
const result = await apiFetch<Array<MarketplaceAppOverview>, unknown>(configUrl, undefined, { resource: 'marketplace-apps' });
if (!Array.isArray(result)) {
throw result;
}
const item = result.find((app: MarketplaceAppOverview) => app.id === id);
if (!item) {
throw { status: 404 };
}
type Props = {
address: string | undefined;
data: MarketplaceAppOverview | undefined;
isPending: boolean;
};
return item;
},
enabled: feature.isEnabled,
});
const MarketplaceAppContent = ({ address, data, isPending }: Props) => {
const { iframeRef, isReady } = useDappscoutIframe();
const [ iframeKey, setIframeKey ] = useState(0);
const [ isFrameLoading, setIsFrameLoading ] = useState(isPending);
const { colorMode } = useColorMode();
useEffect(() => {
setIframeKey((key) => key + 1);
}, [ address ]);
const handleIframeLoad = useCallback(() => {
setIsFrameLoading(false);
}, []);
......@@ -69,22 +61,9 @@ const MarketplaceApp = () => {
blockscoutNetworkRpc: config.chain.rpcUrl,
};
ref?.current?.contentWindow?.postMessage(message, data.url);
}
}, [ isFrameLoading, data, colorMode, ref ]);
useEffect(() => {
if (data) {
metadata.update(
{ pathname: '/apps/[id]', query: { id: data.id } },
{ app_name: data.title },
);
iframeRef?.current?.contentWindow?.postMessage(message, data.url);
}
}, [ data ]);
if (isError) {
throw new Error('Unable to load app', { cause: error });
}
}, [ isFrameLoading, data, colorMode, iframeRef ]);
return (
<Center
......@@ -95,10 +74,11 @@ const MarketplaceApp = () => {
<ContentLoader/>
) }
{ data && (
{ (data && isReady) && (
<Box
key={ iframeKey }
allow={ IFRAME_ALLOW_ATTRIBUTE }
ref={ ref }
ref={ iframeRef }
sandbox={ IFRAME_SANDBOX_ATTRIBUTE }
as="iframe"
h="100%"
......@@ -113,4 +93,55 @@ const MarketplaceApp = () => {
);
};
const MarketplaceApp = () => {
const { address, sendTransaction, signMessage, signTypedData } = useMarketplaceWallet();
const apiFetch = useApiFetch();
const router = useRouter();
const id = getQueryParamString(router.query.id);
const { isPending, isError, error, data } = useQuery<unknown, ResourceError<unknown>, MarketplaceAppOverview>({
queryKey: [ 'marketplace-apps', id ],
queryFn: async() => {
const result = await apiFetch<Array<MarketplaceAppOverview>, unknown>(configUrl, undefined, { resource: 'marketplace-apps' });
if (!Array.isArray(result)) {
throw result;
}
const item = result.find((app: MarketplaceAppOverview) => app.id === id);
if (!item) {
throw { status: 404 };
}
return item;
},
enabled: feature.isEnabled,
});
useEffect(() => {
if (data) {
metadata.update(
{ pathname: '/apps/[id]', query: { id: data.id } },
{ app_name: data.title },
);
}
}, [ data ]);
if (isError) {
throw new Error('Unable to load app', { cause: error });
}
return (
<DappscoutIframeProvider
address={ address }
appUrl={ data?.url }
rpcUrl={ config.chain.rpcUrl }
sendTransaction={ sendTransaction }
signMessage={ signMessage }
signTypedData={ signTypedData }
>
<MarketplaceAppContent address={ address } data={ data } isPending={ isPending }/>
</DappscoutIframeProvider>
);
};
export default MarketplaceApp;
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment