feat: move to nextjs
This commit is contained in:
29
components/Body/IFrameConnectTab/AppUrlLabel.tsx
Normal file
29
components/Body/IFrameConnectTab/AppUrlLabel.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { FormLabel, Tooltip, Text, Box } from "@chakra-ui/react";
|
||||
import { InfoIcon } from "@chakra-ui/icons";
|
||||
|
||||
function AppUrlLabel() {
|
||||
return (
|
||||
<>
|
||||
<FormLabel>dapp URL</FormLabel>
|
||||
<Tooltip
|
||||
label={
|
||||
<>
|
||||
<Text>Paste the URL of dapp you want to connect to</Text>
|
||||
<Text>
|
||||
Note: Some dapps might not support it, so use WalletConnect in
|
||||
that case
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
hasArrow
|
||||
placement="top"
|
||||
>
|
||||
<Box pb="0.8rem">
|
||||
<InfoIcon />
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AppUrlLabel;
|
||||
59
components/Body/IFrameConnectTab/ShareModal.tsx
Normal file
59
components/Body/IFrameConnectTab/ShareModal.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
HStack,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Input,
|
||||
} from "@chakra-ui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faShareAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import CopyToClipboard from "../CopyToClipboard";
|
||||
|
||||
interface ShareModalParams {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
appUrl: string;
|
||||
showAddress: string;
|
||||
}
|
||||
|
||||
function ShareModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
appUrl,
|
||||
showAddress,
|
||||
}: ShareModalParams) {
|
||||
const urlToShare = `https://impersonator.xyz/?address=${showAddress}&url=${encodeURI(
|
||||
appUrl
|
||||
)}`;
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} isCentered>
|
||||
<ModalOverlay bg="none" backdropFilter="auto" backdropBlur="5px" />
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<HStack>
|
||||
<FontAwesomeIcon icon={faShareAlt} />
|
||||
<Text>Share</Text>
|
||||
</HStack>
|
||||
</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Text>
|
||||
Share this link so that anyone can auto-connect to this dapp with
|
||||
your provided address!
|
||||
</Text>
|
||||
<HStack my="3">
|
||||
<Input value={urlToShare} isReadOnly />
|
||||
<CopyToClipboard txt={urlToShare} />
|
||||
</HStack>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ShareModal;
|
||||
45
components/Body/IFrameConnectTab/SupportedDapps/DappTile.tsx
Normal file
45
components/Body/IFrameConnectTab/SupportedDapps/DappTile.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { GridItem, Center, Image, Text } from "@chakra-ui/react";
|
||||
import { SafeDappInfo } from "../../../../types";
|
||||
|
||||
interface DappTileParams {
|
||||
initIFrame: (_inputAppUrl?: string | undefined) => Promise<void>;
|
||||
setInputAppUrl: (value: string | undefined) => void;
|
||||
closeSafeApps: () => void;
|
||||
dapp: SafeDappInfo;
|
||||
}
|
||||
|
||||
function DappTile({
|
||||
initIFrame,
|
||||
setInputAppUrl,
|
||||
closeSafeApps,
|
||||
dapp,
|
||||
}: DappTileParams) {
|
||||
return (
|
||||
<GridItem
|
||||
border="2px solid"
|
||||
borderColor={"gray.500"}
|
||||
bg={"white"}
|
||||
color={"black"}
|
||||
_hover={{
|
||||
cursor: "pointer",
|
||||
bgColor: "gray.600",
|
||||
color: "white",
|
||||
}}
|
||||
rounded="lg"
|
||||
onClick={() => {
|
||||
initIFrame(dapp.url);
|
||||
setInputAppUrl(dapp.url);
|
||||
closeSafeApps();
|
||||
}}
|
||||
>
|
||||
<Center flexDir={"column"} h="100%" p="1rem">
|
||||
<Image bg="white" w="2rem" src={dapp.iconUrl} borderRadius="full" />
|
||||
<Text mt="0.5rem" textAlign={"center"}>
|
||||
{dapp.name}
|
||||
</Text>
|
||||
</Center>
|
||||
</GridItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default DappTile;
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
Center,
|
||||
InputGroup,
|
||||
Input,
|
||||
InputRightElement,
|
||||
Button,
|
||||
} from "@chakra-ui/react";
|
||||
import { CloseIcon } from "@chakra-ui/icons";
|
||||
|
||||
interface DappsSearchParams {
|
||||
searchSafeDapp: string | undefined;
|
||||
setSearchSafeDapp: (value: string) => void;
|
||||
}
|
||||
|
||||
function DappsSearch({ searchSafeDapp, setSearchSafeDapp }: DappsSearchParams) {
|
||||
return (
|
||||
<Center pb="0.5rem">
|
||||
<InputGroup maxW="30rem">
|
||||
<Input
|
||||
placeholder="search 🔎"
|
||||
value={searchSafeDapp}
|
||||
onChange={(e) => setSearchSafeDapp(e.target.value)}
|
||||
/>
|
||||
{searchSafeDapp && (
|
||||
<InputRightElement width="3rem">
|
||||
<Button
|
||||
size="xs"
|
||||
variant={"ghost"}
|
||||
onClick={() => setSearchSafeDapp("")}
|
||||
>
|
||||
<CloseIcon />
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
export default DappsSearch;
|
||||
147
components/Body/IFrameConnectTab/SupportedDapps/index.tsx
Normal file
147
components/Body/IFrameConnectTab/SupportedDapps/index.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
SimpleGrid,
|
||||
Spinner,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import axios from "axios";
|
||||
import DappsSearch from "./DappsSearch";
|
||||
import DappTile from "./DappTile";
|
||||
import { SafeDappInfo } from "../../../../types";
|
||||
|
||||
interface SupportedDappsParams {
|
||||
networkId: number;
|
||||
initIFrame: (_inputAppUrl?: string | undefined) => Promise<void>;
|
||||
setInputAppUrl: (value: string | undefined) => void;
|
||||
}
|
||||
|
||||
function SupportedDapps({
|
||||
networkId,
|
||||
initIFrame,
|
||||
setInputAppUrl,
|
||||
}: SupportedDappsParams) {
|
||||
const {
|
||||
isOpen: isSafeAppsOpen,
|
||||
onOpen: openSafeAapps,
|
||||
onClose: closeSafeApps,
|
||||
} = useDisclosure();
|
||||
|
||||
const [safeDapps, setSafeDapps] = useState<{
|
||||
[networkId: number]: SafeDappInfo[];
|
||||
}>({});
|
||||
const [searchSafeDapp, setSearchSafeDapp] = useState<string>();
|
||||
const [filteredSafeDapps, setFilteredSafeDapps] = useState<SafeDappInfo[]>();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSafeDapps = async (networkId: number) => {
|
||||
const response = await axios.get<SafeDappInfo[]>(
|
||||
`https://safe-client.safe.global/v1/chains/${networkId}/safe-apps`
|
||||
);
|
||||
setSafeDapps((dapps) => ({
|
||||
...dapps,
|
||||
[networkId]: response.data.filter((d) => ![29, 11].includes(d.id)), // Filter out Transaction Builder and WalletConnect
|
||||
}));
|
||||
};
|
||||
|
||||
if (isSafeAppsOpen && !safeDapps[networkId]) {
|
||||
fetchSafeDapps(networkId);
|
||||
}
|
||||
}, [isSafeAppsOpen, safeDapps, networkId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (safeDapps[networkId]) {
|
||||
setFilteredSafeDapps(
|
||||
safeDapps[networkId].filter((dapp) => {
|
||||
if (!searchSafeDapp) return true;
|
||||
|
||||
return (
|
||||
dapp.name
|
||||
.toLowerCase()
|
||||
.indexOf(searchSafeDapp.toLocaleLowerCase()) !== -1 ||
|
||||
dapp.url
|
||||
.toLowerCase()
|
||||
.indexOf(searchSafeDapp.toLocaleLowerCase()) !== -1
|
||||
);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
setFilteredSafeDapps(undefined);
|
||||
}
|
||||
}, [safeDapps, networkId, searchSafeDapp]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box pb="0.5rem">
|
||||
<Button size="sm" onClick={openSafeAapps}>
|
||||
Supported dapps
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Modal isOpen={isSafeAppsOpen} onClose={closeSafeApps} isCentered>
|
||||
<ModalOverlay bg="none" backdropFilter="auto" backdropBlur="3px" />
|
||||
<ModalContent
|
||||
minW={{
|
||||
base: 0,
|
||||
sm: "30rem",
|
||||
md: "40rem",
|
||||
lg: "60rem",
|
||||
}}
|
||||
bg={"brand.lightBlack"}
|
||||
>
|
||||
<ModalHeader>Select a dapp</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
{(!safeDapps || !safeDapps[networkId]) && (
|
||||
<Center py="3rem" w="100%">
|
||||
<Spinner />
|
||||
</Center>
|
||||
)}
|
||||
<Box pb="2rem" px={{ base: 0, md: "2rem" }}>
|
||||
{safeDapps && safeDapps[networkId] && (
|
||||
<DappsSearch
|
||||
searchSafeDapp={searchSafeDapp}
|
||||
setSearchSafeDapp={setSearchSafeDapp}
|
||||
/>
|
||||
)}
|
||||
<Box
|
||||
minH="30rem"
|
||||
maxH="30rem"
|
||||
overflow="scroll"
|
||||
overflowX="auto"
|
||||
overflowY="auto"
|
||||
>
|
||||
<SimpleGrid
|
||||
pt="1rem"
|
||||
columns={{ base: 2, md: 3, lg: 4 }}
|
||||
gap={6}
|
||||
>
|
||||
{filteredSafeDapps &&
|
||||
filteredSafeDapps.map((dapp, i) => (
|
||||
<DappTile
|
||||
key={i}
|
||||
initIFrame={initIFrame}
|
||||
setInputAppUrl={setInputAppUrl}
|
||||
closeSafeApps={closeSafeApps}
|
||||
dapp={dapp}
|
||||
/>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SupportedDapps;
|
||||
140
components/Body/IFrameConnectTab/index.tsx
Normal file
140
components/Body/IFrameConnectTab/index.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
Button,
|
||||
Box,
|
||||
Center,
|
||||
Spacer,
|
||||
HStack,
|
||||
FormControl,
|
||||
Input,
|
||||
Text,
|
||||
useDisclosure,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
} from "@chakra-ui/react";
|
||||
import { DeleteIcon } from "@chakra-ui/icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faShareAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import SupportedDapps from "./SupportedDapps";
|
||||
import AppUrlLabel from "./AppUrlLabel";
|
||||
import ShareModal from "./ShareModal";
|
||||
|
||||
interface IFrameConnectTabParams {
|
||||
networkId: number;
|
||||
initIFrame: (_inputAppUrl?: string | undefined) => Promise<void>;
|
||||
inputAppUrl: string | undefined;
|
||||
setInputAppUrl: (value: string | undefined) => void;
|
||||
appUrl: string | undefined;
|
||||
isIFrameLoading: boolean;
|
||||
setIsIFrameLoading: (value: boolean) => void;
|
||||
iframeKey: number;
|
||||
iframeRef: React.RefObject<HTMLIFrameElement> | null;
|
||||
showAddress: string;
|
||||
}
|
||||
|
||||
function IFrameConnectTab({
|
||||
networkId,
|
||||
initIFrame,
|
||||
setInputAppUrl,
|
||||
inputAppUrl,
|
||||
isIFrameLoading,
|
||||
appUrl,
|
||||
iframeKey,
|
||||
iframeRef,
|
||||
setIsIFrameLoading,
|
||||
showAddress,
|
||||
}: IFrameConnectTabParams) {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl my={4}>
|
||||
<HStack>
|
||||
<AppUrlLabel />
|
||||
<Spacer />
|
||||
<SupportedDapps
|
||||
networkId={networkId}
|
||||
initIFrame={initIFrame}
|
||||
setInputAppUrl={setInputAppUrl}
|
||||
/>
|
||||
</HStack>
|
||||
<HStack mt="2">
|
||||
<InputGroup>
|
||||
<Input
|
||||
pr="3.5rem"
|
||||
bg={"brand.lightBlack"}
|
||||
placeholder="https://app.uniswap.org/"
|
||||
aria-label="dapp-url"
|
||||
autoComplete="off"
|
||||
value={inputAppUrl}
|
||||
onChange={(e) => setInputAppUrl(e.target.value)}
|
||||
/>
|
||||
{inputAppUrl && (
|
||||
<InputRightElement px="1rem" mr="0.5rem">
|
||||
<Button
|
||||
h="1.75rem"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setInputAppUrl("");
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
{appUrl && (
|
||||
<>
|
||||
<Button onClick={onOpen}>
|
||||
<HStack>
|
||||
<FontAwesomeIcon icon={faShareAlt} />
|
||||
<Text>Share</Text>
|
||||
</HStack>
|
||||
</Button>
|
||||
<ShareModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
appUrl={appUrl}
|
||||
showAddress={showAddress}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</HStack>
|
||||
</FormControl>
|
||||
<Center>
|
||||
<Button onClick={() => initIFrame()} isLoading={isIFrameLoading}>
|
||||
Connect
|
||||
</Button>
|
||||
</Center>
|
||||
<Center
|
||||
mt="1rem"
|
||||
ml={{ base: "-385", sm: "-315", md: "-240", lg: "-60" }}
|
||||
px={{ base: "10rem", lg: 0 }}
|
||||
w="70rem"
|
||||
>
|
||||
{appUrl && (
|
||||
<Box
|
||||
as="iframe"
|
||||
w={{
|
||||
base: "22rem",
|
||||
sm: "45rem",
|
||||
md: "55rem",
|
||||
lg: "1500rem",
|
||||
}}
|
||||
h={{ base: "33rem", md: "35rem", lg: "38rem" }}
|
||||
title="app"
|
||||
src={appUrl}
|
||||
key={iframeKey}
|
||||
borderWidth="1px"
|
||||
borderStyle={"solid"}
|
||||
borderColor="white"
|
||||
bg="white"
|
||||
ref={iframeRef}
|
||||
onLoad={() => setIsIFrameLoading(false)}
|
||||
/>
|
||||
)}
|
||||
</Center>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default IFrameConnectTab;
|
||||
Reference in New Issue
Block a user