diff --git a/examples/starknet-react-next/src/components/StarknetProvider.tsx b/examples/starknet-react-next/src/components/StarknetProvider.tsx
index 9d506c99b..7e3a41562 100644
--- a/examples/starknet-react-next/src/components/StarknetProvider.tsx
+++ b/examples/starknet-react-next/src/components/StarknetProvider.tsx
@@ -52,7 +52,7 @@ const connectors = [
],
{
url,
- rpc: process.env.NEXT_PUBLIC_RPC_SEPOLIA
+ rpc: process.env.NEXT_PUBLIC_RPC_SEPOLIA,
// theme: "rollyourown",
// colorMode: "light"
},
diff --git a/packages/keychain/src/components/Container/Header/index.tsx b/packages/keychain/src/components/Container/Header/index.tsx
deleted file mode 100644
index 63fd9f0e6..000000000
--- a/packages/keychain/src/components/Container/Header/index.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import React, { useMemo } from "react";
-import {
- Flex,
- Spacer,
- HStack,
- Container as ChakraContainer,
- StyleProps,
- IconButton,
- VStack,
- Image,
- Center,
- useColorMode,
-} from "@chakra-ui/react";
-import { ArrowLeftIcon, CartridgeColorIcon } from "@cartridge/ui";
-// import { NetworkButton } from "./NetworkButton";
-// import { EthBalance } from "./EthBalance";
-// import { AccountMenu } from "./AccountMenu";
-import { useController } from "hooks/controller";
-import { useControllerTheme } from "hooks/theme";
-
-export type HeaderProps = {
- chainId?: string;
- onLogout?: () => void;
- onBack?: () => void;
- hideAccount?: boolean;
-};
-
-export function Header({
- // chainId,
- // onLogout,
- onBack,
- hideAccount,
-}: HeaderProps) {
- const { controller } = useController();
- const address = useMemo(() => controller?.address, [controller]);
- const theme = useControllerTheme();
- const { colorMode } = useColorMode();
-
- const cover = useMemo(
- () =>
- typeof theme.cover === "string" ? theme.cover : theme.cover[colorMode],
- [theme, colorMode],
- );
-
- if (!address || hideAccount) {
- return (
-
-
-
-
-
-
-
-
-
- );
- }
-
- return (
-
-
- {onBack ? (
- }
- onClick={onBack}
- />
- ) : theme.id === "cartridge" ? (
-
- ) : (
-
- )}
-
-
-
- {/* */}
- {/* */}
-
- {/* {chainId && } */}
-
-
- );
-}
-
-function Container({
- h,
- children,
- ...rest
-}: {
- children: React.ReactNode;
-} & StyleProps) {
- return (
-
-
- {children}
-
-
- );
-}
-
-export const BANNER_HEIGHT = 150;
-export const ICON_IMAGE_SIZE = 64;
-export const ICON_SIZE = 80;
-export const ICON_OFFSET = 32;
diff --git a/packages/keychain/src/components/DeploymentRequired.tsx b/packages/keychain/src/components/DeploymentRequired.tsx
index f976cb39c..8e2c9ceb7 100644
--- a/packages/keychain/src/components/DeploymentRequired.tsx
+++ b/packages/keychain/src/components/DeploymentRequired.tsx
@@ -1,26 +1,22 @@
import { constants } from "starknet";
-import Controller from "utils/controller";
-import { Container } from "./Container";
+import { Container, Banner, Footer, Content } from "components/layout";
import { useEffect, useState } from "react";
import { Status } from "utils/account";
import { Loading } from "./Loading";
import { Button, Link, Text } from "@chakra-ui/react";
import { ExternalIcon } from "@cartridge/ui";
-import { PortalBanner } from "./PortalBanner";
-import { PortalFooter } from "./PortalFooter";
+import { useController } from "hooks/controller";
+
export function DeploymentRequired({
- chainId,
- controller,
onClose,
onLogout,
children,
}: {
- chainId: string;
- controller: Controller;
onClose: () => void;
onLogout: () => void;
children: React.ReactNode;
}) {
+ const { controller } = useController()
const account = controller.account;
const [status, setStatus] = useState(account.status);
const [deployHash, setDeployHash] = useState();
@@ -65,40 +61,41 @@ export function DeploymentRequired({
if (status !== Status.DEPLOYED) {
return (
-
-
+
- {status === Status.DEPLOYING && (
-
+ {status === Status.DEPLOYING && (
+
- }>
- View on Starkscan
-
-
- )}
+ }starkscan.co/tx/${deployHash}`}
+ isExternal
+ >
+ }>
+ View on Starkscan
+
+
+ )}
- {error && (
- <>
-
- We encounter an account deployment error: {error.message}
-
- Please come by discord and report this issue.
- >
- )}
+ {error && (
+ <>
+
+ We encounter an account deployment error: {error.message}
+
+ Please come by discord and report this issue.
+ >
+ )}
+
-
+
+
);
}
diff --git a/packages/keychain/src/components/Execute/Call.tsx b/packages/keychain/src/components/Execute/Call.tsx
index 8c89cacc9..68cf35e53 100644
--- a/packages/keychain/src/components/Execute/Call.tsx
+++ b/packages/keychain/src/components/Execute/Call.tsx
@@ -3,11 +3,9 @@ import { CodeUtilIcon } from "@cartridge/ui";
import { HStack, Spacer, SystemProps, Text } from "@chakra-ui/react";
export function Call({
- chainId,
policy,
...rest
}: {
- chainId: string;
policy: Policy;
} & SystemProps) {
return (
diff --git a/packages/keychain/src/components/Execute/Fees.tsx b/packages/keychain/src/components/Execute/Fees.tsx
index e59e9b165..7b3f83d41 100644
--- a/packages/keychain/src/components/Execute/Fees.tsx
+++ b/packages/keychain/src/components/Execute/Fees.tsx
@@ -11,6 +11,7 @@ import {
import { constants } from "starknet";
import { formatUnits } from "viem";
import { Error } from "components/Error";
+import { useChainId } from "hooks/connection";
async function fetchEthPrice() {
const res = await fetch(process.env.NEXT_PUBLIC_API_URL, {
@@ -25,17 +26,16 @@ async function fetchEthPrice() {
export function Fees({
error,
- chainId,
fees,
balance,
approved,
}: {
error: Error;
- chainId: string;
fees?: { base: bigint; max: bigint };
balance: string;
approved?: string;
}) {
+ const chainId = useChainId();
const [formattedFee, setFormattedFee] = useState<{
base: string;
max: string;
@@ -66,13 +66,13 @@ export function Fees({
setFormattedFee(
fees.max > 10000000000000n
? {
- base: `~${parseFloat(formatUnits(fees.base, 18)).toFixed(5)} eth`,
- max: `~${parseFloat(formatUnits(fees.max, 18)).toFixed(5)} eth`,
- }
+ base: `~${parseFloat(formatUnits(fees.base, 18)).toFixed(5)} eth`,
+ max: `~${parseFloat(formatUnits(fees.max, 18)).toFixed(5)} eth`,
+ }
: {
- base: "<0.00001",
- max: "<0.00001",
- },
+ base: "<0.00001",
+ max: "<0.00001",
+ },
);
}
compute();
diff --git a/packages/keychain/src/components/Execute/LowEth.tsx b/packages/keychain/src/components/Execute/InsufficientFunds.tsx
similarity index 89%
rename from packages/keychain/src/components/Execute/LowEth.tsx
rename to packages/keychain/src/components/Execute/InsufficientFunds.tsx
index 172aa9121..f47935eb5 100644
--- a/packages/keychain/src/components/Execute/LowEth.tsx
+++ b/packages/keychain/src/components/Execute/InsufficientFunds.tsx
@@ -1,29 +1,26 @@
import { CopyIcon, EthereumIcon, StarknetIcon } from "@cartridge/ui";
import { HStack, Spacer, Text, VStack } from "@chakra-ui/react";
-import { Container } from "components/Container";
-import { PortalBanner } from "components/PortalBanner";
+import { Container, Content, Banner } from "components/layout";
import { useState } from "react";
import { BigNumberish } from "starknet";
import { formatAddress } from "utils/contracts";
-const NewLowEth = ({
- chainId,
+export function InsufficientFunds({
address,
balance,
}: {
- chainId: string;
address: BigNumberish;
balance: BigNumberish;
-}) => {
+}) {
const [copied, setCopied] = useState(false);
return (
-
-
+
-
+
BALANCE
@@ -88,9 +85,7 @@ const NewLowEth = ({
)}
-
+
);
};
-
-export default NewLowEth;
diff --git a/packages/keychain/src/components/Execute/index.tsx b/packages/keychain/src/components/Execute/index.tsx
index 9b15184ec..e75f7720a 100644
--- a/packages/keychain/src/components/Execute/index.tsx
+++ b/packages/keychain/src/components/Execute/index.tsx
@@ -1,54 +1,51 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { Text, VStack, Button } from "@chakra-ui/react";
-
-import Controller from "utils/controller";
import { Call as StarknetCall, InvocationsDetails } from "starknet";
import { Fees } from "./Fees";
import { formatEther } from "viem";
import { ExecuteReply, ResponseCodes } from "@cartridge/controller";
-import { Container } from "../Container";
+import {
+ Container,
+ Content,
+ Banner,
+ FOOTER_MIN_HEIGHT,
+ Footer,
+} from "components/layout";
import { Status } from "utils/account";
-import { PortalBanner } from "../PortalBanner";
import { TransactionDuoIcon } from "@cartridge/ui";
import { Call } from "./Call";
-import {
- PORTAL_FOOTER_MIN_HEIGHT,
- PortalFooter,
-} from "components/PortalFooter";
-import LowEth from "./LowEth";
+import { InsufficientFunds } from "./InsufficientFunds";
+import { useChainId, useOrigin } from "hooks/connection";
+import { useController } from "hooks/controller";
export const CONTRACT_ETH =
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
export function Execute({
- // origin,
- chainId,
- controller,
transactions,
transactionsDetail,
- // abis,
onExecute,
onCancel,
onLogout,
}: {
- // origin: string;
- chainId: string;
- controller: Controller;
transactions: StarknetCall | StarknetCall[];
transactionsDetail?: InvocationsDetails;
- // abis?: Abi[];
onExecute: (res: ExecuteReply) => void;
onCancel: () => void;
onLogout: () => void;
}) {
+ const chainId = useChainId();
+ const { controller } = useController();
+ const origin = useOrigin();
+
const [fees, setFees] = useState<{
base: bigint;
max: bigint;
}>();
const [error, setError] = useState();
const [isLoading, setLoading] = useState(false);
- const [ethBalance, setEthBalance] = useState();
- const [lowEth, setLowEth] = useState(false);
+ const [ethBalance, setEthBalance] = useState(0n);
+ const [isInsufficient, setIsInsufficient] = useState(false);
const account = controller.account;
const calls = useMemo(() => {
@@ -122,7 +119,7 @@ export function Execute({
}
if (ethBalance < fees.max) {
- setLowEth(true);
+ setIsInsufficient(true);
}
}, [ethBalance, fees]);
@@ -137,10 +134,9 @@ export function Execute({
});
}, [account, calls, fees, onExecute]);
- if (lowEth) {
+ if (isInsufficient) {
return (
-
@@ -148,10 +144,10 @@ export function Execute({
}
return (
-
-
+
+
-
+
@@ -163,7 +159,6 @@ export function Execute({
{calls.map((call, i) => (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
);
}
diff --git a/packages/keychain/src/components/PortalBanner.tsx b/packages/keychain/src/components/PortalBanner.tsx
deleted file mode 100644
index 2c328faa1..000000000
--- a/packages/keychain/src/components/PortalBanner.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { VStack, Circle, Text, IconProps } from "@chakra-ui/react";
-
-export function PortalBanner({
- Icon,
- icon,
- title,
- description,
-}: {
- Icon?: React.ComponentType;
- icon?: React.ReactElement;
- title: string;
- description?: string | React.ReactElement;
-}) {
- return (
-
- {!!Icon && (
-
-
-
- )}
-
- {!!icon && (
-
- {icon}
-
- )}
-
-
- {title}
-
-
- {description && (
-
- {description}
-
- )}
-
- );
-}
diff --git a/packages/keychain/src/components/PortalFooter/index.tsx b/packages/keychain/src/components/PortalFooter/index.tsx
deleted file mode 100644
index 9b30df11a..000000000
--- a/packages/keychain/src/components/PortalFooter/index.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import {
- HStack,
- IconButton,
- Spacer,
- VStack,
- useDisclosure,
-} from "@chakra-ui/react";
-import { TransactionSummary } from "./TransactionSummary";
-import { WedgeUpIcon } from "@cartridge/ui";
-import { Policy } from "@cartridge/controller";
-import { SessionDetails } from "./SessionDetails";
-import React, { useMemo } from "react";
-import { FOOTER_HEIGHT } from "components";
-import {
- BANNER_HEIGHT,
- ICON_OFFSET,
- ICON_SIZE,
-} from "components/Container/Header";
-import { motion } from "framer-motion";
-
-export function PortalFooter({
- children,
- origin,
- policies,
- isSignup,
- isSlot,
- showTerm = false,
-}: React.PropsWithChildren & {
- origin?: string;
- policies?: Policy[];
- isSignup?: boolean;
- isSlot?: boolean;
- showTerm?: boolean;
-}) {
- const { isOpen, onToggle } = useDisclosure();
- const isExpandable = useMemo(() => !!origin, [origin]);
- const hostname = useMemo(
- () => (origin ? new URL(origin).hostname : undefined),
- [origin],
- );
-
- const height = useMemo(
- () =>
- isOpen
- ? `${
- window.innerHeight -
- BANNER_HEIGHT -
- FOOTER_HEIGHT +
- ICON_SIZE / 2 -
- ICON_OFFSET
- }px`
- : "auto",
- [isOpen],
- );
-
- return (
-
-
-
-
-
-
-
- {isExpandable && (
-
- }
- size="sm"
- bg="solid.primary"
- zIndex="999999"
- onClick={onToggle}
- />
- )}
-
-
- {isOpen && policies && (
-
- )}
-
- {/* TODO: starter pack
- starterData && remaining > 0 && (
- <>
-
- {starterData.game.starterPack.starterPackTokens.map(
- (data, key) => (
-
- ),
- )}
-
-
-
-
-
-
-
- Claim Starterpack
-
-
- >
- ) */}
-
-
-
-
-
- {children}
-
-
- );
-}
-
-export const PORTAL_FOOTER_MIN_HEIGHT = 252;
diff --git a/packages/keychain/src/components/Quests/Card.tsx b/packages/keychain/src/components/Quests/Card.tsx
deleted file mode 100644
index 5cedf076f..000000000
--- a/packages/keychain/src/components/Quests/Card.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { CircleCheckIcon, SparklesIcon } from "@cartridge/ui";
-import { HStack, Spacer, Text } from "@chakra-ui/react";
-import { QuestState } from "./types";
-
-export function QuestCard({
- name,
- state,
-}: {
- name: string;
- state: QuestState;
-}) {
- let bgColor = "solid.primary";
- let color = "white";
- let check = false;
- let border = undefined;
-
- switch (state) {
- case QuestState.Claimable:
- bgColor = "solid.secondary";
- color = "green.400";
- check = true;
- break;
- case QuestState.Complete:
- bgColor = "transparent";
- color = "text.secondary";
- check = true;
- border = {
- border: "1px solid",
- borderColor: "solid.primary",
- };
- break;
- default:
- break;
- }
-
- return (
-
- {check && }
- {name}
-
- {state !== QuestState.Complete && (
-
- )}
- {state === QuestState.Complete && (
-
- Completed
-
- )}
-
- );
-}
diff --git a/packages/keychain/src/components/Quests/Details.tsx b/packages/keychain/src/components/Quests/Details.tsx
deleted file mode 100644
index 29080419c..000000000
--- a/packages/keychain/src/components/Quests/Details.tsx
+++ /dev/null
@@ -1,218 +0,0 @@
-import { Box, Button, Flex, HStack, Text, VStack } from "@chakra-ui/react";
-import { QuestState } from "./types";
-import { useEffect, useState } from "react";
-import { useClaimQuestRewardsMutation } from "generated/graphql";
-import {
- CheckIcon,
- CircleCheckIcon,
- CircleNoCheckIcon,
- QuestsDuoIcon,
- SparklesIcon,
-} from "@cartridge/ui";
-import { PortalBanner } from "components/PortalBanner";
-
-export function QuestDetails({
- questsWithProgress,
- selectedId,
- address,
- onClaim,
-}: {
- questsWithProgress: Array<{ quest: any; progress: any }>;
- selectedId: string;
- address: string;
- onClaim: () => void;
-}) {
- const [quest, setQuest] = useState();
- const [questState, setQuestState] = useState();
- const [questProgress, setQuestProgress] = useState();
- const { mutateAsync } = useClaimQuestRewardsMutation();
-
- useEffect(() => {
- const q = questsWithProgress.find((qwp) => qwp.quest.id === selectedId);
- setQuest(q.quest);
- setQuestProgress(q.progress);
- setQuestState(
- q.progress?.claimed
- ? QuestState.Complete
- : q.progress?.completed
- ? QuestState.Claimable
- : QuestState.Incomplete,
- );
- }, [questsWithProgress, selectedId]);
-
- if (quest === undefined) {
- return <>>;
- }
-
- return (
-
-
-
-
-
-
-
-
- {typeof questState !== "undefined" ||
- (questState !== QuestState.Incomplete && (
-
- ))}
- {typeof questState === "undefined" ||
- questState === QuestState.Incomplete
- ? "incomplete"
- : "completed"}
-
-
-
-
-
- Requirements
- {quest.questEvents?.length &&
- quest.questEvents?.map((evt) => {
- const eventProgress = questProgress?.completion?.find(
- (c) => c.questEvent === evt.id,
- );
- return (
-
- {eventProgress?.completed ? (
-
- ) : (
-
- )}
-
- {evt.description}
-
-
- );
- })}
-
-
- Rewards
- {quest.points && (
- }
- amount={quest.points}
- name="XP"
- />
- )}
-
-
-
-
-
-
-
- );
-}
-
-const Tag = ({ children }: { children: React.ReactNode }) => {
- return (
-
- {children}
-
- );
-};
-
-const Reward = ({
- icon,
- name,
- amount,
-}: {
- icon: React.ReactNode;
- name: string;
- amount: string;
-}) => {
- return (
-
-
- {icon}
-
-
- {amount} {name}
-
-
- );
-};
diff --git a/packages/keychain/src/components/Quests/Overview.tsx b/packages/keychain/src/components/Quests/Overview.tsx
deleted file mode 100644
index 54e1f3f01..000000000
--- a/packages/keychain/src/components/Quests/Overview.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Box, Flex, Text } from "@chakra-ui/react";
-import { QuestState } from "./types";
-import { QuestCard } from "./Card";
-import { PortalBanner } from "components/PortalBanner";
-import { QuestsDuoIcon } from "@cartridge/ui";
-
-export function QuestOverview({
- questsWithProgression,
- onSelect,
-}: {
- questsWithProgression: Array<{ quest: any; progress: any }>;
- onSelect: (id: string) => void;
-}) {
- return (
- <>
-
-
-
- <>
-
- {questsWithProgression?.map((qwp) => {
- if (!qwp.progress?.claimed) {
- return (
- onSelect(qwp.quest.id)}
- >
-
-
- );
- }
- })}
-
-
-
- {questsWithProgression?.map((qwp) => {
- if (qwp.progress?.claimed) {
- return (
- onSelect(qwp.quest.id)}
- >
-
-
- );
- }
- })}
- >
-
- >
- );
-}
-
-const Label = ({ children }: { children: React.ReactNode }) => (
-
-
- {children}
-
-
-);
diff --git a/packages/keychain/src/components/Quests/index.tsx b/packages/keychain/src/components/Quests/index.tsx
deleted file mode 100644
index 8a5e9f814..000000000
--- a/packages/keychain/src/components/Quests/index.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { Container } from "../Container";
-import { useEffect, useState } from "react";
-import { useAccountInfoQuery, useAccountQuestsQuery } from "generated/graphql";
-import { constants } from "starknet";
-import { QuestOverview } from "./Overview";
-import { QuestDetails } from "./Details";
-import { Flex, Text, useToast } from "@chakra-ui/react";
-
-export function Quests({
- gameId,
- address,
- chainId,
- onLogout,
-}: {
- gameId: string;
- address: string;
- chainId: constants.StarknetChainId;
- onLogout: () => void;
-}) {
- const toast = useToast();
- const [selectedQuestId, setSelectedQuestId] = useState();
- const [questsWithProgression, setQuestsWithProgression] =
- useState>();
- const { data: accountData } = useAccountInfoQuery({ address });
- const accountId = accountData?.accounts?.edges[0]?.node.id;
- const { data: questData, refetch: refetchQuests } = useAccountQuestsQuery(
- { accountId, gameId },
- {
- enabled: !!accountId,
- },
- );
- const questProgression = questData?.account?.questProgression?.edges;
- const quests = questData?.quests?.edges;
-
- useEffect(() => {
- const qwp: Array<{ quest: any; progress: any }> = [];
- quests?.forEach(({ node: q }) => {
- const progress = questProgression?.find(
- ({ node: qp }) => qp.questID === q.id,
- );
- qwp.push({
- quest: q,
- progress: progress?.node ?? null,
- });
- });
- setQuestsWithProgression(qwp);
- }, [quests, questProgression]);
-
- return (
- setSelectedQuestId(null) : undefined}
- onLogout={onLogout}
- >
- {!selectedQuestId ? (
- setSelectedQuestId(id)}
- />
- ) : (
- {
- const res = await refetchQuests();
- const quest = res.data.quests?.edges?.find((q) => q.node?.id);
- const progress = res.data.account?.questProgression.edges?.find(
- (qp) => qp.node?.questID === quest?.node?.id,
- );
- if (progress.node?.claimed) {
- toast({
- position: "top-right",
- title: "Quest Complete",
- render: () => (
-
- Quest Complete
-
- ),
- duration: 2000,
- });
- }
- }}
- />
- )}
-
- );
-}
diff --git a/packages/keychain/src/components/Quests/types.ts b/packages/keychain/src/components/Quests/types.ts
deleted file mode 100644
index f088fac47..000000000
--- a/packages/keychain/src/components/Quests/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export enum QuestState {
- Incomplete,
- Claimable,
- Complete,
-}
diff --git a/packages/keychain/src/components/Redeploy.tsx b/packages/keychain/src/components/Redeploy.tsx
index 560db09a8..04464a3cf 100644
--- a/packages/keychain/src/components/Redeploy.tsx
+++ b/packages/keychain/src/components/Redeploy.tsx
@@ -1,22 +1,22 @@
-import { constants, addAddressPadding } from "starknet";
-import { Container } from "./Container";
+import { addAddressPadding } from "starknet";
+import { Container, Banner } from "components/layout";
import Controller from "utils/controller";
import { useEffect } from "react";
import { client } from "utils/graphql";
import { DeployAccountDocument, AccountInfoDocument } from "generated/graphql";
import { Status } from "utils/account";
import { SparklesDuoIcon } from "@cartridge/ui";
-import { PortalBanner } from "./PortalBanner";
+import { useChainId } from "hooks/connection";
export function Redeploy({
- chainId,
controller,
onLogout,
}: {
- chainId: constants.StarknetChainId;
controller: Controller;
onLogout: () => void;
}) {
+ const chainId = useChainId();
+
useEffect(() => {
const deploy = async () => {
const result = await client.request(AccountInfoDocument, {
@@ -39,8 +39,8 @@ export function Redeploy({
}, [chainId, controller]);
return (
-
-
+ void;
onCancel: () => void;
onLogout: () => void;
}) {
+ const { controller } = useController();
const [messageData, setMessageData] = useState();
useEffect(() => {
@@ -64,53 +59,55 @@ export function SignMessage({
}, [controller, onSign, typedData]);
return (
-
-
+
-
- {(() => {
- if (!messageData) return <>>;
- const ptName = messageData.primaryType;
- const pt = messageData.types[ptName];
- const values = (typeName: string) => {
- const v = messageData.message[typeName];
- if (typeof v === "object") {
- return Object.entries(v).map(([key, value]) => {
- return (
-
-
- {key}:
- {" "}
- {value as string}
-
- );
- });
- } else {
- return {v as string};
- }
- };
+
+
+ {(() => {
+ if (!messageData) return <>>;
+ const ptName = messageData.primaryType;
+ const pt = messageData.types[ptName];
+ const values = (typeName: string) => {
+ const v = messageData.message[typeName];
+ if (typeof v === "object") {
+ return Object.entries(v).map(([key, value]) => {
+ return (
+
+
+ {key}:
+ {" "}
+ {value as string}
+
+ );
+ });
+ } else {
+ return {v as string};
+ }
+ };
- return pt.map((typ) => {
- return (
-
- {values(typ.name)}
-
- );
- });
- })()}
-
+ return pt.map((typ) => {
+ return (
+
+ {values(typ.name)}
+
+ );
+ });
+ })()}
+
+
-
+
+
);
}
diff --git a/packages/keychain/src/components/bridge/BridgeEth.tsx b/packages/keychain/src/components/bridge/BridgeEth.tsx
index d7b6972c5..f7876fccb 100644
--- a/packages/keychain/src/components/bridge/BridgeEth.tsx
+++ b/packages/keychain/src/components/bridge/BridgeEth.tsx
@@ -26,6 +26,7 @@ import { TransferButton } from "./TransferButton";
import { TxnTracker } from "./Transactions";
import { Label } from "./Label";
import Controller from "utils/controller";
+import { Banner } from "components/layout";
import { Error } from "components/Error";
import {
CheckIcon,
@@ -34,7 +35,6 @@ import {
MetaMaskIcon,
WedgeDownIcon,
} from "@cartridge/ui";
-import { PortalBanner } from "components/PortalBanner";
export function BridgeEth({
chainId,
@@ -116,7 +116,7 @@ export function BridgeEth({
return (
-
+
@@ -130,8 +130,8 @@ export function BridgeEth({
text={
!!ethAddress
? ethAddress.substring(0, 3) +
- "..." +
- ethAddress.substring(ethAddress.length - 4)
+ "..." +
+ ethAddress.substring(ethAddress.length - 4)
: "Metamask"
}
pointerEvents={!!ethAddress ? "none" : "auto"}
diff --git a/packages/keychain/src/components/bridge/Transactions.tsx b/packages/keychain/src/components/bridge/Transactions.tsx
index d7749f5f8..a230d3a43 100644
--- a/packages/keychain/src/components/bridge/Transactions.tsx
+++ b/packages/keychain/src/components/bridge/Transactions.tsx
@@ -6,7 +6,7 @@ import { constants } from "starknet";
import { mainnet, useWaitForTransaction } from "wagmi";
import { sepolia } from "wagmi/chains";
import { CheckIcon, ExternalIcon, TransferDuoIcon } from "@cartridge/ui";
-import { PortalBanner } from "components/PortalBanner";
+import { Banner } from "components/layout";
enum CardState {
PENDING = "PENDING",
@@ -30,7 +30,7 @@ export function TxnTracker({
return (
-
+
{txn.state === CardState.PENDING && (
<>
diff --git a/packages/keychain/src/components/connect/Authenticate.tsx b/packages/keychain/src/components/connect/Authenticate.tsx
deleted file mode 100644
index 9815d3c67..000000000
--- a/packages/keychain/src/components/connect/Authenticate.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import React, { useEffect, useState, useCallback } from "react";
-import { Button } from "@chakra-ui/react";
-import { Unsupported } from "./Unsupported";
-import { doSignup } from "hooks/account";
-import { Container } from "../Container";
-import { PortalBanner } from "components/PortalBanner";
-import { PortalFooter } from "components/PortalFooter";
-
-type AuthAction = "signup" | "login";
-
-export function Authenticate({
- name,
- action,
- onSuccess,
-}: {
- name: string;
- action: AuthAction;
- onSuccess: () => void;
-}) {
- const [isLoading, setIsLoading] = useState(false);
- const [unsupportedMessage, setUnsupportedMessage] = useState();
-
- const onAuth = useCallback(async () => {
- setIsLoading(true);
-
- try {
- switch (action) {
- case "signup":
- await doSignup(decodeURIComponent(name));
- break;
- case "login":
- break;
- default:
- throw new Error(`Unsupported action ${action}`);
- }
-
- onSuccess();
- } catch (e) {
- console.error(e);
- setIsLoading(false);
- throw e;
- }
- }, [onSuccess, action, name]);
-
- useEffect(() => {
- const userAgent = window.navigator.userAgent;
- if (/iPad|iPhone|iPod/.test(userAgent)) {
- const iosVersion = /OS (\d+)_?(?:\d+_?){0,2}\s/.exec(userAgent);
- if (iosVersion && Number(iosVersion[1]) < 16) {
- setUnsupportedMessage(
- `iOS ${iosVersion[1]} does not support passkeys. Upgrade to iOS 16 to continue`,
- );
- }
- }
- }, []);
-
- if (unsupportedMessage) {
- return ;
- }
-
- const title =
- action === "signup" ? "Authenticate Yourself" : "Hello from Cartridge!";
- const description =
- action === "signup" ? (
- <>
- You will now be asked to authenticate yourself.
-
- Note: this experience varies from browser to browser.
- >
- ) : (
- <>Please click continue.>
- );
- return (
- <>
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/packages/keychain/src/components/connect/Authenticate/Unsupported.tsx b/packages/keychain/src/components/connect/Authenticate/Unsupported.tsx
new file mode 100644
index 000000000..3f1a3d539
--- /dev/null
+++ b/packages/keychain/src/components/connect/Authenticate/Unsupported.tsx
@@ -0,0 +1,33 @@
+import { AlertIcon } from "@cartridge/ui";
+import { Container, Banner } from "components/layout";
+import { useEffect, useState } from "react";
+
+export function Unsupported({ message }: { message: string }) {
+ return (
+
+
+
+ );
+}
+
+export function useIsSupported() {
+ const [message, setMessage] = useState();
+
+ useEffect(() => {
+ const userAgent = window.navigator.userAgent;
+ if (/iPad|iPhone|iPod/.test(userAgent)) {
+ const iosVersion = /OS (\d+)_?(?:\d+_?){0,2}\s/.exec(userAgent);
+ if (iosVersion && Number(iosVersion[1]) < 16) {
+ setMessage(
+ `iOS ${iosVersion[1]} does not support passkeys. Upgrade to iOS 16 to continue`,
+ );
+ }
+ }
+ }, []);
+
+ return { isSupported: !message, message };
+}
diff --git a/packages/keychain/src/components/connect/Authenticate/index.tsx b/packages/keychain/src/components/connect/Authenticate/index.tsx
new file mode 100644
index 000000000..9cbde1228
--- /dev/null
+++ b/packages/keychain/src/components/connect/Authenticate/index.tsx
@@ -0,0 +1,72 @@
+import React, { useState, useCallback } from "react";
+import { Button } from "@chakra-ui/react";
+import { Unsupported, useIsSupported } from "./Unsupported";
+import { doSignup } from "hooks/account";
+import { Container, Banner, Footer } from "components/layout";
+
+export type AuthAction = "signup" | "login";
+
+export function Authenticate({
+ name,
+ action,
+ onSuccess,
+}: {
+ name: string;
+ action: AuthAction;
+ onSuccess: () => void;
+}) {
+ const [isLoading, setIsLoading] = useState(false);
+ const { isSupported, message } = useIsSupported();
+
+ const onAuth = useCallback(async () => {
+ setIsLoading(true);
+
+ try {
+ switch (action) {
+ case "signup":
+ await doSignup(decodeURIComponent(name));
+ break;
+ case "login":
+ break;
+ default:
+ throw new Error(`Unsupported action ${action}`);
+ }
+
+ onSuccess();
+ } catch (e) {
+ console.error(e);
+ throw e;
+ } finally {
+ setIsLoading(false);
+ }
+ }, [onSuccess, action, name]);
+
+ if (!isSupported) {
+ return ;
+ }
+
+ const title =
+ action === "signup" ? "Authenticate Yourself" : "Hello from Cartridge!";
+ const description =
+ action === "signup" ? (
+ <>
+ You will now be asked to authenticate yourself.
+
+ Note: this experience varies from browser to browser.
+ >
+ ) : (
+ <>Please click continue.>
+ );
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/packages/keychain/src/components/connect/BannerImage.tsx b/packages/keychain/src/components/connect/BannerImage.tsx
deleted file mode 100644
index 944f0afcc..000000000
--- a/packages/keychain/src/components/connect/BannerImage.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Box, StyleProps } from "@chakra-ui/react";
-import Image from "next/image";
-
-export function BannerImage({
- imgSrc,
- ...rest
-}: {
- imgSrc?: string;
-} & StyleProps) {
- return (
-
- {imgSrc && (
-
- )}
-
-
- );
-}
diff --git a/packages/keychain/src/components/connect/CreateSession.tsx b/packages/keychain/src/components/connect/CreateSession.tsx
index 501e48b35..b6a692bef 100644
--- a/packages/keychain/src/components/connect/CreateSession.tsx
+++ b/packages/keychain/src/components/connect/CreateSession.tsx
@@ -1,4 +1,4 @@
-import { Container, PortalBanner, PortalFooter } from "components";
+import { Container, Banner, Footer } from "components/layout";
import { BigNumberish } from "starknet";
import { Policy } from "@cartridge/controller";
import { PlugNewDuoIcon } from "@cartridge/ui";
@@ -7,14 +7,12 @@ import { useState } from "react";
import { useController } from "hooks/controller";
export function CreateSession({
- chainId,
policies,
origin,
onConnect,
onCancel,
onLogout,
}: {
- chainId: string;
policies: Policy[];
origin: string;
onConnect: (policies: Policy[]) => void;
@@ -26,37 +24,35 @@ export function CreateSession({
const [expiresAt] = useState(3000000000n);
const [maxFees] = useState();
return (
-
-
+
-
- <>
-
+
+
+
);
}
diff --git a/packages/keychain/src/components/connect/Login.tsx b/packages/keychain/src/components/connect/Login.tsx
index a3fa3f228..c2ee7c6ea 100644
--- a/packages/keychain/src/components/connect/Login.tsx
+++ b/packages/keychain/src/components/connect/Login.tsx
@@ -1,12 +1,13 @@
import { Field } from "@cartridge/ui";
-import { VStack, Button } from "@chakra-ui/react";
-import { Container } from "../Container";
-import { Form as FormikForm, Field as FormikField, Formik } from "formik";
+import { Button } from "@chakra-ui/react";
import {
- PORTAL_FOOTER_MIN_HEIGHT,
- PortalBanner,
- PortalFooter,
-} from "components";
+ Container,
+ FOOTER_MIN_HEIGHT,
+ Banner,
+ Footer,
+ Content,
+} from "components/layout";
+import { Form as FormikForm, Field as FormikField, Formik } from "formik";
import { useCallback, useState } from "react";
import Controller from "utils/controller";
import { FormValues, LoginMode, LoginProps } from "./types";
@@ -104,7 +105,7 @@ export function Login({
);
return (
-
+
{(props) => (
-
-
+
-
+
-
+
+
)}
diff --git a/packages/keychain/src/components/Logout.tsx b/packages/keychain/src/components/connect/Logout.tsx
similarity index 71%
rename from packages/keychain/src/components/Logout.tsx
rename to packages/keychain/src/components/connect/Logout.tsx
index 268186b17..afcef879f 100644
--- a/packages/keychain/src/components/Logout.tsx
+++ b/packages/keychain/src/components/connect/Logout.tsx
@@ -1,8 +1,6 @@
import { Button } from "@chakra-ui/react";
-import { Container } from "./Container";
-import { PortalBanner } from "./PortalBanner";
+import { Container, Banner, Footer } from "components/layout";
import { LogoutDuoIcon } from "@cartridge/ui";
-import { PortalFooter } from "./PortalFooter";
export function Logout({
onConfirm,
@@ -13,18 +11,18 @@ export function Logout({
}) {
return (
-
-
+
+
);
}
diff --git a/packages/keychain/src/components/connect/MediaViewer.tsx b/packages/keychain/src/components/connect/MediaViewer.tsx
deleted file mode 100644
index 608f2365b..000000000
--- a/packages/keychain/src/components/connect/MediaViewer.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Flex, Spinner } from "@chakra-ui/react";
-import Image, { ImageProps } from "next/image";
-import Script from "next/script";
-import { useState } from "react";
-
-export function MediaViewer({
- height,
- width,
- src,
- alt = "Media viewer",
-}: {
- src: string;
-} & Pick) {
- const ext = src?.split(".").pop();
- const [isLoading, setIsLoading] = useState(true);
-
- if (ext === "glb") {
- return (
- <>
-