diff --git a/apps/web/package.json b/apps/web/package.json index cd865c4e..90126a78 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,6 +11,7 @@ "dependencies": { "@headlessui/react": "1.7.10", "@heroicons/react": "^2.0.18", + "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toolbar": "^1.0.4", diff --git a/apps/web/src/app/(routes)/bridge/[address]/page.tsx b/apps/web/src/app/(routes)/bridge/[address]/page.tsx index b5f95462..16db1100 100644 --- a/apps/web/src/app/(routes)/bridge/[address]/page.tsx +++ b/apps/web/src/app/(routes)/bridge/[address]/page.tsx @@ -3,7 +3,11 @@ import Link from "next/link"; import TokenList from "./TokenList"; -export default function Page({ params }: { params: { address: string } }) { +interface PageProps { + params: { address: string }; +} + +export default function Page({ params: { address } }: PageProps) { return ( <> {/* TODO @YohanTz: Refacto to be a variant in the Button component */} @@ -30,7 +34,7 @@ export default function Page({ params }: { params: { address: string } }) { Back - + ); } diff --git a/apps/web/src/app/(routes)/bridge/_components/NftTransferSummary.tsx b/apps/web/src/app/(routes)/bridge/_components/NftTransferSummary.tsx index edc2aacf..dd5be3b9 100644 --- a/apps/web/src/app/(routes)/bridge/_components/NftTransferSummary.tsx +++ b/apps/web/src/app/(routes)/bridge/_components/NftTransferSummary.tsx @@ -7,8 +7,7 @@ import { Typography, } from "design-system"; import Image from "next/image"; -import { redirect } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import useCurrentChain from "~/app/_hooks/useCurrentChain"; import { api } from "~/utils/api"; @@ -21,20 +20,8 @@ function TransferAction() { const { sourceChain, targetChain } = useCurrentChain(); const { totalSelectedNfts } = useNftSelection(); - const { - approveForAll, - depositTokens, - isApproveLoading, - isApprovedForAll, - isDepositLoading, - isDepositSuccess, - } = useTransferNftsFromChain(sourceChain); - - useEffect(() => { - if (isDepositSuccess) { - redirect("/lounge"); - } - }, [isDepositSuccess]); + const { approveForAll, depositTokens, isApproveLoading, isApprovedForAll } = + useTransferNftsFromChain(sourceChain); return isApprovedForAll ? ( <> @@ -91,25 +78,13 @@ function TransferAction() { - {isDepositLoading && ( - Bridge loading animation - )} ) : ( <> - {totalSelectedNfts > 0 && ( + {totalSelectedNfts > 0 && isApprovedForAll !== undefined && ( { + if (depositHash !== undefined) { + void router.push(`lounge/${depositHash}`); + } + }, [depositHash, router]); + + // const { isLoading: isDepositLoading, isSuccess: isDepositSuccess } = + // useWaitForTransactionReceipt({ hash: depositHash }); return { approveForAll: () => approveForAll(), depositTokens: () => depositTokens(), + depositTransactionHash: depositHash, isApproveLoading: isApproveLoading && approveHash !== undefined, isApprovedForAll, - isDepositLoading: isDepositLoading && depositHash !== undefined, - isDepositSuccess: isDepositSuccess && depositHash !== undefined, }; } diff --git a/apps/web/src/app/(routes)/bridge/_hooks/useTransferStarknetNfts.ts b/apps/web/src/app/(routes)/bridge/_hooks/useTransferStarknetNfts.ts index 2f77fa50..dd56dd3f 100644 --- a/apps/web/src/app/(routes)/bridge/_hooks/useTransferStarknetNfts.ts +++ b/apps/web/src/app/(routes)/bridge/_hooks/useTransferStarknetNfts.ts @@ -12,6 +12,7 @@ import useNftSelection from "./useNftSelection"; const L2_BRIDGE_ADDRESS = process.env.NEXT_PUBLIC_L2_BRIDGE_ADDRESS || ""; +// TODO @YohanTz: Divide this hook in 2 (approve / deposit) export default function useTransferStarknetNfts() { const { selectedCollectionAddress, selectedTokenIds } = useNftSelection(); @@ -128,19 +129,17 @@ export default function useTransferStarknetNfts() { ], }); - const { isLoading: isDepositLoading, isSuccess: isDepositSuccess } = - useWaitForTransaction({ - hash: depositData?.transaction_hash, - }); + // const { isLoading: isDepositLoading, isSuccess: isDepositSuccess } = + // useWaitForTransaction({ + // hash: depositData?.transaction_hash, + // }); return { approveForAll: () => approveForAll(), depositTokens: () => depositTokens(), + depositTransactionHash: depositData?.transaction_hash, isApproveLoading: isApproveLoading && approveData?.transaction_hash !== undefined, isApprovedForAll, - isDepositLoading: - isDepositLoading && depositData?.transaction_hash !== undefined, - isDepositSuccess, }; } diff --git a/apps/web/src/app/(routes)/lounge/[transactionHash]/page.tsx b/apps/web/src/app/(routes)/lounge/[transactionHash]/page.tsx new file mode 100644 index 00000000..604c1ada --- /dev/null +++ b/apps/web/src/app/(routes)/lounge/[transactionHash]/page.tsx @@ -0,0 +1,40 @@ +"use client"; +import Image from "next/image"; +import { redirect } from "next/navigation"; +import { useEffect } from "react"; + +import { api } from "~/utils/api"; + +interface PageProps { + params: { transactionHash: string }; +} + +/** + * Page used when waiting for a deposit transaction to be detected by the bridge + */ +export default function Page({ params: { transactionHash } }: PageProps) { + console.log(transactionHash); + const { data: hasBridgeRequestBeenIndexed } = + api.bridgeRequest.getHasBrigeRequestIndexed.useQuery( + { + transactionHash, + }, + { refetchInterval: 1000 } + ); + + useEffect(() => { + if (hasBridgeRequestBeenIndexed) { + redirect("/lounge"); + } + }, [hasBridgeRequestBeenIndexed]); + + return ( + Bridge loading animation + ); +} diff --git a/apps/web/src/app/(routes)/lounge/_components/NftTransferCard.tsx b/apps/web/src/app/(routes)/lounge/_components/NftTransferCard.tsx deleted file mode 100644 index 034477eb..00000000 --- a/apps/web/src/app/(routes)/lounge/_components/NftTransferCard.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { Typography } from "design-system"; -import Image from "next/image"; -import { useState } from "react"; - -import NftCardStackBackground from "~/app/_components/NftCard/NftCardStackBackground"; -import { type BridgeRequestEventStatus } from "~/server/api/routers/bridgeRequest"; - -import NftCardStatus from "./NftCardStatus"; -import NftTransferModal from "./NftTransferModal"; - -interface NftTransferCard { - image?: string; - name: string; - status: BridgeRequestEventStatus; - statusTimestamp: number; -} - -function utcUnixSecondsToIso8601(utcTs: number) { - const utcMs = utcTs * 1000; - - const localDt = new Date(); - - // Get the local timezone offset in minutes and convert it to milliseconds. - const localOffset = localDt.getTimezoneOffset() * 60 * 1000; - // Calculate the local time. - const localTs = utcMs + localOffset; - - const date = new Date(localTs); - - // Get day, month, and year components from the Date object - const day = date.getDate(); - const month = date.getMonth() + 1; // Months are zero-based, so add 1 - const year = date.getFullYear(); - - // Get hours and minutes components from the Date object - const hours = date.getHours(); - const minutes = date.getMinutes(); - - // Format day, month, hours, and minutes to have leading zeros if necessary - const formattedDay = day < 10 ? "0" + day.toString() : day; - const formattedMonth = month < 10 ? "0" + month.toString() : month; - const formattedHours = (hours < 10 ? "0" : "") + hours.toString(); - const formattedMinutes = minutes < 10 ? "0" + String(minutes) : String(minutes); - - // Create the formatted date string in the format "dd/mm/yyyy HH:mm" - const formattedDateTime = `${formattedDay}/${formattedMonth}/${year} ${formattedHours}:${formattedMinutes}`; - - return formattedDateTime; -} - -export default function NftTransferCard({ - image, - name, - status, - statusTimestamp, -}: NftTransferCard) { - const [isModalOpen, setIsModalOpen] = useState(false); - const readableDate = utcUnixSecondsToIso8601(statusTimestamp); - - function handleOpenModal() { - setIsModalOpen(true); - } - - return ( -
- - - -
- ); -} diff --git a/apps/web/src/app/(routes)/lounge/_components/NftTransferItem.tsx b/apps/web/src/app/(routes)/lounge/_components/NftTransferItem.tsx new file mode 100644 index 00000000..23ce528c --- /dev/null +++ b/apps/web/src/app/(routes)/lounge/_components/NftTransferItem.tsx @@ -0,0 +1,71 @@ +import * as Collapsible from "@radix-ui/react-collapsible"; +import { Typography } from "design-system"; +import { useState } from "react"; + +import NftTransferItemContent from "./NftTransferItemContent"; +import NftTransferStatus from "./NftTransferStatus"; + +interface NftTransferItemProps { + contractAddress: string; + tokenIds: Array; + totalCount: number; +} + +export default function NftTransferItem({ + contractAddress, + tokenIds, + totalCount, +}: NftTransferItemProps) { + const [open, setOpen] = useState(false); + + return ( + +
+
+
+
+ + Everai Collection + + + ID: 123121312 + + + {totalCount} {totalCount > 1 ? "Nfts" : "Nft"} + +
+
+ + + +
+ + kwiss.stark + +
+ +
+ + + + +
+ + + + ); +} diff --git a/apps/web/src/app/(routes)/lounge/_components/NftTransferItemContent.tsx b/apps/web/src/app/(routes)/lounge/_components/NftTransferItemContent.tsx new file mode 100644 index 00000000..04c15fc1 --- /dev/null +++ b/apps/web/src/app/(routes)/lounge/_components/NftTransferItemContent.tsx @@ -0,0 +1,95 @@ +import * as Collapsible from "@radix-ui/react-collapsible"; +import { Typography } from "design-system"; +import Image from "next/image"; + +import { api } from "~/utils/api"; + +import NftTransferStatus from "./NftTransferStatus"; + +interface NftTransferItemContentProps { + contractAddress: string; + open: boolean; + tokenIds: Array; +} + +export default function NftTransferItemContent({ + contractAddress, + open, + tokenIds, +}: NftTransferItemContentProps) { + const { data: nfts } = api.nfts.getL1NftMetadataBatch.useQuery( + { + contractAddress, + tokenIds, + }, + { + enabled: open, + } + ); + + if (nfts === undefined) { + return <>; + } + + return ( + +
+
+
+ {nfts.map((nft) => { + console.log(nft); + return ( +
+
+
+ {nft.image !== undefined ? ( + nft + ) : ( + <> + empty Nft image + empty Nft image + + )} +
+ + {nft.collectionName} + + + {nft.tokenName} + +
+
+ + +
+ + kwiss.stark + +
+
+
+ ); + })} +
+
+ + ); +} diff --git a/apps/web/src/app/(routes)/lounge/_components/NftTransferList.tsx b/apps/web/src/app/(routes)/lounge/_components/NftTransferList.tsx index 8941298a..3c77a5e4 100644 --- a/apps/web/src/app/(routes)/lounge/_components/NftTransferList.tsx +++ b/apps/web/src/app/(routes)/lounge/_components/NftTransferList.tsx @@ -1,24 +1,42 @@ -"use client"; - -import * as Toolbar from "@radix-ui/react-toolbar"; import { Typography } from "design-system"; -import Image from "next/image"; -import { useState } from "react"; -import NftsEmptyState from "~/app/_components/NftsEmptyState"; -import NftsLoadingState from "~/app/_components/NftsLoadingState"; import useAccountFromChain from "~/app/_hooks/useAccountFromChain"; import useCurrentChain from "~/app/_hooks/useCurrentChain"; import { api } from "~/utils/api"; -import NftTransferCard from "./NftTransferCard"; +import NftTransferItem from "./NftTransferItem"; + +interface NftTransferHeaderProps { + className?: string; +} -export default function NftTransferList() { - const [displayOption, setDisplayOption] = useState("card"); +function NftTransferHeader({ className }: NftTransferHeaderProps) { + return ( +
+ + Nfts transfered (8) + + + Transfer status + + + Arrival + +
+ ); +} + +interface NftTransferListProps { + className?: string; +} + +export default function NftTransferListt({ className }: NftTransferListProps) { const { targetChain } = useCurrentChain(); const { address } = useAccountFromChain(targetChain); - const { data: bridgeRequestData } = + const { data: bridgeRequests } = api.bridgeRequest.getBridgeRequestsFromAddress.useQuery( { address: address ?? "", @@ -26,270 +44,40 @@ export default function NftTransferList() { { enabled: address !== undefined, refetchInterval: 3000 } ); - if (bridgeRequestData === undefined) { - return ; + if (bridgeRequests === undefined) { + // Loading state + return <>; + } + + if (bridgeRequests.length === 0) { + // Empty state + return <>; } - return bridgeRequestData.length === 0 ? ( - <> - + return ( +
- There is nothing there... + Your past transactions - - ) : ( -
-
- - Nfts in transit ({bridgeRequestData.length}) - - - { - if (value) { - setDisplayOption(value); - } - }} - aria-label="Display options" - className="hidden overflow-hidden rounded-md border border-[#d3e2e1] dark:border-dark-blue-600 sm:flex" - type="single" - value={displayOption} - > - - - - - - - - - - - - - - - - - - - - - - - - -
+
- {displayOption === "card" ? ( -
- {bridgeRequestData.map((bridgeRequest, index) => { - return ( - - ); - })} -
- ) : ( - - - - - Nfts in transit ({bridgeRequestData.length}) - - - Transfer status - - - Arrival - - - Grid options - - - + - - {bridgeRequestData.map((bridgeRequest, index) => { - return ( - - - - - - - - ); - })} - -
-
- nft image -
- - {bridgeRequest.sourceCollection} - - - No Nft name - -
-
-
- {/* TODO @YohanTz: Extract this badge to its own component (used in cards also) */} - - {bridgeRequest.status} - - -
-
- - Estimated arrival - - - {bridgeRequest.statusTimestamp} - -
- {/* */} -
-
- + -
- )} +
+ {bridgeRequests.map((bridgeRequest) => { + return ( + + ); + })} +
); } diff --git a/apps/web/src/app/(routes)/lounge/_components/NftTransferModal.tsx b/apps/web/src/app/(routes)/lounge/_components/NftTransferModal.tsx deleted file mode 100644 index 07ce1fd0..00000000 --- a/apps/web/src/app/(routes)/lounge/_components/NftTransferModal.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { Modal, Typography } from "design-system"; -import Image from "next/image"; - -import useCurrentChain from "~/app/_hooks/useCurrentChain"; - -import { CHAIN_LOGOS_BY_NAME } from "../../../_lib/utils/connectors"; - -interface NftTransferModalProps { - image?: string; - isOpen: boolean; - name: string; - onOpenChange: (open: boolean) => void; -} - -export default function NftTransferModal({ - image, - isOpen, - name, - onOpenChange, -}: NftTransferModalProps) { - const { sourceChain, targetChain } = useCurrentChain(); - return ( - -
-
-
- {image ? ( - nft image - ) : ( -
- No metadata -
- )} -
-
- - {name} -
- Migration in Progress -
- - Your asset cross the bridge, the small walk will take 15 minutes - -
-
-
- {/*
*/} - {`${CHAIN_LOGOS_BY_NAME[sourceChain]} - - - 🌈
- Assets en route to {targetChain} -
- - {`${CHAIN_LOGOS_BY_NAME[targetChain]} -
-
-
- Transaction sent - 1/1 -
-
- - Transaction confirmed - - 1/1 -
-
- - Nfts received on {targetChain} - - 1/1 -
-
-
- - Note that it will not cancel the gas fee. - - {/* */} -
-
- - ); -} diff --git a/apps/web/src/app/(routes)/lounge/_components/NftCardStatus.tsx b/apps/web/src/app/(routes)/lounge/_components/NftTransferStatus.tsx similarity index 86% rename from apps/web/src/app/(routes)/lounge/_components/NftCardStatus.tsx rename to apps/web/src/app/(routes)/lounge/_components/NftTransferStatus.tsx index a807fa94..514b04d9 100644 --- a/apps/web/src/app/(routes)/lounge/_components/NftCardStatus.tsx +++ b/apps/web/src/app/(routes)/lounge/_components/NftTransferStatus.tsx @@ -23,14 +23,18 @@ const variantsToStatusText: Record = { }; interface NftCardStatusProps { + className?: string; status: keyof typeof variants; } -export default function NftCardStatus({ status }: NftCardStatusProps) { +export default function NftCardStatus({ + className, + status, +}: NftCardStatusProps) { return ( <> diff --git a/apps/web/src/app/(routes)/lounge/page.tsx b/apps/web/src/app/(routes)/lounge/page.tsx index 63f36ea1..e523f2ca 100644 --- a/apps/web/src/app/(routes)/lounge/page.tsx +++ b/apps/web/src/app/(routes)/lounge/page.tsx @@ -1,10 +1,13 @@ "use client"; +import Link from "next/link"; + import Footer from "~/app/_components/Footer"; import MainPageContainer from "../../_components/MainPageContainer"; import Banner from "./_components/Banner"; import ChainSwitch from "./_components/ChainSwitch"; +// import NftTransferList from "./_components/NftTransferList"; import NftTransferList from "./_components/NftTransferList"; export default function Page() { @@ -12,9 +15,12 @@ export default function Page() { <>
+ + CHECK + - +