diff --git a/apps/dashboard/src/@/actions/validLogin.ts b/apps/dashboard/src/@/actions/validLogin.ts deleted file mode 100644 index 03d637d2b06..00000000000 --- a/apps/dashboard/src/@/actions/validLogin.ts +++ /dev/null @@ -1,48 +0,0 @@ -"use server"; - -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; -import { cookies } from "next/headers"; -import { getAddress } from "thirdweb"; -import { COOKIE_PREFIX_TOKEN } from "../constants/cookie"; -import { API_SERVER_URL } from "../constants/env"; - -/** - * Check that the connected wallet is valid for the current active account - */ -export async function isWalletValidForActiveAccount(params: { - address: string; - account: Account; - authToken: string; -}) { - const cookieStore = await cookies(); - const authCookieNameForAddress = - COOKIE_PREFIX_TOKEN + getAddress(params.address); - - // authToken for this wallet address should be present - const authTokenForAddress = cookieStore.get(authCookieNameForAddress)?.value; - if (!authTokenForAddress) { - return false; - } - - // this authToken should be same as current active authToken - if (authTokenForAddress !== params.authToken) { - return false; - } - - // authToken should be valid - const accountRes = await fetch(`${API_SERVER_URL}/v1/account/me`, { - method: "GET", - headers: { - Authorization: `Bearer ${authTokenForAddress}`, - }, - }); - - if (accountRes.status !== 200) { - return false; - } - - const account = (await accountRes.json()).data as Account; - - // the current account should match the account fetched for the authToken - return account.id === params.account.id; -} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx index c762a0fe766..e220810b628 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx @@ -25,7 +25,6 @@ import { } from "@/constants/env"; import { useThirdwebClient } from "@/constants/thirdweb.client"; import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { zodResolver } from "@hookform/resolvers/zod"; import { Turnstile } from "@marsidev/react-turnstile"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; @@ -52,7 +51,6 @@ import { useWalletBalance, } from "thirdweb/react"; import { z } from "zod"; -import { isAccountOnboardingComplete } from "../../../../../../login/onboarding/isOnboardingRequired"; function formatTime(seconds: number) { const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); @@ -78,11 +76,11 @@ const claimFaucetSchema = z.object({ export function FaucetButton({ chain, amount, - twAccount, + isLoggedIn, }: { chain: ChainMetadata; amount: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const pathname = usePathname(); const client = useThirdwebClient(); @@ -186,7 +184,7 @@ export function FaucetButton({ { @@ -198,7 +196,7 @@ export function FaucetButton({ } // Force users to log in to claim the faucet - if (!address || !twAccount) { + if (!address || !isLoggedIn) { return ( - ); - } - // Can not claim if (canClaimFaucetQuery.data && canClaimFaucetQuery.data.canClaim === false) { return ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx index 196ff1e9576..49f76af8f6a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx @@ -1,4 +1,3 @@ -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ChainMetadata } from "thirdweb/chains"; import { getFaucetClaimAmount } from "../../../../../../api/testnet-faucet/claim/claim-amount"; import { ChainIcon } from "../../../../components/server/chain-icon"; @@ -8,9 +7,9 @@ import { SectionTitle } from "./SectionTitle"; export async function FaucetSection(props: { chain: ChainMetadata; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { - const { chain, twAccount } = props; + const { chain, isLoggedIn } = props; // Check eligibility. const sanitizedChainName = chain.name.replace("Mainnet", "").trim(); @@ -47,7 +46,7 @@ export async function FaucetSection(props: { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx index 4dcb7b16e07..4193da0672c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx @@ -19,7 +19,6 @@ import type { Metadata } from "next"; import Link from "next/link"; import { redirect } from "next/navigation"; import { mapV4ChainToV5Chain } from "../../../../../contexts/map-chains"; -import { getRawAccount } from "../../../../account/settings/getAccount"; import { getAuthToken, getAuthTokenWalletAddress, @@ -61,10 +60,9 @@ export default async function ChainPageLayout(props: { }) { const params = await props.params; const { children } = props; - const [chain, authToken, account, accountAddress] = await Promise.all([ + const [chain, authToken, accountAddress] = await Promise.all([ getChain(params.chain_id), getAuthToken(), - getRawAccount(), getAuthTokenWalletAddress(), ]); @@ -100,7 +98,6 @@ The following is the user's message: <> + ) : chain.services.find((c) => c.service === "pay" && c.enabled) ? ( ) : null} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx index 47ec0db126e..5f02901bfcd 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/cancel-tab.tsx @@ -1,6 +1,4 @@ "use client"; - -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; import { useAllChainsData } from "hooks/chains/allChains"; @@ -13,14 +11,14 @@ interface CancelTabProps { id: string; contract: ThirdwebContract; isAuction?: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const CancelTab: React.FC = ({ id, contract, isAuction, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const { idToChain } = useAllChainsData(); @@ -32,7 +30,7 @@ export const CancelTab: React.FC = ({ return (
= ({ createText = "Create", type, contract, - twAccount, + isLoggedIn, isInsightSupported, ...restButtonProps }) => { @@ -77,7 +76,7 @@ export const CreateListingButton: React.FC = ({ />
= ({ ) : (
>; mode: "automatic" | "manual"; type?: "direct-listings" | "english-auctions"; - twAccount: Account | undefined; + isLoggedIn: boolean; isInsightSupported: boolean; }; @@ -104,7 +103,7 @@ export const CreateListingsForm: React.FC = ({ type, actionText, setOpen, - twAccount, + isLoggedIn, mode, isInsightSupported, }) => { @@ -659,7 +658,7 @@ export const CreateListingsForm: React.FC = ({ Cancel void; data: DirectListing | EnglishAuction | null; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ListingDrawer: React.FC = ({ @@ -32,7 +30,7 @@ export const ListingDrawer: React.FC = ({ isOpen, onClose, data, - twAccount, + isLoggedIn, }) => { const address = useActiveAccount()?.address; const prevData = usePrevious(data); @@ -161,7 +159,7 @@ export const ListingDrawer: React.FC = ({ contract={contract} id={renderData.id.toString()} isAuction={renderData.type === "english-auction"} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> )} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/marketplace-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/marketplace-table.tsx index 0f56344b01b..910a0a4358f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/marketplace-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/marketplace-table.tsx @@ -1,6 +1,5 @@ import { WalletAddress } from "@/components/blocks/wallet-address"; import { Button } from "@/components/ui/button"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { IconButton, Select, @@ -99,7 +98,7 @@ interface MarketplaceTableProps { start: number; }> >; - twAccount: Account | undefined; + isLoggedIn: boolean; } const DEFAULT_QUERY_STATE = { count: 50, start: 0 }; @@ -111,7 +110,7 @@ export const MarketplaceTable: React.FC = ({ totalCountQuery, queryParams, setQueryParams, - twAccount, + isLoggedIn, }) => { const [listingsToShow, setListingsToShow_] = useState<"all" | "valid">("all"); @@ -216,7 +215,7 @@ export const MarketplaceTable: React.FC = ({ data={tokenRow} isOpen={!!tokenRow} onClose={() => setTokenRow(null)} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx index a98bc3cb26d..779be64a80f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../../_components/page-skeletons"; import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client"; @@ -9,7 +8,7 @@ import { ContractDirectListingsPage } from "./ContractDirectListingsPage"; export function ContractDirectListingsPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -28,7 +27,7 @@ export function ContractDirectListingsPageClient(props: { return ( ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.tsx index 4ca2669c5c5..3c3aa1a4c77 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/ContractDirectListingsPage.tsx @@ -1,19 +1,18 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { CreateListingButton } from "../components/list-button"; import { DirectListingsTable } from "./components/table"; interface ContractDirectListingsPageProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; isInsightSupported: boolean; } export const ContractDirectListingsPage: React.FC< ContractDirectListingsPageProps -> = ({ contract, twAccount, isInsightSupported }) => { +> = ({ contract, isLoggedIn, isInsightSupported }) => { return (
@@ -26,12 +25,12 @@ export const ContractDirectListingsPage: React.FC< contract={contract} type="direct-listings" createText="Create Direct Listing" - twAccount={twAccount} + isLoggedIn={isLoggedIn} />
- + ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/components/table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/components/table.tsx index ba981978120..73958adc9ce 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/components/table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/components/table.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useState } from "react"; import type { ThirdwebContract } from "thirdweb"; import { @@ -13,14 +12,14 @@ import { MarketplaceTable } from "../../components/marketplace-table"; interface DirectListingsTableProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } const DEFAULT_QUERY_STATE = { count: 50, start: 0 }; export const DirectListingsTable: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE); const getAllQueryResult = useReadContract(getAllListings, { @@ -43,7 +42,7 @@ export const DirectListingsTable: React.FC = ({ totalCountQuery={totalCountQuery} queryParams={queryParams} setQueryParams={setQueryParams} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx index 2b44bb4f8fd..a0b938f4fb3 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/direct-listings/page.tsx @@ -24,7 +24,7 @@ export default async function Page(props: { return ( ); } @@ -39,7 +39,7 @@ export default async function Page(props: { return ( ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx index 2288bf727f1..26d755c3cf4 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../../_components/page-skeletons"; import { RedirectToContractOverview } from "../../_components/redirect-contract-overview.client"; @@ -9,7 +8,7 @@ import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage"; export function ContractEnglishAuctionsPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -28,7 +27,7 @@ export function ContractEnglishAuctionsPageClient(props: { return ( ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.tsx index 1b9491d2c6f..b2d6e01e300 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/ContractEnglishAuctionsPage.tsx @@ -1,19 +1,18 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { CreateListingButton } from "../components/list-button"; import { EnglishAuctionsTable } from "./components/table"; interface ContractEnglishAuctionsProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; isInsightSupported: boolean; } export const ContractEnglishAuctionsPage: React.FC< ContractEnglishAuctionsProps -> = ({ contract, twAccount, isInsightSupported }) => { +> = ({ contract, isLoggedIn, isInsightSupported }) => { return (
@@ -25,13 +24,13 @@ export const ContractEnglishAuctionsPage: React.FC< contract={contract} type="english-auctions" createText="Create English Auction" - twAccount={twAccount} + isLoggedIn={isLoggedIn} isInsightSupported={isInsightSupported} />
- + ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/components/table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/components/table.tsx index 74dabe2594b..34a4006ccc1 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/components/table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/english-auctions/components/table.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useState } from "react"; import type { ThirdwebContract } from "thirdweb"; import { @@ -13,14 +12,14 @@ import { MarketplaceTable } from "../../components/marketplace-table"; interface EnglishAuctionsTableProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } const DEFAULT_QUERY_STATE = { count: 50, start: 0 }; export const EnglishAuctionsTable: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE); const getAllQueryResult = useReadContract(getAllAuctions, { @@ -37,7 +36,7 @@ export const EnglishAuctionsTable: React.FC = ({ return ( ); } @@ -40,7 +40,7 @@ export default async function Page(props: { return ( ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx index b6e752d9c00..b3a71cabb3b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/index.tsx @@ -3,7 +3,6 @@ import { Spinner } from "@/components/ui/Spinner/Spinner"; import { ToolTipLabel } from "@/components/ui/tooltip"; import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useIsAdmin } from "@3rdweb-sdk/react/hooks/useContractRoles"; import { Alert, @@ -187,7 +186,7 @@ interface ClaimConditionsFormProps { isColumn?: true; isErc20: boolean; isMultiPhase: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ClaimConditionsForm: React.FC = ({ @@ -196,7 +195,7 @@ export const ClaimConditionsForm: React.FC = ({ isColumn, isErc20, isMultiPhase, - twAccount, + isLoggedIn, }) => { // assume 1155 if we have a tokenId const isErc1155 = tokenId !== undefined; @@ -636,7 +635,7 @@ export const ClaimConditionsForm: React.FC = ({ {controlledFields.some((field) => field.fromSdk) && ( = ({ hasRemovedPhases || !isMultiPhase ? ( = ({ @@ -17,7 +16,7 @@ export const ClaimConditions: React.FC = ({ tokenId, isColumn, isERC20, - twAccount, + isLoggedIn, isMultiphase, }) => { return ( @@ -34,7 +33,7 @@ export const ClaimConditions: React.FC = ({ {/* Set Claim Conditions */} = ({ contract, tokenId, isErc20, - twAccount, + isLoggedIn, isMultiphase, }) => { const trackEvent = useTrack(); @@ -95,7 +94,7 @@ export const ResetClaimEligibility: React.FC = ({ return ( }> ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.tsx index 548ebd8bc42..40d651ddee0 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/AccountPage.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import type { ChainMetadata } from "thirdweb/chains"; import { Heading } from "tw-components"; @@ -11,14 +10,14 @@ import { NftsOwned } from "./components/nfts-owned"; interface AccountPageProps { contract: ThirdwebContract; chainMetadata: ChainMetadata; - twAccount: Account | undefined; + isLoggedIn: boolean; isInsightSupported: boolean; } export const AccountPage: React.FC = ({ contract, chainMetadata, - twAccount, + isLoggedIn, isInsightSupported, }) => { const symbol = chainMetadata.nativeCurrency.symbol || "Native Token"; @@ -35,7 +34,7 @@ export const AccountPage: React.FC = ({ {chainMetadata && ( = ({ address, symbol, chain, - twAccount, + isLoggedIn, }) => { const client = useThirdwebClient(); const { mutate: transfer, isPending } = useSendAndConfirmTransaction(); @@ -49,7 +48,7 @@ export const DepositNative: React.FC = ({ value={amount} /> ); } @@ -44,7 +44,7 @@ export default async function Page(props: { ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx index d708264b402..1cb62b58268 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; @@ -9,7 +8,7 @@ import { AccountsPage } from "./AccountsPage"; export function AccountsPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -25,5 +24,7 @@ export function AccountsPageClient(props: { return ; } - return ; + return ( + + ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx index 5981eccf758..5c2d81fff05 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/AccountsPage.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { ButtonGroup, Flex } from "@chakra-ui/react"; import type { ThirdwebContract } from "thirdweb"; import { Heading, TrackedLinkButton } from "tw-components"; @@ -10,12 +9,12 @@ import { CreateAccountButton } from "./components/create-account-button"; interface AccountsPageProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const AccountsPage: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { return ( @@ -40,7 +39,7 @@ export const AccountsPage: React.FC = ({ > View Documentation - + diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx index 2ffdc9eca1e..f61ed87d287 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx @@ -2,7 +2,6 @@ import { Button } from "@/components/ui/button"; import { ToolTipLabel } from "@/components/ui/tooltip"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { TransactionButton } from "components/buttons/TransactionButton"; import type { ThirdwebContract } from "thirdweb"; import * as ERC4337Ext from "thirdweb/extensions/erc4337"; @@ -14,12 +13,12 @@ import { interface CreateAccountButtonProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const CreateAccountButton: React.FC = ({ contract, - twAccount, + isLoggedIn, ...restButtonProps }) => { const sendTxMutation = useSendAndConfirmTransaction(); @@ -62,7 +61,7 @@ export const CreateAccountButton: React.FC = ({ return ( { const tx = ERC4337Ext.createAccount({ diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx index 04fb4c6fbde..e3bad8a74e8 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/page.tsx @@ -24,7 +24,7 @@ export default async function Page(props: { const account = await getRawAccount(); if (chainMetadata.chainId === localhost.id) { - return ; + return ; } const { isAccountFactory } = await getContractPageMetadata(contract); @@ -33,5 +33,5 @@ export default async function Page(props: { redirect(`/${params.chain_id}/${params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx index e42fe28465c..ae20d7af531 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/ClaimConditions.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ClaimConditions } from "../_components/claim-conditions/claim-conditions"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; @@ -9,7 +8,7 @@ import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; export function ClaimConditionsClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -35,7 +34,7 @@ export function ClaimConditionsClient(props: { ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx index 8325cfa20ca..97efacfc673 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/page.tsx @@ -23,7 +23,7 @@ export default async function Page(props: { const account = await getRawAccount(); if (chainMetadata.chainId === localhost.id) { - return ; + return ; } const { @@ -40,7 +40,7 @@ export default async function Page(props: { ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx index 3bcef38aac0..96965e7f9a6 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import type { ChainMetadata } from "thirdweb/chains"; import { LoadingPage } from "../_components/page-skeletons"; @@ -10,13 +9,13 @@ import { ContractExplorerPage } from "./ContractExplorerPage"; interface ContractExplorePageProps { contract: ThirdwebContract; chainMetadata: ChainMetadata; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractExplorerPageClient: React.FC = ({ contract, chainMetadata, - twAccount, + isLoggedIn, }) => { const abiQuery = useResolveContractABI(contract); @@ -29,7 +28,7 @@ export const ContractExplorerPageClient: React.FC = ({ abi={abiQuery.data} contract={contract} chainMetadata={chainMetadata} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx index 2c49fbf2e8a..e6ba456c7fd 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/ContractExplorerPage.tsx @@ -1,5 +1,4 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { Abi } from "abitype"; import { getContractFunctionsFromAbi } from "components/contract-components/getContractFunctionsFromAbi"; import { ContractFunctionsOverview } from "components/contract-functions/contract-functions"; @@ -11,14 +10,14 @@ interface ContractExplorePageProps { contract: ThirdwebContract; abi: Abi | undefined; chainMetadata: ChainMetadata; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractExplorerPage: React.FC = ({ contract, abi, chainMetadata, - twAccount, + isLoggedIn, }) => { if (!abi) { return ( @@ -41,7 +40,7 @@ export const ContractExplorerPage: React.FC = ({ onlyFunctions functions={functions} contract={contract} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> ) : (
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx index 5cebb37decc..a7338514a65 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/explorer/page.tsx @@ -28,7 +28,7 @@ export default async function Page(props: { ); } @@ -40,7 +40,7 @@ export default async function Page(props: { contract={contract} abi={abi} chainMetadata={chainMetadata} - twAccount={account} + isLoggedIn={!!account} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx index 90847460123..eb0dcf4bfde 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx @@ -9,7 +9,6 @@ import { isAddress, isContractDeployed } from "thirdweb/utils"; import type { MinimalTeamsAndProjects } from "../../../../../components/contract-components/contract-deploy-form/add-to-project-card"; import { resolveFunctionSelectors } from "../../../../../lib/selectors"; import { shortenIfAddress } from "../../../../../utils/usedapp-external"; -import { getRawAccount } from "../../../../account/settings/getAccount"; import { getAuthToken, getAuthTokenWalletAddress, @@ -48,9 +47,8 @@ export default async function Layout(props: { notFound(); } - const [authToken, account, accountAddress] = await Promise.all([ + const [authToken, accountAddress] = await Promise.all([ getAuthToken(), - getRawAccount(), getAuthTokenWalletAddress(), ]); @@ -118,7 +116,6 @@ The following is the user's message:`; > ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx index fc6b30d1c6c..e8d0ddead5f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx @@ -2,7 +2,6 @@ import { Spinner } from "@/components/ui/Spinner/Spinner"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { UserXIcon } from "lucide-react"; import type { ThirdwebContract } from "thirdweb"; import { getInstalledModules, owner } from "thirdweb/modules"; @@ -12,12 +11,12 @@ import { InstallModuleForm } from "./components/ModuleForm"; interface ContractEditModulesPageProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractEditModulesPage: React.FC< ContractEditModulesPageProps -> = ({ contract, twAccount }) => { +> = ({ contract, isLoggedIn }) => { const account = useActiveAccount(); const installedModulesQuery = useReadContract(getInstalledModules, { @@ -68,7 +67,7 @@ export const ContractEditModulesPage: React.FC<
installedModulesQuery.refetch()} account={account} @@ -100,7 +99,7 @@ export const ContractEditModulesPage: React.FC< refetchModules={() => installedModulesQuery.refetch()} contract={contract} ownerAccount={isOwner ? account : undefined} - twAccount={twAccount} + isLoggedIn={isLoggedIn} />
); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx index 3248cbba442..521b889ee1c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx @@ -16,7 +16,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; import { TransactionButton } from "components/buttons/TransactionButton"; @@ -104,7 +103,7 @@ export function BatchMetadataModuleUI( isOwnerAccount: boolean; uploadMetadata: (values: UploadMetadataFormValues) => Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }, ) { return ( @@ -119,7 +118,7 @@ export function BatchMetadataModuleUI( {props.isOwnerAccount && ( @@ -163,7 +162,7 @@ export function BatchMetadataModuleUI( function UploadMetadataNFTSection(props: { uploadMetadata: (values: UploadMetadataFormValues) => Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(uploadMetadataFormSchema), @@ -262,7 +261,7 @@ function UploadMetadataNFTSection(props: {
@@ -390,7 +389,7 @@ export function ClaimableModuleUI( {props.name !== "ClaimableERC1155" || props.isValidTokenId ? ( props.claimConditionSection.data ? ( {props.primarySaleRecipientSection.data ? ( Promise; isOwnerAccount: boolean; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(primarySaleRecipientFormSchema), @@ -823,7 +822,7 @@ function PrimarySaleRecipientSection(props: {
Promise; name: string; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(mintFormSchema), @@ -947,7 +946,7 @@ function MintNFTSection(props: { isPending={mintMutation.isPending} txChainID={props.contractChainId} transactionCount={1} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} > Mint diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/InstalledModulesTable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/InstalledModulesTable.tsx index 07f5d045501..44818ea9aad 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/InstalledModulesTable.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/InstalledModulesTable.tsx @@ -2,7 +2,6 @@ import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow"; import { Alert, AlertTitle } from "@/components/ui/alert"; -import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi"; import { CircleSlash } from "lucide-react"; import type { ThirdwebContract } from "thirdweb/contract"; import type { Account } from "thirdweb/wallets"; @@ -17,7 +16,7 @@ export const InstalledModulesTable = (props: { }; refetchModules: () => void; ownerAccount: Account | undefined; - twAccount: TWAccount | undefined; + isLoggedIn: boolean; }) => { const { installedModules, ownerAccount } = props; @@ -59,7 +58,7 @@ export const InstalledModulesTable = (props: { contract={props.contract} onRemoveModule={props.refetchModules} ownerAccount={ownerAccount} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} /> ))}
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx index 2b930a1fb31..33015c30e46 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Mintable.tsx @@ -20,7 +20,6 @@ import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; import { Skeleton } from "@/components/ui/skeleton"; import { Textarea } from "@/components/ui/textarea"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; import { TransactionButton } from "components/buttons/TransactionButton"; @@ -192,7 +191,7 @@ export function MintableModuleUI( name: string; isBatchMetadataInstalled: boolean; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }, ) { return ( @@ -211,7 +210,7 @@ export function MintableModuleUI( {props.isOwnerAccount && ( @@ -262,7 +261,7 @@ function PrimarySalesSection(props: { update: (values: UpdateFormValues) => Promise; isOwnerAccount: boolean; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(primarySaleRecipientFormSchema), @@ -312,7 +311,7 @@ function PrimarySalesSection(props: {
({ resolver: zodResolver(mintFormSchema), @@ -524,7 +523,7 @@ function MintNFTSection(props: {
; @@ -388,7 +387,7 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => { {/* Submit */}
Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }, ) { return ( @@ -99,7 +98,7 @@ export function OpenEditionMetadataModuleUI( )} {!props.isOwnerAccount && ( @@ -121,7 +120,7 @@ export function OpenEditionMetadataModuleUI( function SetSharedMetadataSection(props: { setSharedMetadata: (values: SetSharedMetadataFormValues) => Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(setSharedMetadataFormSchema), @@ -219,7 +218,7 @@ function SetSharedMetadataSection(props: { isPending={setSharedMetadataMutation.isPending} transactionCount={1} txChainID={props.contractChainId} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} > Update diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Royalty.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Royalty.tsx index ac7c28c9db9..ef5a031a434 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Royalty.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Royalty.tsx @@ -16,7 +16,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; import { TransactionButton } from "components/buttons/TransactionButton"; @@ -167,7 +166,7 @@ export function RoyaltyModuleUI( ) => Promise; setRoyaltyInfoForToken: (values: RoyaltyInfoFormValues) => Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }, ) { return ( @@ -186,7 +185,7 @@ export function RoyaltyModuleUI( ) : ( @@ -209,7 +208,7 @@ export function RoyaltyModuleUI( update={props.setDefaultRoyaltyInfo} defaultRoyaltyInfo={props.defaultRoyaltyInfo} contractChainId={props.contractChainId} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} /> ) : ( @@ -232,7 +231,7 @@ export function RoyaltyModuleUI( update={props.setTransferValidator} transferValidator={props.transferValidator} contractChainId={props.contractChainId} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} /> ) : ( @@ -269,7 +268,7 @@ export type RoyaltyInfoFormValues = z.infer; function RoyaltyInfoPerTokenSection(props: { setRoyaltyInfoForToken: (values: RoyaltyInfoFormValues) => Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(royaltyInfoFormSchema), @@ -349,7 +348,7 @@ function RoyaltyInfoPerTokenSection(props: {
Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const [defaultRoyaltyRecipient, defaultRoyaltyPercentage] = props.defaultRoyaltyInfo || []; @@ -460,7 +459,7 @@ function DefaultRoyaltyInfoSection(props: { transactionCount={1} isPending={updateMutation.isPending} txChainID={props.contractChainId} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} > Update @@ -483,7 +482,7 @@ function TransferValidatorSection(props: { transferValidator: string | undefined; update: (values: TransferValidatorFormValues) => Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const form = useForm({ resolver: zodResolver(transferValidatorFormSchema), @@ -531,7 +530,7 @@ function TransferValidatorSection(props: {
Promise; contractChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }, ) { const form = useForm({ @@ -200,7 +199,7 @@ export function TransferableModuleUI( (props.isRestricted ? 0 : 1) + allowListLength } txChainID={props.contractChainId} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} > Update diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/batchMetadata.stories.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/batchMetadata.stories.tsx index c678e49d0fc..546fa983002 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/batchMetadata.stories.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/batchMetadata.stories.tsx @@ -8,7 +8,6 @@ import { BadgeContainer } from "stories/utils"; import { ZERO_ADDRESS } from "thirdweb"; import { ConnectButton, ThirdwebProvider } from "thirdweb/react"; import type { TransactionError } from "../../../../../../../contexts/error-handler"; -import { accountStub } from "../../../../../../../stories/stubs"; import { BatchMetadataModuleUI, type UploadMetadataFormValues, @@ -85,7 +84,7 @@ function Component() {
@@ -110,7 +107,7 @@ function Component() {
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/royalty.stories.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/royalty.stories.tsx index d2080aabef5..83b0f3769cb 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/royalty.stories.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/royalty.stories.tsx @@ -6,7 +6,6 @@ import { useState } from "react"; import { toast } from "sonner"; import { BadgeContainer } from "stories/utils"; import { ConnectButton, ThirdwebProvider } from "thirdweb/react"; -import { accountStub } from "../../../../../../../stories/stubs"; import { type DefaultRoyaltyFormValues, type RoyaltyInfoFormValues, @@ -67,8 +66,6 @@ function Component() { version: "1.0.0", }; - const twAccount = accountStub(); - return (
@@ -99,7 +96,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={twAccount} + isLoggedIn={true} /> @@ -118,7 +115,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={twAccount} + isLoggedIn={true} /> @@ -137,7 +134,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={twAccount} + isLoggedIn={true} /> @@ -157,7 +154,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={twAccount} + isLoggedIn={true} />
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/transferable.stories.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/transferable.stories.tsx index 2512b20310b..50d57fa3afd 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/transferable.stories.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/transferable.stories.tsx @@ -6,7 +6,6 @@ import { useState } from "react"; import { toast } from "sonner"; import { BadgeContainer } from "stories/utils"; import { ConnectButton, ThirdwebProvider } from "thirdweb/react"; -import { accountStub } from "../../../../../../../stories/stubs"; import { type TransferableModuleFormValues, TransferableModuleUI, @@ -92,7 +91,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={accountStub()} + isLoggedIn={true} />
@@ -110,7 +109,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={accountStub()} + isLoggedIn={true} /> @@ -128,7 +127,7 @@ function Component() { }} isOwnerAccount={isOwner} contractChainId={1} - twAccount={accountStub()} + isLoggedIn={true} />
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx index f02dd3365d4..8a4bf0fbe66 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/page.tsx @@ -25,7 +25,10 @@ export default async function Page(props: { if (contract.chain.id === localhost.id) { return ( - + ); } @@ -35,5 +38,5 @@ export default async function Page(props: { redirect(`/${params.chain_id}/${params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx index 261a873a35e..0bc76e7e921 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; @@ -9,7 +8,7 @@ import { ContractNFTPage } from "./ContractNFTPage"; export function ContractNFTPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -32,7 +31,7 @@ export function ContractNFTPageClient(props: { contract={props.contract} isErc721={supportedERCs.isERC721} functionSelectors={functionSelectors} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx index 53585231a54..769f258a0a0 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/ContractNFTPage.tsx @@ -1,4 +1,3 @@ -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import * as ERC721Ext from "thirdweb/extensions/erc721"; import * as ERC1155Ext from "thirdweb/extensions/erc1155"; @@ -16,14 +15,14 @@ interface NftOverviewPageProps { isErc721: boolean; tokenId?: string; functionSelectors: string[]; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractNFTPage: React.FC = ({ contract, isErc721, functionSelectors, - twAccount, + isLoggedIn, }) => { const isERC721ClaimToSupported = ERC721Ext.isClaimToSupported(functionSelectors); @@ -63,38 +62,38 @@ export const ContractNFTPage: React.FC = ({

NFTs

{isRevealable && ( - + )} {isERC721ClaimToSupported && ( /** * This button is used for claiming NFT Drop contract (erc721) only! * For Edition Drop we have a dedicated ClaimTabERC1155 inside each Edition's page */ - + )} {isMintToSupported && ( )} {isSetSharedMetadataSupported && ( )} {isLazyMintable && ( )} {isLazyMintable && ( ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx index dd336d4f168..d60c1fda13e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx @@ -9,7 +9,6 @@ import { SheetTrigger, } from "@/components/ui/sheet"; import { cn } from "@/lib/utils"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; import { useTxNotifications } from "hooks/useTxNotifications"; @@ -29,7 +28,7 @@ import { interface AirdropTabProps { contract: ThirdwebContract; tokenId: string; - twAccount: Account | undefined; + isLoggedIn: boolean; } /** @@ -38,7 +37,7 @@ interface AirdropTabProps { const AirdropTab: React.FC = ({ contract, tokenId, - twAccount, + isLoggedIn, }) => { const address = useActiveAccount()?.address; const { handleSubmit, setValue, watch, reset } = useForm<{ @@ -163,7 +162,7 @@ const AirdropTab: React.FC = ({ more, please do it in multiple transactions.

= ({ contract, tokenId, twAccount }) => { +const BurnTab: React.FC = ({ contract, tokenId, isLoggedIn }) => { const account = useActiveAccount(); const trackEvent = useTrack(); const { @@ -137,7 +136,7 @@ const BurnTab: React.FC = ({ contract, tokenId, twAccount }) => { type="submit" className="self-end" txChainID={contract.chain.id} - twAccount={twAccount} + isLoggedIn={isLoggedIn} > Burn diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx index 6e37d7e5249..03c4c73e172 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Flex, FormControl, Input } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; @@ -15,13 +14,13 @@ import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components"; interface ClaimTabProps { contract: ThirdwebContract; tokenId: string; - twAccount: Account | undefined; + isLoggedIn: boolean; } const ClaimTabERC1155: React.FC = ({ contract, tokenId, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -125,12 +124,12 @@ const ClaimTabERC1155: React.FC = ({ Claim diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx index 1cbe0f6d256..3092391952f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx @@ -10,7 +10,6 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { zodResolver } from "@hookform/resolvers/zod"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; @@ -24,7 +23,7 @@ import { z } from "zod"; interface MintSupplyTabProps { contract: ThirdwebContract; tokenId: string; - twAccount: Account | undefined; + isLoggedIn: boolean; } const mintAdditionalSupplyFormSchema = z.object({ @@ -37,7 +36,7 @@ const mintAdditionalSupplyFormSchema = z.object({ const MintSupplyTab: React.FC = ({ contract, tokenId, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -132,7 +131,7 @@ const MintSupplyTab: React.FC = ({ isPending={sendAndConfirmTx.isPending} type="submit" className="self-end" - twAccount={twAccount} + isLoggedIn={isLoggedIn} > Mint diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx index 76f9ff7365f..d0ebf124c32 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { FormControl, Input } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import { SolidityInput } from "contract-ui/components/solidity-inputs"; @@ -20,13 +19,13 @@ import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components"; interface TransferTabProps { contract: ThirdwebContract; tokenId: string; - twAccount: Account | undefined; + isLoggedIn: boolean; } const TransferTab: React.FC = ({ contract, tokenId, - twAccount, + isLoggedIn, }) => { const account = useActiveAccount(); @@ -126,7 +125,7 @@ const TransferTab: React.FC = ({ )}
use `setTokenURI` */ useUpdateMetadata: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; }; export const UpdateNftMetadata: React.FC = ({ @@ -62,7 +61,7 @@ export const UpdateNftMetadata: React.FC = ({ nft, useUpdateMetadata, setOpen, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -408,7 +407,7 @@ export const UpdateNftMetadata: React.FC = ({ Cancel use `setTokenURI` */ useUpdateMetadata: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } const UpdateMetadataTab: React.FC = ({ contract, nft, useUpdateMetadata, - twAccount, + isLoggedIn, }) => { const [open, setOpen] = useState(false); @@ -40,7 +39,7 @@ const UpdateMetadataTab: React.FC = ({ Update NFT Metadata ); } @@ -48,7 +48,7 @@ export default async function Page(props: { contract={contract} isErc721={supportedERCs.isERC721} tokenId={params.tokenId} - twAccount={account} + isLoggedIn={!!account} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx index f04543bfdd7..0aa4efe8827 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/token-id.tsx @@ -19,7 +19,6 @@ import { ToolTipLabel } from "@/components/ui/tooltip"; import { useThirdwebClient } from "@/constants/thirdweb.client"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useChainSlug } from "hooks/chains/chainSlug"; import { ExternalLinkIcon } from "lucide-react"; import Link from "next/link"; @@ -52,7 +51,7 @@ interface TokenIdPageProps { tokenId: string; contract: ThirdwebContract; isErc721: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } // TODO: verify the entire nft object with zod schema and display an error message @@ -61,7 +60,7 @@ export const TokenIdPage: React.FC = ({ contract, tokenId, isErc721, - twAccount, + isLoggedIn, }) => { const [tab, setTab] = useState("Details"); const router = useDashboardRouter(); @@ -71,7 +70,7 @@ export const TokenIdPage: React.FC = ({ const tabs = useNFTDrawerTabs({ contract, tokenId, - twAccount, + isLoggedIn, }); const client = useThirdwebClient(); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/useNftDrawerTabs.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/useNftDrawerTabs.tsx index 96c4c024940..1b8883b8f7e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/useNftDrawerTabs.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/useNftDrawerTabs.tsx @@ -1,4 +1,3 @@ -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useIsMinter } from "@3rdweb-sdk/react/hooks/useContractRoles"; import { useContractFunctionSelectors } from "contract-ui/hooks/useContractFunctionSelectors"; import dynamic from "next/dynamic"; @@ -12,7 +11,7 @@ import type { NFTDrawerTab } from "./types"; type UseNFTDrawerTabsParams = { contract: ThirdwebContract; tokenId: string; - twAccount: Account | undefined; + isLoggedIn: boolean; }; const TransferTab = dynamic(() => import("./components/transfer-tab")); @@ -32,7 +31,7 @@ const UpdateMetadataTab = dynamic( export function useNFTDrawerTabs({ contract, tokenId, - twAccount, + isLoggedIn, }: UseNFTDrawerTabsParams): NFTDrawerTab[] { const functionSelectorQuery = useContractFunctionSelectors(contract); const functionSelectors = functionSelectorQuery.data || []; @@ -132,7 +131,7 @@ export function useNFTDrawerTabs({ contract={contract} tokenId={tokenId} isColumn - twAccount={twAccount} + isLoggedIn={isLoggedIn} isMultiphase={isERC1155Multiphase} /> ), @@ -151,7 +150,7 @@ export function useNFTDrawerTabs({ ), }, @@ -167,7 +166,7 @@ export function useNFTDrawerTabs({ ), }, @@ -183,7 +182,7 @@ export function useNFTDrawerTabs({ ), }, @@ -199,7 +198,7 @@ export function useNFTDrawerTabs({ ), }, @@ -214,7 +213,7 @@ export function useNFTDrawerTabs({ ), }, @@ -232,7 +231,7 @@ export function useNFTDrawerTabs({ contract={contract} nft={nft} useUpdateMetadata={supportsUpdateMetadata} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> ), }, @@ -250,6 +249,6 @@ export function useNFTDrawerTabs({ isMinterRole, contract, functionSelectors, - twAccount, + isLoggedIn, ]); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx index bf5e9c1f943..606739e40ab 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/batch-lazy-mint-button.tsx @@ -9,7 +9,6 @@ import { SheetTrigger, } from "@/components/ui/sheet"; import { MinterOnly } from "@3rdweb-sdk/react/components/roles/minter-only"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { BatchLazyMint } from "core-ui/batch-upload/batch-lazy-mint"; import { useTrack } from "hooks/analytics/useTrack"; import { useTxNotifications } from "hooks/useTxNotifications"; @@ -24,14 +23,14 @@ interface BatchLazyMintButtonProps { canCreateDelayedRevealBatch: boolean; contract: ThirdwebContract; isErc721: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const BatchLazyMintButton: React.FC = ({ contract, canCreateDelayedRevealBatch, isErc721, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const [open, setOpen] = useState(false); @@ -68,7 +67,7 @@ export const BatchLazyMintButton: React.FC = ({ Upload NFTs { // nice, we can set up everything the same for both the only thing that changes is the action string diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx index 843f184be6a..37022762dc4 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx @@ -8,7 +8,6 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { FormControl, Input } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; @@ -27,7 +26,7 @@ const CLAIM_FORM_ID = "nft-claim-form"; interface NFTClaimButtonProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } /** @@ -36,7 +35,7 @@ interface NFTClaimButtonProps { */ export const NFTClaimButton: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -108,7 +107,7 @@ export const NFTClaimButton: React.FC = ({
= ({ contract, isErc721, - twAccount, + isLoggedIn, ...restButtonProps }) => { const [open, setOpen] = useState(false); @@ -44,7 +43,7 @@ export const NFTLazyMintButton: React.FC = ({ contract={contract} isErc721={isErc721} setOpen={setOpen} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx index 983431c038c..e51be2b7155 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx @@ -1,7 +1,6 @@ "use client"; import { Button } from "@/components/ui/button"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Accordion, AccordionButton, @@ -42,14 +41,14 @@ type LazyMintNftFormParams = { contract: ThirdwebContract; isErc721: boolean; setOpen: Dispatch>; - twAccount: Account | undefined; + isLoggedIn: boolean; }; export const LazyMintNftForm: React.FC = ({ contract, isErc721, setOpen, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -342,7 +341,7 @@ export const LazyMintNftForm: React.FC = ({ Cancel = ({ contract, isErc721, - twAccount, + isLoggedIn, ...restButtonProps }) => { const [open, setOpen] = useState(false); @@ -46,7 +45,7 @@ export const NFTMintButton: React.FC = ({ contract={contract} isErc721={isErc721} setOpen={setOpen} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx index 720f18143e9..ccd85276f44 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Accordion, AccordionButton, @@ -42,14 +41,14 @@ type NFTMintForm = { contract: ThirdwebContract; isErc721: boolean; setOpen: Dispatch>; - twAccount: Account | undefined; + isLoggedIn: boolean; }; export const NFTMintForm: React.FC = ({ contract, isErc721, setOpen, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -371,7 +370,7 @@ export const NFTMintForm: React.FC = ({ form={MINT_FORM_ID} type="submit" disabled={!isDirty} - twAccount={twAccount} + isLoggedIn={isLoggedIn} > Mint NFT diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx index 92f07a607cc..89fa82d7a3f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx @@ -10,7 +10,6 @@ import { } from "@/components/ui/sheet"; import { ToolTipLabel } from "@/components/ui/tooltip"; import { MinterOnly } from "@3rdweb-sdk/react/components/roles/minter-only"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { FormControl, Input, Select } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; @@ -25,14 +24,14 @@ import { FormErrorMessage, FormLabel } from "tw-components"; interface NFTRevealButtonProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } const REVEAL_FORM_ID = "reveal-form"; export const NFTRevealButton: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const batchesQuery = useReadContract(getBatchesToReveal, { contract, @@ -154,7 +153,7 @@ export const NFTRevealButton: React.FC = ({
= ({ contract, twAccount, ...restButtonProps }) => { +> = ({ contract, isLoggedIn, ...restButtonProps }) => { const [open, setOpen] = useState(false); return ( @@ -38,7 +37,7 @@ export const NFTSharedMetadataButton: React.FC< diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx index 43ed8339d0f..510320a113b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Accordion, AccordionButton, @@ -38,8 +37,8 @@ const SHARED_METADATA_FORM_ID = "shared-metadata-form"; export const SharedMetadataForm: React.FC<{ contract: ThirdwebContract; setOpen: Dispatch>; - twAccount: Account | undefined; -}> = ({ contract, setOpen, twAccount }) => { + isLoggedIn: boolean; +}> = ({ contract, setOpen, isLoggedIn }) => { const trackEvent = useTrack(); const address = useActiveAccount()?.address; const sendAndConfirmTx = useSendAndConfirmTransaction(); @@ -297,7 +296,7 @@ export const SharedMetadataForm: React.FC<{ form={SHARED_METADATA_FORM_ID} type="submit" disabled={!isDirty} - twAccount={twAccount} + isLoggedIn={isLoggedIn} > Set NFT Metadata diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/page.tsx index 22600aa5200..ffe7e5ffaa8 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/page.tsx @@ -23,7 +23,7 @@ export default async function Page(props: { const { contract } = info; if (contract.chain.id === localhost.id) { - return ; + return ; } const { supportedERCs, functionSelectors } = @@ -38,7 +38,7 @@ export default async function Page(props: { contract={contract} isErc721={supportedERCs.isERC721} functionSelectors={functionSelectors} - twAccount={account} + isLoggedIn={!!account} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx index cf7a2b54e3e..11feb7a7b9d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/ContractPermissionsPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import type { ChainMetadata } from "thirdweb/chains"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; @@ -10,7 +9,7 @@ import { ContractPermissionsPage } from "./ContractPermissionsPage"; export function ContractPermissionsPageClient(props: { contract: ThirdwebContract; chainMetadata: ChainMetadata; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -26,7 +25,7 @@ export function ContractPermissionsPageClient(props: { = ({ contract, detectedPermissionEnumerable, chainSlug, twAccount }) => { +> = ({ contract, detectedPermissionEnumerable, chainSlug, isLoggedIn }) => { useIsomorphicLayoutEffect(() => { window?.scrollTo({ top: 0, behavior: "smooth" }); }, []); @@ -61,7 +60,7 @@ export const ContractPermissionsPage: React.FC< return ( - + ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx index db9cbbb7609..f89c0eec013 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/permissions/components/index.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { ButtonGroup, Flex } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import { ROLE_DESCRIPTION_MAP } from "constants/mappings"; @@ -27,10 +26,10 @@ type PermissionFormContext = { export function Permissions({ contract, - twAccount, + isLoggedIn, }: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const trackEvent = useTrack(); const account = useActiveAccount(); @@ -127,7 +126,7 @@ export function Permissions({ Reset ); } @@ -40,7 +40,7 @@ export default async function Page(props: { contract={contract} chainSlug={info.chainMetadata.slug} detectedPermissionEnumerable={isPermissionsEnumerableSupported} - twAccount={account} + isLoggedIn={!!account} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx index 8405c5c3eec..95dae159ff8 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; @@ -9,7 +8,7 @@ import { ContractProposalsPage } from "./ContractProposalsPage"; export function ContractProposalsPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -28,7 +27,7 @@ export function ContractProposalsPageClient(props: { return ( ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.tsx index 53622d3b68d..a73537b1c4d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/ContractProposalsPage.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { voteTokenBalances } from "@3rdweb-sdk/react/hooks/useVote"; import { Divider, Flex, Stat, StatLabel, StatNumber } from "@chakra-ui/react"; import { useMemo } from "react"; @@ -14,12 +13,12 @@ import { ProposalButton } from "./components/proposal-button"; interface ProposalsPageProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractProposalsPage: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const address = useActiveAccount()?.address; const proposalsQuery = useReadContract(VoteExt.getAll, { @@ -45,8 +44,8 @@ export const ContractProposalsPage: React.FC = ({
Proposals
- - + +
@@ -55,7 +54,7 @@ export const ContractProposalsPage: React.FC = ({ key={proposal.proposalId.toString()} contract={contract} proposal={proposal} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> ))} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx index f17a45f33df..a1b46fa46e0 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx @@ -1,7 +1,6 @@ "use client"; import { ToolTipLabel } from "@/components/ui/tooltip"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { tokensDelegated, useDelegateMutation, @@ -14,12 +13,12 @@ import { useActiveAccount, useReadContract } from "thirdweb/react"; interface VoteButtonProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const DelegateButton: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const trackEvent = useTrack(); const account = useActiveAccount(); @@ -39,7 +38,7 @@ export const DelegateButton: React.FC = ({ return ( { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx index 4792e9dd22c..d63f00a58a7 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/proposal-button.tsx @@ -7,7 +7,6 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { FormControl, Textarea } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; @@ -22,14 +21,14 @@ import { Button, FormErrorMessage, FormLabel } from "tw-components"; interface VoteButtonProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } const PROPOSAL_FORM_ID = "proposal-form-id"; export const ProposalButton: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const [open, setOpen] = useState(false); const sendTx = useSendAndConfirmTransaction(); @@ -107,7 +106,7 @@ export const ProposalButton: React.FC = ({ Cancel = ({ proposal, contract, - twAccount, + isLoggedIn, }) => { const account = useActiveAccount(); const hasVotedQuery = useReadContract(VoteExt.hasVoted, { @@ -138,7 +137,7 @@ export const Proposal: React.FC = ({ tokensDelegatedQuery.data ? (
= ({
= ({
+ ); } @@ -33,5 +33,5 @@ export default async function Page(props: { redirect(`/${params.chain_id}/${params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx index a535a0eb75e..2acb8a54f12 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; import { useContractPageMetadata } from "../_hooks/useContractPageMetadata"; @@ -8,7 +7,7 @@ import { ContractSettingsPage } from "./ContractSettingsPage"; export function ContractSettingsPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -24,7 +23,7 @@ export function ContractSettingsPageClient(props: { ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx index 1253b9bcc3e..1cf47685d4e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/ContractSettingsPage.tsx @@ -1,5 +1,4 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Flex, GridItem, SimpleGrid } from "@chakra-ui/react"; import type { ThirdwebContract } from "thirdweb"; import * as CommonExt from "thirdweb/extensions/common"; @@ -14,7 +13,7 @@ interface ContractSettingsPageProps { isPrimarySaleSupported: boolean; isRoyaltiesSupported: boolean; isPlatformFeesSupported: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } const ContractSettingsPageInner: React.FC = ({ @@ -23,7 +22,7 @@ const ContractSettingsPageInner: React.FC = ({ isPrimarySaleSupported, isRoyaltiesSupported, isPlatformFeesSupported, - twAccount, + isLoggedIn, }) => { return ( @@ -33,7 +32,7 @@ const ContractSettingsPageInner: React.FC = ({ = ({ )} @@ -55,7 +54,7 @@ const ContractSettingsPageInner: React.FC = ({ )} @@ -65,7 +64,7 @@ const ContractSettingsPageInner: React.FC = ({ )} @@ -78,10 +77,11 @@ const ContractSettingsPageInner: React.FC = ({ export function ContractSettingsPage(props: { contract: ThirdwebContract; functionSelectors: string[]; - twAccount: Account | undefined; + isLoggedIn: boolean; hasDefaultFeeConfig: boolean; }) { - const { functionSelectors, contract, twAccount, hasDefaultFeeConfig } = props; + const { functionSelectors, contract, isLoggedIn, hasDefaultFeeConfig } = + props; return ( ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx index 1e63e224fb5..71bf43990a9 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/components/metadata.tsx @@ -1,7 +1,6 @@ "use client"; import { AdminOnly } from "@3rdweb-sdk/react/components/roles/admin-only"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Flex, FormControl, @@ -66,11 +65,11 @@ const SocialUrlSchema = z.record(z.string(), z.string()); export const SettingsMetadata = ({ contract, detectedState, - twAccount, + isLoggedIn, }: { contract: ThirdwebContract; detectedState: ExtensionDetectedState; - twAccount: Account | undefined; + isLoggedIn: boolean; }) => { const trackEvent = useTrack(); const metadata = useReadContract(getContractMetadata, { contract }); @@ -331,7 +330,7 @@ export const SettingsMetadata = ({ { const trackEvent = useTrack(); const address = useActiveAccount()?.address; @@ -176,7 +175,7 @@ export const SettingsPlatformFees = ({ { const address = useActiveAccount()?.address; const trackEvent = useTrack(); @@ -144,7 +143,7 @@ export const SettingsPrimarySale = ({ { const trackEvent = useTrack(); const query = useReadContract(getDefaultRoyaltyInfo, { @@ -189,7 +188,7 @@ export const SettingsRoyalties = ({ type="submit" isPending={mutation.isPending} className="!rounded-t-none rounded-xl" - twAccount={twAccount} + isLoggedIn={isLoggedIn} > {mutation.isPending ? "Updating Royalty Settings" diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx index 1d01580c8dc..0219f342673 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/settings/page.tsx @@ -26,7 +26,7 @@ export default async function Page(props: { if (contract.chain.id === localhost.id) { const account = await getRawAccount(); return ( - + ); } @@ -46,7 +46,7 @@ export default async function Page(props: { ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx index 53b7c8be423..52ad418fbea 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.client.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { ErrorPage, LoadingPage } from "../_components/page-skeletons"; import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client"; @@ -9,7 +8,7 @@ import { ContractSplitPage } from "./ContractSplitPage"; export function ContractSplitPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -26,6 +25,9 @@ export function ContractSplitPageClient(props: { } return ( - + ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx index 99bb0ed76e9..48886eb96fa 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useSplitBalances } from "@3rdweb-sdk/react/hooks/useSplit"; import { Flex, @@ -38,12 +37,12 @@ export type Balance = { interface SplitPageProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractSplitPage: React.FC = ({ contract, - twAccount, + isLoggedIn, }) => { const address = useActiveAccount()?.address; const { idToChain } = useAllChainsData(); @@ -115,7 +114,7 @@ export const ContractSplitPage: React.FC = ({ } balancesIsError={balanceQuery.isError && nativeBalanceQuery.isError} contract={contract} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx index e837ea79dca..7b6b44f4e51 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx @@ -1,7 +1,6 @@ "use client"; import { Button } from "@/components/ui/button"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useSplitDistributeFunds } from "@3rdweb-sdk/react/hooks/useSplit"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; @@ -15,7 +14,7 @@ interface DistributeButtonProps { balances: Balance[]; balancesIsPending: boolean; balancesIsError: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const DistributeButton: React.FC = ({ @@ -23,7 +22,7 @@ export const DistributeButton: React.FC = ({ balances, balancesIsPending, balancesIsError, - twAccount, + isLoggedIn, ...restButtonProps }) => { const trackEvent = useTrack(); @@ -77,7 +76,7 @@ export const DistributeButton: React.FC = ({ if (balancesIsError) { return ( = ({ return ( + ); } @@ -34,5 +34,5 @@ export default async function Page(props: { redirect(`/${params.chain_id}/${params.contractAddress}`); } - return ; + return ; } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx index 7f55f7f196c..f7fc95cd489 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.client.tsx @@ -1,6 +1,4 @@ "use client"; - -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { isClaimToSupported, @@ -12,7 +10,7 @@ import { ContractTokensPage } from "./ContractTokensPage"; export function ContractTokensPageClient(props: { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; }) { const metadataQuery = useContractPageMetadata(props.contract); @@ -32,7 +30,7 @@ export function ContractTokensPageClient(props: { isERC20={supportedERCs.isERC20} isMintToSupported={isMintToSupported(functionSelectors)} isClaimToSupported={isClaimToSupported(functionSelectors)} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx index d7d6b09eaf9..4e2fa0c4215 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/ContractTokensPage.tsx @@ -1,5 +1,4 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { ThirdwebContract } from "thirdweb"; import { Card, Heading, LinkButton, Text } from "tw-components"; import { TokenAirdropButton } from "./components/airdrop-button"; @@ -14,7 +13,7 @@ interface ContractTokenPageProps { isERC20: boolean; isMintToSupported: boolean; isClaimToSupported: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractTokensPage: React.FC = ({ @@ -22,7 +21,7 @@ export const ContractTokensPage: React.FC = ({ isERC20, isMintToSupported, isClaimToSupported, - twAccount, + isLoggedIn, }) => { if (!isERC20) { return ( @@ -52,13 +51,13 @@ export const ContractTokensPage: React.FC = ({

Tokens

{isClaimToSupported && ( - + )} - - - + + + {isMintToSupported && ( - + )}
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-button.tsx index d11f691d789..f3dc088d3a6 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-button.tsx @@ -8,7 +8,6 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { DropletIcon } from "lucide-react"; import { useState } from "react"; import type { ThirdwebContract } from "thirdweb"; @@ -18,12 +17,12 @@ import { TokenAirdropForm } from "./airdrop-form"; interface TokenAirdropButtonProps { contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const TokenAirdropButton: React.FC = ({ contract, - twAccount, + isLoggedIn, ...restButtonProps }) => { const address = useActiveAccount()?.address; @@ -50,11 +49,7 @@ export const TokenAirdropButton: React.FC = ({ Airdrop tokens - + ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx index 1216c4ae6c2..861938914a7 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-form.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { TransactionButton } from "components/buttons/TransactionButton"; import { useTrack } from "hooks/analytics/useTrack"; import { useTxNotifications } from "hooks/useTxNotifications"; @@ -16,14 +15,14 @@ import { type AirdropAddressInput, AirdropUpload } from "./airdrop-upload"; interface TokenAirdropFormProps { contract: ThirdwebContract; toggle?: Dispatch>; - twAccount: Account | undefined; + isLoggedIn: boolean; } const GAS_COST_PER_ERC20_TRANSFER = 21000; export const TokenAirdropForm: React.FC = ({ contract, toggle, - twAccount, + isLoggedIn, }) => { const { handleSubmit, setValue, watch } = useForm<{ addresses: AirdropAddressInput[]; @@ -136,7 +135,7 @@ export const TokenAirdropForm: React.FC = ({ )} = ({ contract, - twAccount, + isLoggedIn, ...restButtonProps }) => { const address = useActiveAccount()?.address; @@ -98,7 +97,7 @@ export const TokenBurnButton: React.FC = ({ = ({ contract, - twAccount, + isLoggedIn, ...restButtonProps }) => { const [open, setOpen] = useState(false); @@ -93,7 +92,7 @@ export const TokenClaimButton: React.FC = ({ = ({ contract, - account, + isLoggedIn, ...restButtonProps }) => { const [open, setOpen] = useState(false); @@ -125,7 +124,7 @@ export const TokenMintButton: React.FC = ({ = ({ contract, - twAccount, + isLoggedIn, ...restButtonProps }) => { const address = useActiveAccount()?.address; @@ -103,7 +102,7 @@ export const TokenTransferButton: React.FC = ({ + ); } @@ -41,7 +44,7 @@ export default async function Page(props: { isERC20={supportedERCs.isERC20} isMintToSupported={isMintToSupported(functionSelectors)} isClaimToSupported={isClaimToSupported(functionSelectors)} - twAccount={account} + isLoggedIn={!!account} /> ); } diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx index 8d906c2ad24..9f7fa2820ad 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx @@ -85,7 +85,7 @@ export default async function PublishedContractPage( diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx index f437907c6b1..61cc382ddeb 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx @@ -54,7 +54,7 @@ export default async function PublishedContractPage(
diff --git a/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginClient.tsx b/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginClient.tsx index 88356d65bc9..d158555c1de 100644 --- a/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginClient.tsx +++ b/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginClient.tsx @@ -1,55 +1,32 @@ "use client"; -import { isWalletValidForActiveAccount } from "@/actions/validLogin"; import { useDashboardRouter } from "@/lib/DashboardRouter"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; -import { useQuery } from "@tanstack/react-query"; +import { useEffect } from "react"; import { useActiveAccount } from "thirdweb/react"; export function EnsureValidConnectedWalletLoginClient(props: { - account: Account; - authToken: string; + loggedInAddress: string; }) { const router = useDashboardRouter(); const connectedAddress = useActiveAccount()?.address; - useQuery({ - queryKey: [ - "EnsureValidLogin", - connectedAddress, - props.account, - props.authToken, - { persist: false }, - ], - retry: false, - enabled: !!connectedAddress, - queryFn: async () => { - if (!connectedAddress) { - throw new Error("No connected address"); - } - - const isValidLogin = await isWalletValidForActiveAccount({ - address: connectedAddress, - account: props.account, - authToken: props.authToken, - }); - - // directly doing side-effects in query instead of useEffect - // to avoid doing this multiple times on re-renders - if (!isValidLogin) { - const currentHref = new URL(window.location.href); - const currentPathname = currentHref.pathname; - const currentSearchParams = currentHref.searchParams.toString(); - router.replace( - buildLoginPath( - `${currentPathname}${currentSearchParams ? `?${currentSearchParams}` : ""}`, - ), - ); - } - - return isValidLogin; - }, - }); + // eslint-disable-next-line no-restricted-syntax + useEffect(() => { + if (!connectedAddress) { + return; + } + + if (connectedAddress !== props.loggedInAddress) { + const currentHref = new URL(window.location.href); + const currentPathname = currentHref.pathname; + const currentSearchParams = currentHref.searchParams.toString(); + router.replace( + buildLoginPath( + `${currentPathname}${currentSearchParams ? `?${currentSearchParams}` : ""}`, + ), + ); + } + }, [connectedAddress, props.loggedInAddress, router]); return null; } diff --git a/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer.tsx b/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer.tsx index df42b2cc4a7..81ed76bbbe1 100644 --- a/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer.tsx +++ b/apps/dashboard/src/app/components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer.tsx @@ -1,24 +1,14 @@ -import { getRawAccount } from "../../account/settings/getAccount"; -import { getAuthToken } from "../../api/lib/getAuthToken"; +import { getAuthTokenWalletAddress } from "../../api/lib/getAuthToken"; import { EnsureValidConnectedWalletLoginClient } from "./EnsureValidConnectedWalletLoginClient"; -// when the user is connected with a wallet and logged in -// ensure that the wallet is a valid wallet for the active account -// if not, redirect to login page +// ensure that address in backend matches connected wallet address +// if there's a mismatch - redirect to login page export async function EnsureValidConnectedWalletLoginServer() { - const [account, accountAuthToken] = await Promise.all([ - getRawAccount(), - getAuthToken(), - ]); + const address = await getAuthTokenWalletAddress(); - if (account && accountAuthToken) { - return ( - - ); + if (address) { + return ; } return null; diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index 6a99c541bd2..aa60d74bb8f 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -5,13 +5,9 @@ import type { Metadata } from "next"; import PlausibleProvider from "next-plausible"; import { Inter } from "next/font/google"; import NextTopLoader from "nextjs-toploader"; -import { Suspense } from "react"; -import { OpCreditsGrantedModalWrapperServer } from "../components/onboarding/OpCreditsGrantedModalWrapperServer"; -import { PosthogIdentifierServer } from "../components/wallets/PosthogIdentifierServer"; import { PHProvider } from "../lib/posthog/Posthog"; import { PosthogHeadSetup } from "../lib/posthog/PosthogHeadSetup"; import { PostHogPageView } from "../lib/posthog/PosthogPageView"; -import { EnsureValidConnectedWalletLoginServer } from "./components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginServer"; import { AppRouterProviders } from "./providers"; const fontSans = Inter({ @@ -75,18 +71,7 @@ export default function RootLayout({ fontSans.variable, )} > - - {children} - - - - - - - - - - + {children} { - let ip: string | null = null; - const errors: string[] = []; - try { - ip = requestHeaders.get("CF-Connecting-IP") || null; - } catch (err) { - console.error("failed to get IP address from CF-Connecting-IP", err); - errors.push("failed to get IP address from CF-Connecting-IP"); - } - if (!ip) { - try { - ip = ipAddress(requestHeaders) || null; - } catch (err) { - console.error( - "failed to get IP address from ipAddress() function", - err, - ); - errors.push("failed to get IP address from ipAddress() function"); - } - } - if (!ip) { - try { - ip = requestHeaders.get("X-Forwarded-For"); - } catch (err) { - console.error("failed to get IP address from X-Forwarded-For", err); - errors.push("failed to get IP address from X-Forwarded-For"); - } - } - return [ip, errors]; - })(); - - if (!ip) { - return { - error: "Could not get IP address. Please try again.", - context: errors, - }; - } - - // https://developers.cloudflare.com/turnstile/get-started/server-side-validation/ - // Validate the token by calling the "/siteverify" API endpoint. - const result = await fetch( - "https://challenges.cloudflare.com/turnstile/v0/siteverify", - { - body: JSON.stringify({ - secret: process.env.TURNSTILE_SECRET_KEY, - response: turnstileToken, - remoteip: ip, - }), - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }, - ); - - const outcome = await result.json(); - if (!outcome.success) { + const result = await verifyTurnstileToken(turnstileToken); + if (!result.success) { return { error: "Invalid captcha. Please try again.", + context: result.context, }; } } diff --git a/apps/dashboard/src/app/login/verifyTurnstileToken.ts b/apps/dashboard/src/app/login/verifyTurnstileToken.ts new file mode 100644 index 00000000000..dd080a5bcd5 --- /dev/null +++ b/apps/dashboard/src/app/login/verifyTurnstileToken.ts @@ -0,0 +1,70 @@ +import "server-only"; +import { ipAddress } from "@vercel/functions"; +import { headers } from "next/headers"; + +export async function verifyTurnstileToken(turnstileToken: string) { + // get the request headers + const requestHeaders = await headers(); + + // CF header, fallback to req.ip, then X-Forwarded-For + const [ip, errors] = (() => { + let ip: string | null = null; + const errors: string[] = []; + try { + ip = requestHeaders.get("CF-Connecting-IP") || null; + } catch (err) { + console.error("failed to get IP address from CF-Connecting-IP", err); + errors.push("failed to get IP address from CF-Connecting-IP"); + } + if (!ip) { + try { + ip = ipAddress(requestHeaders) || null; + } catch (err) { + console.error( + "failed to get IP address from ipAddress() function", + err, + ); + errors.push("failed to get IP address from ipAddress() function"); + } + } + if (!ip) { + try { + ip = requestHeaders.get("X-Forwarded-For"); + } catch (err) { + console.error("failed to get IP address from X-Forwarded-For", err); + errors.push("failed to get IP address from X-Forwarded-For"); + } + } + return [ip, errors]; + })(); + + if (!ip) { + return { + error: "Could not get IP address. Please try again.", + context: errors, + }; + } + + // https://developers.cloudflare.com/turnstile/get-started/server-side-validation/ + // Validate the token by calling the "/siteverify" API endpoint. + const result = await fetch( + "https://challenges.cloudflare.com/turnstile/v0/siteverify", + { + body: JSON.stringify({ + secret: process.env.TURNSTILE_SECRET_KEY, + response: turnstileToken, + remoteip: ip, + }), + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }, + ); + + const outcome = await result.json(); + + return { + success: outcome.success as boolean, + }; +} diff --git a/apps/dashboard/src/app/nebula-app/(app)/chat/[session_id]/page.tsx b/apps/dashboard/src/app/nebula-app/(app)/chat/[session_id]/page.tsx index cf321b43ef3..3f96b456fb2 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/chat/[session_id]/page.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/chat/[session_id]/page.tsx @@ -1,7 +1,6 @@ import { redirect } from "next/navigation"; -import { getValidAccount } from "../../../../account/settings/getAccount"; -import { getAuthToken } from "../../../../api/lib/getAuthToken"; import { loginRedirect } from "../../../../login/loginRedirect"; +import { getNebulaAuthToken } from "../../../_utils/authToken"; import { getSessionById } from "../../api/session"; import { ChatPageContent } from "../../components/ChatPageContent"; @@ -12,15 +11,14 @@ export default async function Page(props: { }) { const params = await props.params; const pagePath = `/chat/${params.session_id}`; - const authToken = await getAuthToken(); - const account = await getValidAccount(); + const authToken = await getNebulaAuthToken(); if (!authToken) { loginRedirect(pagePath); } const session = await getSessionById({ - authToken, + authToken: authToken, sessionId: params.session_id, }).catch(() => undefined); @@ -33,7 +31,6 @@ export default async function Page(props: { authToken={authToken} session={session} type="new-chat" - account={account} initialParams={undefined} /> ); diff --git a/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.stories.tsx b/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.stories.tsx index 473acfc6b9a..a54d54cb0b6 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.stories.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { subDays } from "date-fns"; import { ThirdwebProvider } from "thirdweb/react"; -import { accountStub, randomLorem } from "../../../../../stories/stubs"; +import { randomLorem } from "../../../../../stories/stubs"; import { ChatPageLayout } from "../../components/ChatPageLayout"; import { ChatHistoryPageUI } from "./ChatHistoryPage"; @@ -73,7 +73,6 @@ function Variant(props: { }) { return ( ); diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx index ca8431b1f5d..5620e83d1a5 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx @@ -9,7 +9,6 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { useThirdwebClient } from "@/constants/thirdweb.client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { ArrowRightIcon } from "lucide-react"; import Link from "next/link"; import { useCallback, useEffect, useRef, useState } from "react"; @@ -28,7 +27,6 @@ export function ChatPageContent(props: { session: SessionInfo | undefined; authToken: string; type: "landing" | "new-chat"; - account: Account; initialParams: | { q: string | undefined; @@ -334,7 +332,6 @@ export function ChatPageContent(props: { authToken={props.authToken} sessionId={sessionId} className="min-w-0 pt-6 pb-32" - twAccount={props.account} client={client} enableAutoScroll={enableAutoScroll} setEnableAutoScroll={setEnableAutoScroll} diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.stories.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.stories.tsx index fa89e5e1da4..fdef7164f40 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.stories.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import { ThirdwebProvider } from "thirdweb/react"; -import { accountStub, randomLorem } from "../../../../stories/stubs"; +import { randomLorem } from "../../../../stories/stubs"; import type { TruncatedSessionInfo } from "../api/types"; import { ChatPageLayout } from "./ChatPageLayout"; @@ -42,13 +42,6 @@ export const ThirtyChats: Story = { }, }; -export const NoEmail: Story = { - args: { - sessions: generateRandomSessions(30), - noEmail: true, - }, -}; - function generateRandomSessions(count: number): TruncatedSessionInfo[] { return Array.from({ length: count }, (_, i) => ({ id: i.toString(), @@ -60,20 +53,12 @@ function generateRandomSessions(count: number): TruncatedSessionInfo[] { function Variant(props: { sessions: TruncatedSessionInfo[]; - noEmail?: boolean; }) { return (
CHILDREN diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.tsx index 703f1583aa0..cfca346a380 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageLayout.tsx @@ -1,5 +1,4 @@ import { cn } from "@/lib/utils"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type { TruncatedSessionInfo } from "../api/types"; import { ChatSidebar } from "./ChatSidebar"; import { MobileNav } from "./NebulaMobileNav"; @@ -10,7 +9,6 @@ export function ChatPageLayout(props: { sessions: TruncatedSessionInfo[]; children: React.ReactNode; className?: string; - account: Account; }) { return (
- + {props.children}
diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx index 8a527908a53..5dcc1870df7 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatSidebar.tsx @@ -1,25 +1,40 @@ "use client"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; -import { ChevronRightIcon, FileCode2Icon, PlusIcon } from "lucide-react"; +import { useMutation } from "@tanstack/react-query"; +import { + ChevronRightIcon, + FileCode2Icon, + LogOutIcon, + PaletteIcon, + PlusIcon, +} from "lucide-react"; +import { useTheme } from "next-themes"; import Link from "next/link"; +import { toast } from "sonner"; +import { Spinner } from "../../../../@/components/ui/Spinner/Spinner"; +import { useDashboardRouter } from "../../../../@/lib/DashboardRouter"; +import { doNebulaLogout } from "../../login/auth-actions"; import type { TruncatedSessionInfo } from "../api/types"; import { useNewChatPageLink } from "../hooks/useNewChatPageLink"; import { useSessionsWithLocalOverrides } from "../hooks/useSessionsWithLocalOverrides"; import { NebulaIcon } from "../icons/NebulaIcon"; import { ChatSidebarLink } from "./ChatSidebarLink"; -import { NebulaAccountButton } from "./NebulaAccountButton"; +import { NebulaConnectWallet } from "./NebulaConnectButton"; export function ChatSidebar(props: { sessions: TruncatedSessionInfo[]; authToken: string; type: "desktop" | "mobile"; - account: Account; }) { const sessions = useSessionsWithLocalOverrides(props.sessions); const sessionsToShow = sessions.slice(0, 10); const newChatPage = useNewChatPageLink(); + const { theme, setTheme } = useTheme(); + const router = useDashboardRouter(); + const logoutMutation = useMutation({ + mutationFn: doNebulaLogout, + }); return (
@@ -77,20 +92,39 @@ export function ChatSidebar(props: {
)} -
+
+ + { + setTheme(theme === "light" ? "dark" : "light"); + }} + icon={PaletteIcon} + label="Theme" + /> + + { + try { + await logoutMutation.mutateAsync(); + router.replace("/login"); + } catch { + toast.error("Failed to log out"); + } + }} + icon={logoutMutation.isPending ? Spinner : LogOutIcon} + label="Log Out" + />
- +
+ +
); } @@ -105,7 +139,7 @@ function SidebarIconLink(props: { ); } + +function SidebarIconButton(props: { + icon: React.FC<{ className?: string }>; + label: string; + onClick: () => void; +}) { + return ( + + ); +} diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx index fdcfa4e3236..5bff9807dee 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx @@ -1,7 +1,7 @@ import { getThirdwebClient } from "@/constants/thirdweb.server"; import type { Meta, StoryObj } from "@storybook/react"; import { ConnectButton, ThirdwebProvider } from "thirdweb/react"; -import { accountStub, randomLorem } from "../../../../stories/stubs"; +import { randomLorem } from "../../../../stories/stubs"; import { BadgeContainer } from "../../../../stories/utils"; import { type ChatMessage, Chats } from "./Chats"; @@ -199,7 +199,6 @@ function Story() { authToken="xxxxx" isChatStreaming={false} sessionId="xxxxx" - twAccount={accountStub()} messages={[ { text: randomLorem(40), @@ -223,7 +222,6 @@ function Story() { authToken="xxxxx" isChatStreaming={false} sessionId="xxxxx" - twAccount={accountStub()} messages={[ { text: responseWithCodeMarkdown, @@ -255,7 +253,6 @@ function Variant(props: { authToken="xxxxx" isChatStreaming={false} sessionId="xxxxx" - twAccount={accountStub()} messages={props.messages} /> diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx index e5bcd4a7e64..b17e171be0c 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx @@ -3,7 +3,6 @@ import { Spinner } from "@/components/ui/Spinner/Spinner"; import { Alert, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi"; import { useMutation } from "@tanstack/react-query"; import { AlertCircleIcon, @@ -49,7 +48,6 @@ export function Chats(props: { authToken: string; sessionId: string | undefined; className?: string; - twAccount: TWAccount; client: ThirdwebClient; setEnableAutoScroll: (enable: boolean) => void; enableAutoScroll: boolean; @@ -170,7 +168,6 @@ export function Chats(props: { ) : message.type === "send_transaction" ? ( ) : ( @@ -208,7 +205,6 @@ export function Chats(props: { function ExecuteTransactionCardWithFallback(props: { txData: NebulaTxData | null; - twAccount: TWAccount; client: ThirdwebClient; }) { if (!props.txData) { @@ -220,13 +216,7 @@ function ExecuteTransactionCardWithFallback(props: { ); } - return ( - - ); + return ; } function MessageActions(props: { diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.stories.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.stories.tsx index 24a717bf082..dc13df1e948 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.stories.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import { useState } from "react"; -import { BadgeContainer } from "../../../../stories/utils"; +import { BadgeContainer, storybookLog } from "../../../../stories/utils"; import type { NebulaContext } from "../api/chat"; import ContextFiltersButton from "./ContextFilters"; @@ -74,7 +74,7 @@ function Variant(props: { contextFilters={contextFilters} setContextFilters={setContextFilters} updateContextFilters={async (values) => { - console.log("Updating context", values); + storybookLog("Updating context", values); await new Promise((resolve) => setTimeout(resolve, 1000)); }} /> diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx index 7a963be128a..2949a28857a 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx @@ -48,7 +48,7 @@ export default function ContextFiltersButton(props: { - ) : ( - - )} - - { - if (e.target instanceof HTMLAnchorElement) { - setIsOpen(false); - } - }} - > -
-
-

{props.account.name}

- {props.account.email && ( -

- {props.account.email} -

- )} -
- -
-
- -
-
- -
- - - - - -
- - - ); -} diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/NebulaConnectButton.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/NebulaConnectButton.tsx new file mode 100644 index 00000000000..0008483ea88 --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/(app)/components/NebulaConnectButton.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { useDashboardRouter } from "@/lib/DashboardRouter"; +import { getSDKTheme } from "app/components/sdk-component-theme"; +import { useTheme } from "next-themes"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { ConnectButton, useActiveAccount } from "thirdweb/react"; +import { useAllChainsData } from "../../../../hooks/chains/allChains"; +import { doNebulaLogout } from "../../login/auth-actions"; + +export const NebulaConnectWallet = (props: { + connectButtonClassName?: string; + signInLinkButtonClassName?: string; + detailsButtonClassName?: string; +}) => { + const thirdwebClient = useThirdwebClient(); + const router = useDashboardRouter(); + const { theme } = useTheme(); + const t = theme === "light" ? "light" : "dark"; + const { allChainsV5 } = useAllChainsData(); + const pathname = usePathname(); + const account = useActiveAccount(); + + if (!account) { + return ( + + ); + } + + return ( + { + try { + await doNebulaLogout(); + router.replace("/login"); + } catch (err) { + console.error("Failed to log out", err); + } + }} + connectButton={{ + className: props.connectButtonClassName, + }} + detailsButton={{ + className: props.detailsButtonClassName, + }} + chains={allChainsV5} + /> + ); +}; diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/NebulaMobileNav.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/NebulaMobileNav.tsx index bb111b1736f..5b9aa2c516c 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/NebulaMobileNav.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/NebulaMobileNav.tsx @@ -7,7 +7,6 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { MenuIcon } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; @@ -18,7 +17,6 @@ import { ChatSidebar } from "./ChatSidebar"; export function MobileNav(props: { sessions: TruncatedSessionInfo[]; authToken: string; - account: Account; }) { const [isOpen, setIsOpen] = useState(false); const newChatPage = useNewChatPageLink(); @@ -55,7 +53,6 @@ export function MobileNav(props: { type="mobile" authToken={props.authToken} sessions={props.sessions} - account={props.account} /> diff --git a/apps/dashboard/src/app/nebula-app/(app)/layout.tsx b/apps/dashboard/src/app/nebula-app/(app)/layout.tsx index 6aba916681e..d5a96564b66 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/layout.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/layout.tsx @@ -1,49 +1,34 @@ -import { getTeams } from "@/api/team"; import type React from "react"; -import { getValidAccount } from "../../account/settings/getAccount"; -import { - getAuthToken, - getAuthTokenWalletAddress, -} from "../../api/lib/getAuthToken"; +import { EnsureValidConnectedWalletLoginClient } from "../../components/EnsureValidConnectedWalletLogin/EnsureValidConnectedWalletLoginClient"; import { loginRedirect } from "../../login/loginRedirect"; +import { getNebulaLoginStatus } from "../_utils/isLoggedIntoNebula"; import { getSessions } from "./api/session"; import { ChatPageLayout } from "./components/ChatPageLayout"; export default async function Layout(props: { children: React.ReactNode; }) { - const account = await getValidAccount(); - const authToken = await getAuthToken(); + const loginStatus = await getNebulaLoginStatus(); - if (!authToken) { - loginRedirect(); - } - - const accountAddress = await getAuthTokenWalletAddress(); - - if (!accountAddress) { - loginRedirect(); - } - - const teams = await getTeams(); - const firstTeam = teams?.[0]; - - if (!firstTeam) { + if (!loginStatus.isLoggedIn) { loginRedirect(); } const sessions = await getSessions({ - authToken, + authToken: loginStatus.authToken, }).catch(() => []); return ( {props.children} + + ); } diff --git a/apps/dashboard/src/app/nebula-app/(app)/page.tsx b/apps/dashboard/src/app/nebula-app/(app)/page.tsx index ab7064e4ac2..0b045188f8f 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/page.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/page.tsx @@ -1,9 +1,8 @@ import { unstable_cache } from "next/cache"; import { isAddress } from "thirdweb"; import { fetchChain } from "../../../utils/fetchChain"; -import { getValidAccount } from "../../account/settings/getAccount"; -import { getAuthToken } from "../../api/lib/getAuthToken"; import { loginRedirect } from "../../login/loginRedirect"; +import { getNebulaAuthToken } from "../_utils/authToken"; import { ChatPageContent } from "./components/ChatPageContent"; export default async function Page(props: { @@ -15,10 +14,9 @@ export default async function Page(props: { }) { const searchParams = await props.searchParams; - const [chainIds, authToken, account] = await Promise.all([ + const [chainIds, authToken] = await Promise.all([ getChainIds(searchParams.chain), - getAuthToken(), - getValidAccount(), + getNebulaAuthToken(), ]); if (!authToken) { @@ -30,7 +28,6 @@ export default async function Page(props: { authToken={authToken} session={undefined} type="landing" - account={account} initialParams={{ q: typeof searchParams.q === "string" ? searchParams.q : undefined, chainIds: chainIds, diff --git a/apps/dashboard/src/app/nebula-app/_utils/authToken.ts b/apps/dashboard/src/app/nebula-app/_utils/authToken.ts new file mode 100644 index 00000000000..3805d0f0d8b --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/_utils/authToken.ts @@ -0,0 +1,34 @@ +import { cookies } from "next/headers"; +import { + NEBULA_COOKIE_ACTIVE_ACCOUNT, + NEBULA_COOKIE_PREFIX_TOKEN, +} from "./constants"; + +export async function getNebulaAuthToken() { + const cookiesManager = await cookies(); + const activeAccount = cookiesManager.get(NEBULA_COOKIE_ACTIVE_ACCOUNT)?.value; + const token = activeAccount + ? cookiesManager.get(NEBULA_COOKIE_PREFIX_TOKEN + activeAccount)?.value + : null; + + return token; +} + +export async function getNebulaAuthTokenWalletAddress() { + const cookiesManager = await cookies(); + const activeAccount = cookiesManager.get(NEBULA_COOKIE_ACTIVE_ACCOUNT)?.value; + + if (!activeAccount) { + return null; + } + + const token = cookiesManager.get( + NEBULA_COOKIE_PREFIX_TOKEN + activeAccount, + )?.value; + + if (token) { + return activeAccount; + } + + return null; +} diff --git a/apps/dashboard/src/app/nebula-app/_utils/constants.ts b/apps/dashboard/src/app/nebula-app/_utils/constants.ts new file mode 100644 index 00000000000..2135632338f --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/_utils/constants.ts @@ -0,0 +1,2 @@ +export const NEBULA_COOKIE_ACTIVE_ACCOUNT = "nebula_active_account"; +export const NEBULA_COOKIE_PREFIX_TOKEN = "nebula_token_"; diff --git a/apps/dashboard/src/app/nebula-app/_utils/isAuthTokenValid.ts b/apps/dashboard/src/app/nebula-app/_utils/isAuthTokenValid.ts new file mode 100644 index 00000000000..7b962e52d39 --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/_utils/isAuthTokenValid.ts @@ -0,0 +1,24 @@ +import { NEXT_PUBLIC_NEBULA_URL } from "@/constants/env"; + +export async function isNebulaAuthTokenValid(token: string) { + const res = await fetch(`${NEXT_PUBLIC_NEBULA_URL}/auth/verify`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + + if (!res.ok) { + return false; + } + + const json = (await res.json()) as { + result: { + address: string; + is_valid: boolean; + }; + }; + + return json.result.is_valid; +} diff --git a/apps/dashboard/src/app/nebula-app/_utils/isLoggedIntoNebula.ts b/apps/dashboard/src/app/nebula-app/_utils/isLoggedIntoNebula.ts new file mode 100644 index 00000000000..d0e83b3af3f --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/_utils/isLoggedIntoNebula.ts @@ -0,0 +1,41 @@ +import { + getNebulaAuthToken, + getNebulaAuthTokenWalletAddress, +} from "./authToken"; +import { isNebulaAuthTokenValid } from "./isAuthTokenValid"; + +export async function getNebulaLoginStatus(): Promise< + | { + isLoggedIn: false; + } + | { + isLoggedIn: true; + authToken: string; + accountAddress: string; + } +> { + const [authToken, accountAddress] = await Promise.all([ + getNebulaAuthToken(), + getNebulaAuthTokenWalletAddress(), + ]); + + if (!authToken || !accountAddress) { + return { + isLoggedIn: false, + }; + } + + const isValidAuthToken = await isNebulaAuthTokenValid(authToken); + + if (!isValidAuthToken) { + return { + isLoggedIn: false, + }; + } + + return { + isLoggedIn: true, + authToken, + accountAddress, + }; +} diff --git a/apps/dashboard/src/app/nebula-app/login/NebulaConnectEmbedLogin.tsx b/apps/dashboard/src/app/nebula-app/login/NebulaConnectEmbedLogin.tsx new file mode 100644 index 00000000000..7c76b9f1edc --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/login/NebulaConnectEmbedLogin.tsx @@ -0,0 +1,194 @@ +"use client"; + +import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage"; +import { Spinner } from "@/components/ui/Spinner/Spinner"; +import { TURNSTILE_SITE_KEY } from "@/constants/env"; +import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { useDashboardRouter } from "@/lib/DashboardRouter"; +import { Turnstile } from "@marsidev/react-turnstile"; +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; +import { + ConnectEmbed, + useActiveAccount, + useActiveWalletConnectionStatus, +} from "thirdweb/react"; +import { createWallet, inAppWallet } from "thirdweb/wallets"; +import { ClientOnly } from "../../../components/ClientOnly/ClientOnly"; +import { isVercel } from "../../../lib/vercel-utils"; +import { getSDKTheme } from "../../components/sdk-component-theme"; +import { + doNebulaLogin, + doNebulaLogout, + getNebulaLoginPayload, + isNebulaLoggedIn, +} from "./auth-actions"; + +export function NebulaLoginPage(props: { + redirectPath: string; +}) { + return ( +
+ +
+ ); +} + +const loginOptions = [ + inAppWallet({ + auth: { + options: [ + "google", + "apple", + "facebook", + "github", + "email", + "phone", + "passkey", + ], + }, + }), + createWallet("io.metamask"), + createWallet("com.coinbase.wallet"), + createWallet("io.rabby"), + createWallet("me.rainbow"), + createWallet("io.zerion.wallet"), +]; + +function NebulaLoginPageContent(props: { + redirectPath: string; +}) { + const accountAddress = useActiveAccount()?.address; + const [screen, setScreen] = useState< + | { id: "login" } + | { + id: "complete"; + } + >({ id: "login" }); + + const router = useDashboardRouter(); + const connectionStatus = useActiveWalletConnectionStatus(); + + function onComplete() { + setScreen({ id: "complete" }); + router.replace(props.redirectPath); + } + + async function onLogin() { + onComplete(); + } + + // eslint-disable-next-line no-restricted-syntax + useEffect(() => { + // if suddenly disconnected + if (connectionStatus !== "connected" && screen.id !== "login") { + setScreen({ id: "login" }); + } + }, [connectionStatus, screen.id]); + + if (screen.id === "complete") { + return ; + } + + if (connectionStatus === "connecting") { + return ; + } + + if ( + connectionStatus !== "connected" || + screen.id === "login" || + !accountAddress + ) { + return ; + } + + return ; +} + +function CustomConnectEmbed(props: { + onLogin: () => void; +}) { + const { theme } = useTheme(); + const client = useThirdwebClient(); + const [turnstileToken, setTurnstileToken] = useState( + undefined, + ); + const [alwaysShowTurnstile, setAlwaysShowTurnstile] = useState(false); + + return ( +
+ setTurnstileToken(token)} + /> + }> + { + if (isVercel() && !turnstileToken) { + setAlwaysShowTurnstile(true); + throw new Error("Please complete the captcha."); + } + + try { + const result = await doNebulaLogin(params, turnstileToken); + if (result.error) { + console.error( + "Failed to login", + result.error, + result.context, + ); + throw new Error(result.error); + } + props.onLogin(); + } catch (e) { + console.error("Failed to login", e); + throw e; + } + }, + doLogout: doNebulaLogout, + isLoggedIn: async (x) => { + const isLoggedInResult = await isNebulaLoggedIn(x); + if (isLoggedInResult) { + props.onLogin(); + } + return isLoggedInResult; + }, + }} + wallets={loginOptions} + client={client} + modalSize="wide" + theme={getSDKTheme(theme === "light" ? "light" : "dark")} + className="shadow-lg" + privacyPolicyUrl="/privacy-policy" + termsOfServiceUrl="/terms" + /> + +
+ ); +} + +function ConnectEmbedSizedCard(props: { + children: React.ReactNode; +}) { + return ( +
+ {props.children} +
+ ); +} + +function ConnectEmbedSizedLoadingCard() { + return ( + + + + ); +} diff --git a/apps/dashboard/src/app/nebula-app/login/NebulaLoginPage.tsx b/apps/dashboard/src/app/nebula-app/login/NebulaLoginPage.tsx index f27cc5bfa69..f1af1e848df 100644 --- a/apps/dashboard/src/app/nebula-app/login/NebulaLoginPage.tsx +++ b/apps/dashboard/src/app/nebula-app/login/NebulaLoginPage.tsx @@ -2,24 +2,23 @@ import { ToggleThemeButton } from "@/components/color-mode-toggle"; import { Button } from "@/components/ui/button"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import Link from "next/link"; import { useState } from "react"; import { EmptyStateChatPageContent } from "../(app)/components/EmptyStateChatPageContent"; import { NebulaIcon } from "../(app)/icons/NebulaIcon"; -import { LoginAndOnboardingPageContent } from "../../login/LoginPage"; +import { NebulaLoginPage } from "./NebulaConnectEmbedLogin"; -export function NebulaLoginPage(props: { - account: Account | undefined; +export function NebulaLoggedOutStatePage(props: { params: { chain: string | string[] | undefined; q: string | undefined; wallet: string | undefined; }; + hasAuthToken: boolean; }) { const [message, setMessage] = useState(props.params.q); const [showPage, setShowPage] = useState<"connect" | "welcome">( - props.account ? "connect" : "welcome", + props.hasAuthToken ? "connect" : "welcome", ); const redirectPathObj = { @@ -48,14 +47,17 @@ export function NebulaLoginPage(props: { {/* nav */}
- +
+ + Nebula +
Support @@ -63,7 +65,7 @@ export function NebulaLoginPage(props: { Docs @@ -79,11 +81,7 @@ export function NebulaLoginPage(props: {
{showPage === "connect" && ( - + )} {showPage === "welcome" && ( diff --git a/apps/dashboard/src/app/nebula-app/login/auth-actions.ts b/apps/dashboard/src/app/nebula-app/login/auth-actions.ts new file mode 100644 index 00000000000..ee192ddc7c7 --- /dev/null +++ b/apps/dashboard/src/app/nebula-app/login/auth-actions.ts @@ -0,0 +1,219 @@ +"use server"; +import "server-only"; + +import { + DASHBOARD_THIRDWEB_SECRET_KEY, + NEXT_PUBLIC_NEBULA_URL, +} from "@/constants/env"; +import { cookies } from "next/headers"; +import { getAddress } from "thirdweb"; +import type { + GenerateLoginPayloadParams, + LoginPayload, + VerifyLoginPayloadParams, +} from "thirdweb/auth"; +import { isVercel } from "../../../lib/vercel-utils"; +import { verifyTurnstileToken } from "../../login/verifyTurnstileToken"; +import { + NEBULA_COOKIE_ACTIVE_ACCOUNT, + NEBULA_COOKIE_PREFIX_TOKEN, +} from "../_utils/constants"; + +export async function getNebulaLoginPayload( + params: GenerateLoginPayloadParams, +): Promise { + const res = await fetch(`${NEXT_PUBLIC_NEBULA_URL}/auth/delegate/payload`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Secret-Key": DASHBOARD_THIRDWEB_SECRET_KEY, + }, + body: JSON.stringify({ + address: params.address, + // chainId: params.chainId?.toString(), + }), + }); + + if (!res.ok) { + console.error("Failed to fetch login payload", await res.text()); + throw new Error("Failed to fetch login payload"); + } + const data = await res.json(); + return data.result.payload as LoginPayload; +} + +export async function doNebulaLogin( + payload: VerifyLoginPayloadParams, + turnstileToken: string | undefined, +) { + // only validate the turnstile token if we are in a vercel environment + if (isVercel()) { + if (!turnstileToken) { + return { + error: "Please complete the captcha.", + }; + } + + const result = await verifyTurnstileToken(turnstileToken); + if (!result.success) { + return { + error: "Invalid captcha. Please try again.", + context: result.context, + }; + } + } + + const cookieStore = await cookies(); + + // forward the request to the API server + const res = await fetch(`${NEXT_PUBLIC_NEBULA_URL}/auth/delegate/login`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Secret-Key": DASHBOARD_THIRDWEB_SECRET_KEY, + }, + body: JSON.stringify(payload), + }); + + // if the request failed, log the error and throw an error + if (!res.ok) { + try { + // clear the cookies to prevent any weird issues + cookieStore.delete( + NEBULA_COOKIE_PREFIX_TOKEN + getAddress(payload.payload.address), + ); + cookieStore.delete(NEBULA_COOKIE_ACTIVE_ACCOUNT); + } catch { + // ignore any errors on this + } + try { + const response = await res.text(); + // try to log the rich error message + console.error( + "Failed to login - api call failed:", + res.status, + res.statusText, + response, + ); + return { + error: "Failed to login. Please try again later.", + }; + } catch { + // just log the basics + console.error( + "Failed to login - api call failed", + res.status, + res.statusText, + ); + } + return { + error: "Failed to login. Please try again later.", + }; + } + + const json = (await res.json()) as { + result: { + token: string; + }; + }; + + const jwt = json.result.token; + + if (!jwt) { + console.error("Failed to login - invalid json", json); + return { + error: "Failed to login. Please try again later.", + }; + } + + // set the token cookie + cookieStore.set( + NEBULA_COOKIE_PREFIX_TOKEN + getAddress(payload.payload.address), + jwt, + { + httpOnly: true, + secure: true, + sameSite: "strict", + // 3 days + maxAge: 3 * 24 * 60 * 60, + }, + ); + + // set the active account cookie + cookieStore.set( + NEBULA_COOKIE_ACTIVE_ACCOUNT, + getAddress(payload.payload.address), + { + httpOnly: true, + secure: true, + sameSite: "strict", + // 3 days + maxAge: 3 * 24 * 60 * 60, + }, + ); + + return { + success: true, + }; +} + +export async function doNebulaLogout() { + const cookieStore = await cookies(); + // delete all cookies that start with the token prefix + const allCookies = cookieStore.getAll(); + for (const cookie of allCookies) { + if (cookie.name.startsWith(NEBULA_COOKIE_PREFIX_TOKEN)) { + cookieStore.delete(cookie.name); + } + } + // also delete the active account cookie + cookieStore.delete(NEBULA_COOKIE_ACTIVE_ACCOUNT); +} + +export async function isNebulaLoggedIn(address: string) { + const cookieName = NEBULA_COOKIE_PREFIX_TOKEN + getAddress(address); + const cookieStore = await cookies(); + // check if we have an access token + const token = cookieStore.get(cookieName)?.value; + if (!token) { + return false; + } + + const res = await fetch(`${NEXT_PUBLIC_NEBULA_URL}/auth/verify`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + + if (!res.ok) { + console.error( + "Failed to check if logged in - api call failed", + res.status, + res.statusText, + ); + // not logged in + // clear the cookie + cookieStore.delete(cookieName); + return false; + } + const json = await res.json(); + + if (!json) { + // not logged in + // clear the cookie + cookieStore.delete(cookieName); + return false; + } + + // set the active account cookie again + cookieStore.set(NEBULA_COOKIE_ACTIVE_ACCOUNT, getAddress(address), { + httpOnly: false, + secure: true, + sameSite: "strict", + // 3 days + maxAge: 3 * 24 * 60 * 60, + }); + return true; +} diff --git a/apps/dashboard/src/app/nebula-app/login/page.tsx b/apps/dashboard/src/app/nebula-app/login/page.tsx index 4a31ccfd4d5..c1440cd5608 100644 --- a/apps/dashboard/src/app/nebula-app/login/page.tsx +++ b/apps/dashboard/src/app/nebula-app/login/page.tsx @@ -1,5 +1,5 @@ -import { getRawAccount } from "../../account/settings/getAccount"; -import { NebulaLoginPage } from "./NebulaLoginPage"; +import { getNebulaAuthToken } from "../_utils/authToken"; +import { NebulaLoggedOutStatePage } from "./NebulaLoginPage"; export default async function NebulaLogin(props: { searchParams: Promise<{ @@ -9,11 +9,11 @@ export default async function NebulaLogin(props: { }>; }) { const searchParams = await props.searchParams; - const account = await getRawAccount(); + const authToken = await getNebulaAuthToken(); return ( - + + + + + + + + + +
); } diff --git a/apps/dashboard/src/components/buttons/MismatchButton.tsx b/apps/dashboard/src/components/buttons/MismatchButton.tsx index f81667ee514..963852aa064 100644 --- a/apps/dashboard/src/components/buttons/MismatchButton.tsx +++ b/apps/dashboard/src/components/buttons/MismatchButton.tsx @@ -18,7 +18,6 @@ import { } from "@/components/ui/popover"; import { useThirdwebClient } from "@/constants/thirdweb.client"; import { cn } from "@/lib/utils"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { useMutation, useQuery } from "@tanstack/react-query"; import { FaucetButton } from "app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton"; import { GiftIcon } from "app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/icons/GiftIcon"; @@ -78,14 +77,14 @@ function useIsNetworkMismatch(txChainId: number) { type MistmatchButtonProps = React.ComponentProps & { txChainId: number; - twAccount: Account | undefined; + isLoggedIn: boolean; }; export const MismatchButton = forwardRef< HTMLButtonElement, MistmatchButtonProps >((props, ref) => { - const { txChainId, twAccount, ...buttonProps } = props; + const { txChainId, isLoggedIn, ...buttonProps } = props; const account = useActiveAccount(); const wallet = useActiveWallet(); const activeWalletChain = useActiveWalletChain(); @@ -110,19 +109,7 @@ export const MismatchButton = forwardRef< const eventRef = useRef>(undefined); - if (!twAccount) { - return ( - - ); - } - - if (!wallet || !chainId) { + if (!wallet || !chainId || !isLoggedIn) { return (
diff --git a/apps/dashboard/src/components/buttons/TransactionButton.stories.tsx b/apps/dashboard/src/components/buttons/TransactionButton.stories.tsx index b24e1f2ef6a..de62eb1d7d1 100644 --- a/apps/dashboard/src/components/buttons/TransactionButton.stories.tsx +++ b/apps/dashboard/src/components/buttons/TransactionButton.stories.tsx @@ -14,7 +14,6 @@ import { useMutation } from "@tanstack/react-query"; import { StarIcon } from "lucide-react"; import { useState } from "react"; import { ConnectButton, ThirdwebProvider } from "thirdweb/react"; -import { accountStub } from "../../stories/stubs"; import { BadgeContainer } from "../../stories/utils"; import { TransactionButton } from "./TransactionButton"; @@ -202,7 +201,7 @@ function Variant(props: { transactionCount={props.transactionCount} txChainID={props.chainId} size={props.size} - twAccount={props.isLoggedIn ? accountStub() : undefined} + isLoggedIn={props.isLoggedIn} > {props.children || "Execute Tx"}
diff --git a/apps/dashboard/src/components/buttons/TransactionButton.tsx b/apps/dashboard/src/components/buttons/TransactionButton.tsx index 567e332f601..dbd9b36a4e4 100644 --- a/apps/dashboard/src/components/buttons/TransactionButton.tsx +++ b/apps/dashboard/src/components/buttons/TransactionButton.tsx @@ -8,7 +8,6 @@ import { } from "@/components/ui/popover"; import { ToolTipLabel } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { CHAIN_ID_TO_GNOSIS } from "constants/mappings"; import { useActiveChainAsDashboardChain } from "lib/v5-adapter"; import { ArrowLeftRightIcon, ExternalLinkIcon } from "lucide-react"; @@ -30,7 +29,7 @@ type TransactionButtonProps = Omit & { isGasless?: boolean; txChainID: number; variant?: "destructive" | "primary" | "default"; - twAccount: Account | undefined; + isLoggedIn: boolean; }; export const TransactionButton: React.FC = ({ @@ -40,7 +39,7 @@ export const TransactionButton: React.FC = ({ isGasless, txChainID, variant, - twAccount, + isLoggedIn, ...restButtonProps }) => { const activeWallet = useActiveWallet(); @@ -71,8 +70,8 @@ export const TransactionButton: React.FC = ({ = ({ publishedContract, - twAccount, + isLoggedIn, }) => { const address = useActiveAccount()?.address; const client = useThirdwebClient(); @@ -148,7 +147,7 @@ export const PublishedContract: React.FC = ({ events={contractEvents} sources={sources.data} abi={publishedContract?.abi} - twAccount={twAccount} + isLoggedIn={isLoggedIn} /> )} diff --git a/apps/dashboard/src/components/contract-functions/contract-function.tsx b/apps/dashboard/src/components/contract-functions/contract-function.tsx index a34c06b8e4f..a9eb59957c0 100644 --- a/apps/dashboard/src/components/contract-functions/contract-function.tsx +++ b/apps/dashboard/src/components/contract-functions/contract-function.tsx @@ -9,7 +9,6 @@ import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import { cn } from "@/lib/utils"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Box, Divider, @@ -62,20 +61,24 @@ const ContractFunctionComment = lazy( interface ContractFunctionProps { fn: AbiFunction | AbiEvent; contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } const ContractFunction: React.FC<{ fn: AbiFunction | AbiEvent; contract?: ThirdwebContract; - twAccount: Account | undefined; -}> = ({ fn, contract, twAccount }) => { + isLoggedIn: boolean; +}> = ({ fn, contract, isLoggedIn }) => { if (!contract) { return ; } return ( - + ); }; @@ -158,7 +161,7 @@ function ContractFunctionInner(props: ContractFunctionProps) { key={JSON.stringify(fn)} contract={contract} abiFunction={fn} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} /> )} @@ -271,7 +274,7 @@ function ContractFunctionInputs(props: { interface ContractFunctionsPanelProps { fnsOrEvents: (AbiFunction | AbiEvent)[]; contract?: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } type ExtensionFunctions = { @@ -282,7 +285,7 @@ type ExtensionFunctions = { export const ContractFunctionsPanel: React.FC = ({ fnsOrEvents, contract, - twAccount, + isLoggedIn, }) => { // TODO: clean this up @@ -499,7 +502,7 @@ export const ContractFunctionsPanel: React.FC = ({ )} diff --git a/apps/dashboard/src/components/contract-functions/contract-functions.tsx b/apps/dashboard/src/components/contract-functions/contract-functions.tsx index 0b141b6a5b3..dcd48071c98 100644 --- a/apps/dashboard/src/components/contract-functions/contract-functions.tsx +++ b/apps/dashboard/src/components/contract-functions/contract-functions.tsx @@ -1,6 +1,5 @@ "use client"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Flex, Tab, @@ -24,7 +23,7 @@ interface ContractFunctionsOverview { sources?: SourceFile[]; abi?: Abi; onlyFunctions?: boolean; - twAccount: Account | undefined; + isLoggedIn: boolean; } export const ContractFunctionsOverview: React.FC = ({ @@ -34,7 +33,7 @@ export const ContractFunctionsOverview: React.FC = ({ sources, abi, onlyFunctions, - twAccount, + isLoggedIn, }) => { if (onlyFunctions) { return ( @@ -43,7 +42,7 @@ export const ContractFunctionsOverview: React.FC = ({ )} @@ -89,7 +88,7 @@ export const ContractFunctionsOverview: React.FC = ({ ) : null} @@ -98,7 +97,7 @@ export const ContractFunctionsOverview: React.FC = ({ ) : null} diff --git a/apps/dashboard/src/components/contract-functions/interactive-abi-function.tsx b/apps/dashboard/src/components/contract-functions/interactive-abi-function.tsx index 211e6cf80d3..ff5fe4be965 100644 --- a/apps/dashboard/src/components/contract-functions/interactive-abi-function.tsx +++ b/apps/dashboard/src/components/contract-functions/interactive-abi-function.tsx @@ -5,7 +5,6 @@ import { CodeClient } from "@/components/ui/code/code.client"; import { PlainTextCodeBlock } from "@/components/ui/code/plaintext-code"; import { InlineCode } from "@/components/ui/inline-code"; import { ToolTipLabel } from "@/components/ui/tooltip"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { ButtonGroup, Divider, @@ -131,7 +130,7 @@ function formatContractCall( interface InteractiveAbiFunctionProps { abiFunction: AbiFunction; contract: ThirdwebContract; - twAccount: Account | undefined; + isLoggedIn: boolean; } function useAsyncRead(contract: ThirdwebContract, abiFunction: AbiFunction) { @@ -546,7 +545,7 @@ export const InteractiveAbiFunction: React.FC = ( form={formId} onClick={handleContractWrite} txChainID={contract.chain.id} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} > Execute
diff --git a/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx b/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx index 56d022f3da5..61e86bc0adf 100644 --- a/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx +++ b/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx @@ -1,7 +1,6 @@ "use client"; import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; -import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Alert, AlertIcon, @@ -104,7 +103,7 @@ function useBatchLazyMintForm() { export const BatchLazyMint: ComponentWithChildren< BatchLazyMintProps & { - twAccount: Account | undefined; + isLoggedIn: boolean; } > = (props) => { const [step, setStep] = useState(0); @@ -269,7 +268,7 @@ export const BatchLazyMint: ComponentWithChildren< disabled={!nftMetadatas.length} type="submit" isPending={form.formState.isSubmitting} - twAccount={props.twAccount} + isLoggedIn={props.isLoggedIn} > {form.formState.isSubmitting ? `Uploading ${nftMetadatas.length} NFTs` diff --git a/apps/dashboard/src/middleware.ts b/apps/dashboard/src/middleware.ts index a6b6d08579a..23b6f4f3ddd 100644 --- a/apps/dashboard/src/middleware.ts +++ b/apps/dashboard/src/middleware.ts @@ -5,6 +5,10 @@ import { type NextRequest, NextResponse } from "next/server"; import { getAddress } from "thirdweb"; import { getChainMetadata } from "thirdweb/chains"; import { isValidENSName } from "thirdweb/utils"; +import { + NEBULA_COOKIE_ACTIVE_ACCOUNT, + NEBULA_COOKIE_PREFIX_TOKEN, +} from "./app/nebula-app/_utils/constants"; import { LAST_VISITED_TEAM_PAGE_PATH } from "./app/team/components/last-visited-page/consts"; import { defineDashboardChain } from "./lib/defineDashboardChain"; @@ -32,9 +36,14 @@ export async function middleware(request: NextRequest) { const subdomain = host?.split(".")[0]; const paths = pathname.slice(1).split("/"); - const activeAccount = request.cookies.get(COOKIE_ACTIVE_ACCOUNT)?.value; - const authCookie = activeAccount - ? request.cookies.get(COOKIE_PREFIX_TOKEN + getAddress(activeAccount)) + const nebulaActiveAccount = request.cookies.get( + NEBULA_COOKIE_ACTIVE_ACCOUNT, + )?.value; + + const nebulaAuthCookie = nebulaActiveAccount + ? request.cookies.get( + NEBULA_COOKIE_PREFIX_TOKEN + getAddress(nebulaActiveAccount), + ) : null; // nebula.thirdweb.com -> render page at app/nebula-app @@ -44,7 +53,7 @@ export async function middleware(request: NextRequest) { (subdomain === "nebula" || subdomain.startsWith("nebula---")) ) { // preserve search params when redirecting to /login page - if (!authCookie && paths[0] !== "login") { + if (!nebulaAuthCookie && paths[0] !== "login") { return redirect(request, "/login", { searchParams: request.nextUrl.searchParams.toString(), }); @@ -69,6 +78,11 @@ export async function middleware(request: NextRequest) { let cookiesToSet: Record | undefined = undefined; + const activeAccount = request.cookies.get(COOKIE_ACTIVE_ACCOUNT)?.value; + const authCookie = activeAccount + ? request.cookies.get(COOKIE_PREFIX_TOKEN + getAddress(activeAccount)) + : null; + // utm collection // if user is already signed in - don't bother capturing utm params if (!authCookie) { diff --git a/apps/dashboard/src/stories/stubs.ts b/apps/dashboard/src/stories/stubs.ts index f805a4119cf..6bb25821789 100644 --- a/apps/dashboard/src/stories/stubs.ts +++ b/apps/dashboard/src/stories/stubs.ts @@ -318,18 +318,6 @@ export function randomLorem(length: number) { }).join(" "); } -export function accountStub(overrides?: Partial): Account { - return { - email: "user@example.com", - name: "John Doe", - id: "foo", - isStaff: false, - advancedEnabled: false, - creatorWalletAddress: "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37", - ...overrides, - }; -} - export function newAccountStub(overrides?: Partial): Account { return { email: undefined, diff --git a/apps/dashboard/src/stories/utils.tsx b/apps/dashboard/src/stories/utils.tsx index bd143c0b9a2..3673008d9a5 100644 --- a/apps/dashboard/src/stories/utils.tsx +++ b/apps/dashboard/src/stories/utils.tsx @@ -35,9 +35,7 @@ export function mobileViewport( }; } -export function storybookLog( - ...mesages: (string | object | number | boolean)[] -) { +export function storybookLog(...mesages: unknown[]) { console.debug( "%cStorybook", "color: white; background-color: black; padding: 2px 4px; border-radius: 4px;",