Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6994310d16 | ||
|
|
e42ffc6a93 | ||
|
|
0ddc123807 | ||
|
|
f91f6310e9 | ||
|
|
dc1e07b5e4 | ||
|
|
2b5080d901 | ||
|
|
5be197bac9 | ||
|
|
34165cb608 | ||
|
|
9954b37b01 | ||
|
|
9b58a086b6 | ||
|
|
22bc557090 | ||
|
|
80b54a937c | ||
|
|
be6433dcf2 | ||
|
|
f6c040719c | ||
|
|
2db85824f0 | ||
|
|
498a26ebbc | ||
|
|
8761fe6102 | ||
|
|
77c8d0eff0 | ||
|
|
29d32b8409 | ||
|
|
67f63c3a48 | ||
|
|
a0770238d4 | ||
|
|
90a16da48e | ||
|
|
e8b1f494fd | ||
|
|
aa72ba2186 | ||
|
|
a57bfdb565 | ||
|
|
c432342ce9 | ||
|
|
64f4103ef7 | ||
|
|
d90857ee5e | ||
|
|
8d2e2ad92d | ||
|
|
c91ca93eba | ||
|
|
db6b0fc1f8 | ||
|
|
203da76f32 | ||
|
|
cd55ada5b2 | ||
|
|
3ec808dc78 | ||
|
|
7d2547979d | ||
|
|
bc368c5a26 | ||
|
|
19487cb430 | ||
|
|
03b6d0d516 | ||
|
|
f7e60419c9 | ||
|
|
08420456af | ||
|
|
611ed239ec | ||
|
|
ce0da51041 | ||
|
|
d738a9086d | ||
|
|
1787539ac5 | ||
|
|
a0fabb2059 | ||
|
|
196f6779e8 | ||
|
|
f701b2d0e6 | ||
|
|
3d6eb969da | ||
|
|
7b6e21fa4a | ||
|
|
4573e2c1a8 | ||
|
|
324e602df0 | ||
|
|
f778d306d6 | ||
|
|
4831926a09 | ||
|
|
2fccfc01af | ||
|
|
d9fb5441bc | ||
|
|
b49dde33be | ||
|
|
46b20716a5 | ||
|
|
da5d8ec53b | ||
|
|
8020bdbf2b | ||
|
|
3f56252298 | ||
|
|
a9dd26cd57 | ||
|
|
5b24faf1f3 | ||
|
|
714602d13c | ||
|
|
c11ae45f89 | ||
|
|
462138dfae | ||
|
|
67305d3bd2 | ||
|
|
27d3a6eb3e | ||
|
|
f25d1d8da5 | ||
|
|
d35eaa6690 |
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
- name: Build Packages
|
||||
run: pnpm build:packages
|
||||
run: pnpm build
|
||||
|
||||
lint:
|
||||
needs: optimize_ci
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
- name: Build Packages
|
||||
run: pnpm build:packages
|
||||
run: pnpm build
|
||||
|
||||
- name: Install Yarn (if needed)
|
||||
if: matrix.package_manager == 'yarn'
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Auto Author Assign
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [ opened, reopened ]
|
||||
types: [opened, reopened]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
@@ -10,5 +10,9 @@ permissions:
|
||||
jobs:
|
||||
assign-author:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event.pull_request.author_association == 'MEMBER' ||
|
||||
github.event.pull_request.author_association == 'OWNER' ||
|
||||
github.event.pull_request.author_association == 'COLLABORATOR'
|
||||
steps:
|
||||
- uses: toshimaru/[email protected]
|
||||
|
||||
@@ -2,10 +2,10 @@ name: Linked Issue
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited]
|
||||
types: [opened, edited, ready_for_review]
|
||||
|
||||
env:
|
||||
VALID_ISSUE_PREFIXES: "CNCT|DASH|PROT"
|
||||
VALID_ISSUE_PREFIXES: "CNCT|DASH|PROT|INSIGHT|ENGINE|CS|DES|BIL|DEVX|SOLU|NEB"
|
||||
|
||||
jobs:
|
||||
linear:
|
||||
@@ -22,6 +22,17 @@ jobs:
|
||||
pull_number: context.issue.number
|
||||
});
|
||||
|
||||
// Check if contributor is external
|
||||
const isInternalContributor = ['MEMBER', 'OWNER', 'COLLABORATOR'].includes(
|
||||
context.payload.pull_request.author_association
|
||||
);
|
||||
|
||||
// Automatically pass for external contributors
|
||||
if (!isInternalContributor) {
|
||||
console.log('External contributor detected - automatically passing check');
|
||||
return;
|
||||
}
|
||||
|
||||
const body = pr.data.body || '';
|
||||
const branchName = pr.data.head.ref;
|
||||
const issueRegex = new RegExp(`(${process.env.VALID_ISSUE_PREFIXES})-\\d+`, 'i');
|
||||
|
||||
@@ -30,17 +30,9 @@ jobs:
|
||||
# Do not use the GITHUB_TOKEN by default
|
||||
token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Create @nightly release
|
||||
run: |
|
||||
pnpm version-packages:nightly
|
||||
|
||||
@@ -31,17 +31,9 @@ jobs:
|
||||
# Do not use the GITHUB_TOKEN by default
|
||||
token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: 1.0.35
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
- name: Build
|
||||
run: pnpm build:release
|
||||
|
||||
- name: Create release Pull Request or publish to NPM
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: TypeDoc
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment
|
||||
concurrency:
|
||||
group: "typedoc"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Generate TypeDoc"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install
|
||||
uses: ./.github/composite-actions/install
|
||||
|
||||
- name: Run TypeDoc
|
||||
run: pnpm typedoc
|
||||
|
||||
- name: Update Gist
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GIST_TOKEN }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const content = fs.readFileSync('./packages/thirdweb/typedoc/parsed.json', 'utf8');
|
||||
const gistId = '678fe1f331a01270bb002fee660f285d';
|
||||
|
||||
await github.rest.gists.update({
|
||||
gist_id: gistId,
|
||||
files: {
|
||||
'data.json': {
|
||||
content: content
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Permalink: https://gist.githubusercontent.com/raw/${gistId}/data.json`);
|
||||
@@ -94,4 +94,7 @@ NEXT_PUBLIC_TURNSTILE_SITE_KEY=""
|
||||
TURNSTILE_SECRET_KEY=""
|
||||
REDIS_URL=""
|
||||
|
||||
ANALYTICS_SERVICE_URL=""
|
||||
ANALYTICS_SERVICE_URL=""
|
||||
|
||||
# Required for Nebula Chat
|
||||
NEXT_PUBLIC_NEBULA_URL=""
|
||||
@@ -3,6 +3,8 @@ module.exports = [
|
||||
// -- home
|
||||
"/",
|
||||
"/pricing",
|
||||
"/bounties",
|
||||
"/contact-us",
|
||||
// -- product landing pages --
|
||||
// -- connect
|
||||
"/connect",
|
||||
@@ -31,4 +33,9 @@ module.exports = [
|
||||
// -- legal --
|
||||
"/terms",
|
||||
"/privacy-policy",
|
||||
// -- community pages --
|
||||
"/community/ambassadors",
|
||||
"/community/startup-program",
|
||||
// -- grants --
|
||||
"/grant/superchain",
|
||||
];
|
||||
|
||||
Vendored
+1
-1
@@ -3,4 +3,4 @@
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
+19
-18
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "next dev",
|
||||
"dev": "next dev --turbo",
|
||||
"build": "NODE_OPTIONS=--max-old-space-size=6144 next build",
|
||||
"start": "next start",
|
||||
"format": "biome format ./src --write",
|
||||
@@ -25,8 +25,8 @@
|
||||
"@chakra-ui/react": "^2.8.2",
|
||||
"@chakra-ui/styled-system": "^2.9.2",
|
||||
"@chakra-ui/theme-tools": "^2.1.2",
|
||||
"@emotion/react": "11.13.5",
|
||||
"@emotion/styled": "11.13.5",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@emotion/styled": "11.14.0",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@n8tb1t/use-scroll-position": "^2.0.3",
|
||||
@@ -46,9 +46,9 @@
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "1.1.4",
|
||||
"@sentry/nextjs": "8.42.0",
|
||||
"@sentry/nextjs": "8.43.0",
|
||||
"@shazow/whatsabi": "^0.17.0",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@tanstack/react-query": "5.62.7",
|
||||
"@tanstack/react-table": "^8.17.3",
|
||||
"@thirdweb-dev/service-utils": "workspace:*",
|
||||
"@vercel/functions": "^1.5.1",
|
||||
@@ -60,14 +60,15 @@
|
||||
"color": "^4.2.3",
|
||||
"compare-versions": "^6.1.0",
|
||||
"date-fns": "4.1.0",
|
||||
"fetch-event-stream": "0.1.5",
|
||||
"flat": "^6.0.1",
|
||||
"framer-motion": "11.13.1",
|
||||
"framer-motion": "11.13.5",
|
||||
"fuse.js": "7.0.0",
|
||||
"input-otp": "^1.4.1",
|
||||
"ioredis": "^5.4.1",
|
||||
"ipaddr.js": "^2.2.0",
|
||||
"lucide-react": "0.468.0",
|
||||
"next": "15.0.4",
|
||||
"next": "15.1.0",
|
||||
"next-plausible": "^3.12.4",
|
||||
"next-seo": "^6.5.0",
|
||||
"next-themes": "^0.4.4",
|
||||
@@ -85,11 +86,11 @@
|
||||
"react-hook-form": "7.53.2",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-table": "^7.8.0",
|
||||
"recharts": "^2.13.3",
|
||||
"recharts": "2.14.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"server-only": "^0.0.1",
|
||||
"shiki": "1.24.0",
|
||||
"sonner": "^1.7.0",
|
||||
"shiki": "1.24.2",
|
||||
"sonner": "^1.7.1",
|
||||
"spdx-correct": "^3.2.0",
|
||||
"swagger-ui-react": "^5.18.2",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
@@ -97,14 +98,14 @@
|
||||
"thirdweb": "workspace:*",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"use-debounce": "^10.0.4",
|
||||
"zod": "3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/cli": "^2.4.1",
|
||||
"@chromatic-com/storybook": "3.2.2",
|
||||
"@next/bundle-analyzer": "15.0.4",
|
||||
"@next/eslint-plugin-next": "15.0.4",
|
||||
"@playwright/test": "1.49.0",
|
||||
"@next/bundle-analyzer": "15.1.0",
|
||||
"@next/eslint-plugin-next": "15.1.0",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@storybook/addon-essentials": "8.4.7",
|
||||
"@storybook/addon-interactions": "8.4.7",
|
||||
"@storybook/addon-links": "8.4.7",
|
||||
@@ -115,24 +116,24 @@
|
||||
"@storybook/react": "8.4.7",
|
||||
"@storybook/test": "8.4.7",
|
||||
"@types/color": "4.2.0",
|
||||
"@types/node": "22.10.1",
|
||||
"@types/node": "22.10.2",
|
||||
"@types/papaparse": "^5.3.15",
|
||||
"@types/pluralize": "^0.0.33",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "19.0.1",
|
||||
"@types/react-dom": "19.0.1",
|
||||
"@types/react-dom": "19.0.2",
|
||||
"@types/react-table": "^7.7.20",
|
||||
"@types/spdx-correct": "^3.1.3",
|
||||
"@types/swagger-ui-react": "^4.18.3",
|
||||
"@typescript-eslint/eslint-plugin": "7.14.1",
|
||||
"@typescript-eslint/parser": "7.14.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"checkly": "^4.14.0",
|
||||
"checkly": "^4.15.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-biome": "1.9.4",
|
||||
"eslint-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124",
|
||||
"eslint-plugin-storybook": "^0.11.1",
|
||||
"knip": "5.39.2",
|
||||
"knip": "5.39.4",
|
||||
"next-sitemap": "^4.2.3",
|
||||
"postcss": "8.4.49",
|
||||
"storybook": "8.4.7",
|
||||
|
||||
@@ -319,6 +319,12 @@ async function redirects() {
|
||||
destination: "/home",
|
||||
permanent: false,
|
||||
},
|
||||
// redirect "/open-source" to "/bounties"
|
||||
{
|
||||
source: "/open-source",
|
||||
destination: "/bounties",
|
||||
permanent: false,
|
||||
},
|
||||
...legacyDashboardToTeamRedirects,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
"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;
|
||||
}
|
||||
@@ -2,6 +2,17 @@ import "server-only";
|
||||
import { API_SERVER_URL } from "@/constants/env";
|
||||
import { getAuthToken } from "../../app/api/lib/getAuthToken";
|
||||
|
||||
type EnabledTeamScope =
|
||||
| "pay"
|
||||
| "storage"
|
||||
| "rpc"
|
||||
| "bundler"
|
||||
| "insight"
|
||||
| "embeddedWallets"
|
||||
| "relayer"
|
||||
| "chainsaw"
|
||||
| "nebula";
|
||||
|
||||
export type Team = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -15,6 +26,7 @@ export type Team = {
|
||||
billingStatus: "validPayment" | (string & {}) | null;
|
||||
billingEmail: string | null;
|
||||
growthTrialEligible: boolean | null;
|
||||
enabledScopes: EnabledTeamScope[];
|
||||
};
|
||||
|
||||
export async function getTeamBySlug(slug: string) {
|
||||
|
||||
@@ -28,36 +28,34 @@ export const Mobile: Story = {
|
||||
|
||||
function Story() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background py-4 text-foreground">
|
||||
<div className="container flex max-w-[1000px] flex-col gap-8 lg:p-10">
|
||||
<BadgeContainer label="Base">
|
||||
<DangerSettingCard
|
||||
title="This is a title"
|
||||
description="This is a description"
|
||||
buttonLabel="Some Action"
|
||||
buttonOnClick={() => {}}
|
||||
isPending={false}
|
||||
confirmationDialog={{
|
||||
title: "This is confirmation title",
|
||||
description: "This is confirmation description",
|
||||
}}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
<div className="container flex max-w-[1000px] flex-col gap-8 lg:p-10">
|
||||
<BadgeContainer label="Base">
|
||||
<DangerSettingCard
|
||||
title="This is a title"
|
||||
description="This is a description"
|
||||
buttonLabel="Some Action"
|
||||
buttonOnClick={() => {}}
|
||||
isPending={false}
|
||||
confirmationDialog={{
|
||||
title: "This is confirmation title",
|
||||
description: "This is confirmation description",
|
||||
}}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
<BadgeContainer label="Loading">
|
||||
<DangerSettingCard
|
||||
title="This is a title"
|
||||
description="This is a description"
|
||||
buttonLabel="Some Action"
|
||||
buttonOnClick={() => {}}
|
||||
isPending={true}
|
||||
confirmationDialog={{
|
||||
title: "This is confirmation title",
|
||||
description: "This is confirmation description",
|
||||
}}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
</div>
|
||||
<BadgeContainer label="Loading">
|
||||
<DangerSettingCard
|
||||
title="This is a title"
|
||||
description="This is a description"
|
||||
buttonLabel="Some Action"
|
||||
buttonOnClick={() => {}}
|
||||
isPending={true}
|
||||
confirmationDialog={{
|
||||
title: "This is confirmation title",
|
||||
description: "This is confirmation description",
|
||||
}}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,16 +3,17 @@ import { ToolTipLabel } from "@/components/ui/tooltip";
|
||||
import { AsteriskIcon, InfoIcon } from "lucide-react";
|
||||
|
||||
export function FormFieldSetup(props: {
|
||||
htmlFor: string;
|
||||
htmlFor?: string;
|
||||
label: string;
|
||||
errorMessage: React.ReactNode | undefined;
|
||||
children: React.ReactNode;
|
||||
tooltip?: React.ReactNode;
|
||||
isRequired: boolean;
|
||||
helperText?: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<div className={props.className}>
|
||||
<div className="mb-2 inline-flex items-center gap-1">
|
||||
<Label htmlFor={props.htmlFor}>{props.label}</Label>
|
||||
|
||||
|
||||
@@ -72,7 +72,10 @@ export function MultiNetworkSelector(props: {
|
||||
onSelectedValuesChange={(chainIds) => {
|
||||
props.onChange(chainIds.map(Number));
|
||||
}}
|
||||
placeholder="Select Chains"
|
||||
placeholder={
|
||||
allChains.length === 0 ? "Loading Chains..." : "Select Chains"
|
||||
}
|
||||
disabled={allChains.length === 0}
|
||||
overrideSearchFn={searchFn}
|
||||
renderOption={renderOption}
|
||||
/>
|
||||
@@ -82,17 +85,29 @@ export function MultiNetworkSelector(props: {
|
||||
export function SingleNetworkSelector(props: {
|
||||
chainId: number | undefined;
|
||||
onChange: (chainId: number) => void;
|
||||
className?: string;
|
||||
popoverContentClassName?: string;
|
||||
// if specified - only these chains will be shown
|
||||
chainIds?: number[];
|
||||
}) {
|
||||
const { allChains, idToChain } = useAllChainsData();
|
||||
|
||||
const chainsToShow = useMemo(() => {
|
||||
if (!props.chainIds) {
|
||||
return allChains;
|
||||
}
|
||||
const chainIdSet = new Set(props.chainIds);
|
||||
return allChains.filter((chain) => chainIdSet.has(chain.chainId));
|
||||
}, [allChains, props.chainIds]);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return allChains.map((chain) => {
|
||||
return chainsToShow.map((chain) => {
|
||||
return {
|
||||
label: chain.name,
|
||||
value: String(chain.chainId),
|
||||
};
|
||||
});
|
||||
}, [allChains]);
|
||||
}, [chainsToShow]);
|
||||
|
||||
const searchFn = useCallback(
|
||||
(option: Option, searchValue: string) => {
|
||||
@@ -129,17 +144,22 @@ export function SingleNetworkSelector(props: {
|
||||
[idToChain],
|
||||
);
|
||||
|
||||
const isLoadingChains = allChains.length === 0;
|
||||
|
||||
return (
|
||||
<SelectWithSearch
|
||||
searchPlaceholder="Search by Name or Chain Id"
|
||||
searchPlaceholder="Search by Name or Chain ID"
|
||||
value={String(props.chainId)}
|
||||
options={options}
|
||||
onValueChange={(chainId) => {
|
||||
props.onChange(Number(chainId));
|
||||
}}
|
||||
placeholder="Select Chain"
|
||||
placeholder={isLoadingChains ? "Loading Chains..." : "Select Chain"}
|
||||
overrideSearchFn={searchFn}
|
||||
renderOption={renderOption}
|
||||
className={props.className}
|
||||
popoverContentClassName={props.popoverContentClassName}
|
||||
disabled={isLoadingChains}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,9 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
maxCount = Number.POSITIVE_INFINITY,
|
||||
className,
|
||||
selectedValues,
|
||||
overrideSearchFn,
|
||||
renderOption,
|
||||
searchPlaceholder,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -105,8 +108,6 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
// show 50 initially and then 20 more when reaching the end
|
||||
const { itemsToShow, lastItemRef } = useShowMore<HTMLButtonElement>(50, 20);
|
||||
|
||||
const { overrideSearchFn } = props;
|
||||
|
||||
const optionsToShow = useMemo(() => {
|
||||
const filteredOptions: {
|
||||
label: string;
|
||||
@@ -152,7 +153,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
}, [searchValue]);
|
||||
|
||||
return (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen} modal>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
ref={ref}
|
||||
@@ -238,7 +239,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
{/* Search */}
|
||||
<div className="relative">
|
||||
<Input
|
||||
placeholder={props.searchPlaceholder || "Search"}
|
||||
placeholder={searchPlaceholder || "Search"}
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
className="!h-auto rounded-b-none border-0 border-border border-b py-4 pl-10 focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||
@@ -285,9 +286,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 grow">
|
||||
{props.renderOption
|
||||
? props.renderOption(option)
|
||||
: option.label}
|
||||
{renderOption ? renderOption(option) : option.label}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -30,6 +30,7 @@ interface SelectWithSearchProps
|
||||
searchTerm: string,
|
||||
) => boolean;
|
||||
renderOption?: (option: { value: string; label: string }) => React.ReactNode;
|
||||
popoverContentClassName?: string;
|
||||
}
|
||||
|
||||
export const SelectWithSearch = React.forwardRef<
|
||||
@@ -37,7 +38,18 @@ export const SelectWithSearch = React.forwardRef<
|
||||
SelectWithSearchProps
|
||||
>(
|
||||
(
|
||||
{ options, onValueChange, placeholder, className, value, ...props },
|
||||
{
|
||||
options,
|
||||
onValueChange,
|
||||
placeholder,
|
||||
className,
|
||||
value,
|
||||
renderOption,
|
||||
overrideSearchFn,
|
||||
popoverContentClassName,
|
||||
searchPlaceholder,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
|
||||
@@ -49,7 +61,6 @@ export const SelectWithSearch = React.forwardRef<
|
||||
|
||||
// show 50 initially and then 20 more when reaching the end
|
||||
const { itemsToShow, lastItemRef } = useShowMore<HTMLButtonElement>(50, 20);
|
||||
const { overrideSearchFn } = props;
|
||||
|
||||
const optionsToShow = useMemo(() => {
|
||||
const filteredOptions: {
|
||||
@@ -122,7 +133,7 @@ export const SelectWithSearch = React.forwardRef<
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent
|
||||
className="z-[10001] p-0"
|
||||
className={cn("z-[10001] p-0", popoverContentClassName)}
|
||||
align="center"
|
||||
sideOffset={10}
|
||||
onEscapeKeyDown={() => setIsPopoverOpen(false)}
|
||||
@@ -136,7 +147,7 @@ export const SelectWithSearch = React.forwardRef<
|
||||
{/* Search */}
|
||||
<div className="relative">
|
||||
<Input
|
||||
placeholder={props.searchPlaceholder || "Search"}
|
||||
placeholder={searchPlaceholder || "Search"}
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
className="!h-auto rounded-b-none border-0 border-border border-b py-3 pl-10 focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||
@@ -175,9 +186,7 @@ export const SelectWithSearch = React.forwardRef<
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 grow">
|
||||
{props.renderOption
|
||||
? props.renderOption(option)
|
||||
: option.label}
|
||||
{renderOption ? renderOption(option) : option.label}
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -34,7 +34,7 @@ export function DynamicHeight(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function useHeightObserver() {
|
||||
export function useHeightObserver() {
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const [height, setHeight] = useState<number | undefined>();
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
height: 32px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.scrollShadowX {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
width: 32px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export function ScrollShadow(props: {
|
||||
scrollableClassName?: string;
|
||||
disableTopShadow?: boolean;
|
||||
shadowColor?: string;
|
||||
shadowClassName?: string;
|
||||
}) {
|
||||
const scrollableEl = useRef<HTMLDivElement>(null);
|
||||
const shadowTopEl = useRef<HTMLDivElement>(null);
|
||||
@@ -94,7 +95,11 @@ export function ScrollShadow(props: {
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={cn(styles.scrollShadowTop, styles.scrollShadowY)}
|
||||
className={cn(
|
||||
styles.scrollShadowTop,
|
||||
styles.scrollShadowY,
|
||||
props.shadowClassName,
|
||||
)}
|
||||
ref={shadowTopEl}
|
||||
style={{
|
||||
opacity: "0",
|
||||
@@ -102,21 +107,33 @@ export function ScrollShadow(props: {
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={cn(styles.scrollShadowBottom, styles.scrollShadowY)}
|
||||
className={cn(
|
||||
styles.scrollShadowBottom,
|
||||
styles.scrollShadowY,
|
||||
props.shadowClassName,
|
||||
)}
|
||||
ref={shadowBottomEl}
|
||||
style={{
|
||||
opacity: "0",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={cn(styles.scrollShadowLeft, styles.scrollShadowX)}
|
||||
className={cn(
|
||||
styles.scrollShadowLeft,
|
||||
styles.scrollShadowX,
|
||||
props.shadowClassName,
|
||||
)}
|
||||
ref={shadowLeftEl}
|
||||
style={{
|
||||
opacity: "0",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={cn(styles.scrollShadowRight, styles.scrollShadowX)}
|
||||
className={cn(
|
||||
styles.scrollShadowRight,
|
||||
styles.scrollShadowX,
|
||||
props.shadowClassName,
|
||||
)}
|
||||
ref={shadowRightEl}
|
||||
style={{
|
||||
opacity: "0",
|
||||
|
||||
@@ -11,7 +11,10 @@ export function CodeBlockContainer(props: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
scrollableClassName?: string;
|
||||
scrollableContainerClassName?: string;
|
||||
copyButtonClassName?: string;
|
||||
shadowColor?: string;
|
||||
onCopy?: (code: string) => void;
|
||||
}) {
|
||||
const { hasCopied, onCopy } = useClipboard(props.codeToCopy);
|
||||
|
||||
@@ -24,8 +27,11 @@ export function CodeBlockContainer(props: {
|
||||
>
|
||||
<ScrollShadow
|
||||
scrollableClassName={cn("p-4", props.scrollableClassName)}
|
||||
className="text-xs md:text-sm [&_*]:leading-relaxed"
|
||||
shadowColor="hsl(var(--muted))"
|
||||
className={cn(
|
||||
"text-xs md:text-sm [&_*]:leading-relaxed",
|
||||
props.scrollableContainerClassName,
|
||||
)}
|
||||
shadowColor={props.shadowColor || "hsl(var(--muted))"}
|
||||
>
|
||||
{props.children}
|
||||
</ScrollShadow>
|
||||
@@ -33,7 +39,10 @@ export function CodeBlockContainer(props: {
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={onCopy}
|
||||
onClick={() => {
|
||||
onCopy();
|
||||
props.onCopy?.(props.codeToCopy);
|
||||
}}
|
||||
className={cn(
|
||||
"absolute top-3.5 right-3.5 h-auto bg-background p-2",
|
||||
props.copyButtonClassName,
|
||||
|
||||
@@ -6,6 +6,9 @@ export function RenderCode(props: {
|
||||
className?: string;
|
||||
scrollableClassName?: string;
|
||||
copyButtonClassName?: string;
|
||||
scrollableContainerClassName?: string;
|
||||
shadowColor?: string;
|
||||
onCopy?: (code: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<CodeBlockContainer
|
||||
@@ -13,6 +16,9 @@ export function RenderCode(props: {
|
||||
className={props.className}
|
||||
copyButtonClassName={props.copyButtonClassName}
|
||||
scrollableClassName={props.scrollableClassName}
|
||||
scrollableContainerClassName={props.scrollableContainerClassName}
|
||||
shadowColor={props.shadowColor}
|
||||
onCopy={props.onCopy}
|
||||
>
|
||||
<div
|
||||
// biome-ignore lint/security/noDangerouslySetInnerHtml: we know what we're doing here
|
||||
|
||||
@@ -12,6 +12,10 @@ export type CodeProps = {
|
||||
scrollableClassName?: string;
|
||||
keepPreviousDataOnCodeChange?: boolean;
|
||||
copyButtonClassName?: string;
|
||||
scrollableContainerClassName?: string;
|
||||
shadowColor?: string;
|
||||
ignoreFormattingErrors?: boolean;
|
||||
onCopy?: (code: string) => void;
|
||||
};
|
||||
|
||||
export const CodeClient: React.FC<CodeProps> = ({
|
||||
@@ -21,10 +25,17 @@ export const CodeClient: React.FC<CodeProps> = ({
|
||||
scrollableClassName,
|
||||
keepPreviousDataOnCodeChange = false,
|
||||
copyButtonClassName,
|
||||
ignoreFormattingErrors,
|
||||
scrollableContainerClassName,
|
||||
shadowColor,
|
||||
onCopy,
|
||||
}) => {
|
||||
const codeQuery = useQuery({
|
||||
queryKey: ["html", code],
|
||||
queryFn: () => getCodeHtml(code, lang),
|
||||
queryFn: () =>
|
||||
getCodeHtml(code, lang, {
|
||||
ignoreFormattingErrors: ignoreFormattingErrors,
|
||||
}),
|
||||
placeholderData: keepPreviousDataOnCodeChange
|
||||
? keepPreviousData
|
||||
: undefined,
|
||||
@@ -38,6 +49,9 @@ export const CodeClient: React.FC<CodeProps> = ({
|
||||
className={className}
|
||||
scrollableClassName={scrollableClassName}
|
||||
copyButtonClassName={copyButtonClassName}
|
||||
scrollableContainerClassName={scrollableContainerClassName}
|
||||
shadowColor={shadowColor}
|
||||
onCopy={onCopy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -49,6 +63,9 @@ export const CodeClient: React.FC<CodeProps> = ({
|
||||
className={className}
|
||||
scrollableClassName={scrollableClassName}
|
||||
copyButtonClassName={copyButtonClassName}
|
||||
scrollableContainerClassName={scrollableContainerClassName}
|
||||
shadowColor={shadowColor}
|
||||
onCopy={onCopy}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,21 +10,32 @@ function isPrettierSupportedLang(lang: BundledLanguage) {
|
||||
lang === "ts" ||
|
||||
lang === "tsx" ||
|
||||
lang === "javascript" ||
|
||||
lang === "typescript" ||
|
||||
lang === "css" ||
|
||||
lang === "json"
|
||||
lang === "typescript"
|
||||
);
|
||||
}
|
||||
|
||||
export async function getCodeHtml(code: string, lang: BundledLanguage) {
|
||||
export async function getCodeHtml(
|
||||
code: string,
|
||||
lang: BundledLanguage,
|
||||
options?: {
|
||||
ignoreFormattingErrors?: boolean;
|
||||
},
|
||||
) {
|
||||
const formattedCode = isPrettierSupportedLang(lang)
|
||||
? await format(code, {
|
||||
parser: "babel-ts",
|
||||
plugins: [parserBabel, estree],
|
||||
printWidth: 60,
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
console.error("Failed to format code");
|
||||
if (!options?.ignoreFormattingErrors) {
|
||||
console.error(e);
|
||||
console.error("Failed to format code");
|
||||
console.log({
|
||||
code,
|
||||
lang,
|
||||
});
|
||||
}
|
||||
|
||||
return code;
|
||||
})
|
||||
: code;
|
||||
|
||||
@@ -7,6 +7,9 @@ export function PlainTextCodeBlock(props: {
|
||||
className?: string;
|
||||
scrollableClassName?: string;
|
||||
codeClassName?: string;
|
||||
scrollableContainerClassName?: string;
|
||||
shadowColor?: string;
|
||||
onCopy?: (code: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<CodeBlockContainer
|
||||
@@ -14,6 +17,9 @@ export function PlainTextCodeBlock(props: {
|
||||
className={props.className}
|
||||
copyButtonClassName={props.copyButtonClassName}
|
||||
scrollableClassName={props.scrollableClassName}
|
||||
scrollableContainerClassName={props.scrollableContainerClassName}
|
||||
shadowColor={props.shadowColor}
|
||||
onCopy={props.onCopy}
|
||||
>
|
||||
<code className={cn("block whitespace-pre", props.codeClassName)}>
|
||||
{props.code}
|
||||
|
||||
@@ -7,7 +7,7 @@ export function InlineCode({
|
||||
return (
|
||||
<code
|
||||
className={cn(
|
||||
"mx-0.5 inline rounded-lg border border-border px-1.5 py-[3px] font-mono text-[0.85em] text-foreground",
|
||||
"mx-0.5 inline rounded-lg border border-border bg-muted px-[0.4em] py-[0.25em] font-mono text-[0.85em] text-foreground",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -171,10 +171,22 @@ function useUnderline<El extends HTMLElement>() {
|
||||
}
|
||||
|
||||
update();
|
||||
let resizeObserver: ResizeObserver | undefined = undefined;
|
||||
|
||||
if (containerRef.current) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
setTimeout(() => {
|
||||
update();
|
||||
}, 100);
|
||||
});
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}
|
||||
|
||||
// add event listener for resize
|
||||
window.addEventListener("resize", update);
|
||||
return () => {
|
||||
window.removeEventListener("resize", update);
|
||||
resizeObserver?.disconnect();
|
||||
};
|
||||
}, [activeTabEl]);
|
||||
|
||||
|
||||
@@ -22,3 +22,27 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
Textarea.displayName = "Textarea";
|
||||
|
||||
export { Textarea };
|
||||
|
||||
export function AutoResizeTextarea(props: TextareaProps) {
|
||||
const textareaRef = React.useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: value is needed in deps array
|
||||
React.useEffect(() => {
|
||||
const textarea = textareaRef.current;
|
||||
if (textarea) {
|
||||
textarea.style.height = "auto";
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}
|
||||
}, [props.value]);
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
{...props}
|
||||
style={{
|
||||
...props.style,
|
||||
overflowY: "hidden",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,3 +39,5 @@ export const BASE_URL = isProd
|
||||
: (process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL
|
||||
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`
|
||||
: "http://localhost:3000") || "https://thirdweb-dev.com";
|
||||
|
||||
export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Spinner } from "@/components/ui/Spinner/Spinner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useThirdwebClient } from "@/constants/thirdweb.client";
|
||||
import { useStore } from "@/lib/reactive";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getSDKTheme } from "app/components/sdk-component-theme";
|
||||
import { CustomChainRenderer } from "components/selects/CustomChainRenderer";
|
||||
import { mapV4ChainToV5Chain } from "contexts/map-chains";
|
||||
@@ -18,6 +16,7 @@ import type { Chain } from "thirdweb";
|
||||
import {
|
||||
ConnectButton,
|
||||
type NetworkSelectorProps,
|
||||
useActiveAccount,
|
||||
useConnectModal,
|
||||
} from "thirdweb/react";
|
||||
import { useFavoriteChainIds } from "../../../../app/(dashboard)/(chain)/components/client/star-button";
|
||||
@@ -29,7 +28,6 @@ import {
|
||||
addRecentlyUsedChainId,
|
||||
recentlyUsedChainIdsStore,
|
||||
} from "../../../../stores/chainStores";
|
||||
import { useLoggedInUser } from "../../hooks/useLoggedInUser";
|
||||
import { popularChains } from "../popularChains";
|
||||
|
||||
export const CustomConnectWallet = (props: {
|
||||
@@ -37,8 +35,8 @@ export const CustomConnectWallet = (props: {
|
||||
connectButtonClassName?: string;
|
||||
signInLinkButtonClassName?: string;
|
||||
detailsButtonClassName?: string;
|
||||
loadingButtonClassName?: string;
|
||||
chain?: Chain;
|
||||
isLoggedIn: boolean;
|
||||
}) => {
|
||||
const thirdwebClient = useThirdwebClient();
|
||||
const loginRequired =
|
||||
@@ -50,12 +48,7 @@ export const CustomConnectWallet = (props: {
|
||||
// chains
|
||||
const favChainIdsQuery = useFavoriteChainIds();
|
||||
const recentChainIds = useStore(recentlyUsedChainIdsStore);
|
||||
const { idToChain, allChains } = useAllChainsData();
|
||||
|
||||
const allChainsWithMetadata = useMemo(
|
||||
() => allChains.map(mapV4ChainToV5Chain),
|
||||
[allChains],
|
||||
);
|
||||
const { idToChain, allChainsV5 } = useAllChainsData();
|
||||
|
||||
const recentlyUsedChainsWithMetadata = useMemo(
|
||||
() =>
|
||||
@@ -120,25 +113,11 @@ export const CustomConnectWallet = (props: {
|
||||
]);
|
||||
|
||||
// ensures login status on pages that need it
|
||||
const { isPending, isLoggedIn } = useLoggedInUser();
|
||||
const { isLoggedIn } = props;
|
||||
const pathname = usePathname();
|
||||
const account = useActiveAccount();
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-[48px] w-[144px] items-center justify-center rounded-lg border border-border bg-muted",
|
||||
props.loadingButtonClassName,
|
||||
)}
|
||||
>
|
||||
<Spinner className="size-4" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoggedIn && loginRequired) {
|
||||
if ((!isLoggedIn || !account) && loginRequired) {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
@@ -186,7 +165,7 @@ export const CustomConnectWallet = (props: {
|
||||
detailsButton={{
|
||||
className: props.detailsButtonClassName,
|
||||
}}
|
||||
chains={allChainsWithMetadata}
|
||||
chains={allChainsV5}
|
||||
detailsModal={{
|
||||
networkSelector: {
|
||||
sections: chainSections,
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import type { Team } from "@/api/team";
|
||||
import {
|
||||
type Query,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { THIRDWEB_ANALYTICS_API_HOST, THIRDWEB_API_HOST } from "constants/urls";
|
||||
import { useAllChainsData } from "hooks/chains/allChains";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useActiveAccount } from "thirdweb/react";
|
||||
import type { UserOpStats } from "types/analytics";
|
||||
import { accountKeys, apiKeys, authorizedWallets } from "../cache-keys";
|
||||
import { useLoggedInUser } from "./useLoggedInUser";
|
||||
|
||||
// FIXME: We keep repeating types, API server should provide them
|
||||
|
||||
@@ -216,55 +210,14 @@ export interface BillingCredit {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
interface UseAccountInput {
|
||||
refetchInterval?:
|
||||
| number
|
||||
| false
|
||||
| ((
|
||||
query: Query<
|
||||
Account,
|
||||
Error,
|
||||
Account,
|
||||
readonly ["account", string, "me"]
|
||||
>,
|
||||
) => number | false | undefined)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export function useAccount({ refetchInterval }: UseAccountInput = {}) {
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
|
||||
return useQuery({
|
||||
queryKey: accountKeys.me(user?.address as string),
|
||||
queryFn: async () => {
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/me`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
if (json.error) {
|
||||
throw new Error(json.error.message);
|
||||
}
|
||||
|
||||
return json.data as Account;
|
||||
},
|
||||
enabled: !!user?.address && isLoggedIn,
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO - remove this hook, fetch on server
|
||||
export function useAccountCredits() {
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
|
||||
const address = useActiveAccount()?.address;
|
||||
return useQuery({
|
||||
queryKey: accountKeys.credits(user?.address as string),
|
||||
queryKey: accountKeys.credits(address || ""),
|
||||
queryFn: async () => {
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/credits`, {
|
||||
method: "GET",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -284,7 +237,7 @@ export function useAccountCredits() {
|
||||
|
||||
return credits;
|
||||
},
|
||||
enabled: !!user?.address && isLoggedIn,
|
||||
enabled: !!address,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -325,19 +278,20 @@ async function getUserOpUsage(args: {
|
||||
return json.data;
|
||||
}
|
||||
|
||||
// TODO - remove this hook, fetch this on server
|
||||
export function useUserOpUsageAggregate(args: {
|
||||
clientId: string;
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
}) {
|
||||
const { clientId, from, to } = args;
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const chainStore = useAllChainsData();
|
||||
|
||||
return useQuery<UserOpStats>({
|
||||
queryKey: accountKeys.userOpStats(
|
||||
user?.address as string,
|
||||
clientId as string,
|
||||
address || "",
|
||||
clientId,
|
||||
from?.toISOString() || "",
|
||||
to?.toISOString() || "",
|
||||
"all",
|
||||
@@ -375,10 +329,11 @@ export function useUserOpUsageAggregate(args: {
|
||||
},
|
||||
);
|
||||
},
|
||||
enabled: !!clientId && !!user?.address && isLoggedIn,
|
||||
enabled: !!clientId && !!address,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO - remove this hook, fetch this on server
|
||||
export function useUserOpUsagePeriod(args: {
|
||||
clientId: string;
|
||||
from?: Date;
|
||||
@@ -386,11 +341,11 @@ export function useUserOpUsagePeriod(args: {
|
||||
period: "day" | "week" | "month" | "year";
|
||||
}) {
|
||||
const { clientId, from, to, period } = args;
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
|
||||
return useQuery({
|
||||
queryKey: accountKeys.userOpStats(
|
||||
user?.address as string,
|
||||
address || "",
|
||||
clientId as string,
|
||||
from?.toISOString() || "",
|
||||
to?.toISOString() || "",
|
||||
@@ -404,21 +359,18 @@ export function useUserOpUsagePeriod(args: {
|
||||
period,
|
||||
});
|
||||
},
|
||||
enabled: !!clientId && !!user?.address && isLoggedIn,
|
||||
enabled: !!clientId && !!address,
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateAccount() {
|
||||
const { user } = useLoggedInUser();
|
||||
const queryClient = useQueryClient();
|
||||
const address = useActiveAccount()?.address;
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (input: UpdateAccountInput) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account`, {
|
||||
method: "PUT",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -436,20 +388,18 @@ export function useUpdateAccount() {
|
||||
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: accountKeys.me(user?.address as string),
|
||||
queryKey: accountKeys.me(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateNotifications() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (input: UpdateAccountNotificationsInput) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/notifications`, {
|
||||
method: "PUT",
|
||||
|
||||
@@ -468,20 +418,18 @@ export function useUpdateNotifications() {
|
||||
},
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: accountKeys.me(user?.address as string),
|
||||
queryKey: accountKeys.me(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useConfirmEmail() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (input: ConfirmEmailInput) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/account/confirmEmail`, {
|
||||
method: "PUT",
|
||||
|
||||
@@ -502,13 +450,13 @@ export function useConfirmEmail() {
|
||||
// invalidate related cache, since could be relinking account
|
||||
return Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: apiKeys.keys(user?.address as string),
|
||||
queryKey: apiKeys.keys(address || ""),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: accountKeys.usage(user?.address as string),
|
||||
queryKey: accountKeys.usage(address || ""),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: accountKeys.me(user?.address as string),
|
||||
queryKey: accountKeys.me(address || ""),
|
||||
}),
|
||||
]);
|
||||
},
|
||||
@@ -516,13 +464,11 @@ export function useConfirmEmail() {
|
||||
}
|
||||
|
||||
export function useResendEmailConfirmation() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(
|
||||
`${THIRDWEB_API_HOST}/v1/account/resendEmailConfirmation`,
|
||||
{
|
||||
@@ -544,20 +490,21 @@ export function useResendEmailConfirmation() {
|
||||
},
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: accountKeys.me(user?.address as string),
|
||||
queryKey: accountKeys.me(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useApiKeys() {
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
export function useApiKeys(props: {
|
||||
isLoggedIn: boolean;
|
||||
}) {
|
||||
const address = useActiveAccount()?.address;
|
||||
return useQuery({
|
||||
queryKey: apiKeys.keys(user?.address as string),
|
||||
queryKey: apiKeys.keys(address || ""),
|
||||
queryFn: async () => {
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/keys`, {
|
||||
method: "GET",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -569,21 +516,18 @@ export function useApiKeys() {
|
||||
}
|
||||
return json.data as ApiKey[];
|
||||
},
|
||||
enabled: !!user?.address && isLoggedIn,
|
||||
enabled: !!address && props.isLoggedIn,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateApiKey() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (input: CreateKeyInput) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/keys`, {
|
||||
method: "POST",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -600,20 +544,18 @@ export function useCreateApiKey() {
|
||||
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: apiKeys.keys(user?.address as string),
|
||||
queryKey: apiKeys.keys(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateApiKey() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (input: UpdateKeyInput) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/keys/${input.id}`, {
|
||||
method: "PUT",
|
||||
|
||||
@@ -633,23 +575,20 @@ export function useUpdateApiKey() {
|
||||
},
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: apiKeys.keys(user?.address as string),
|
||||
queryKey: apiKeys.keys(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRevokeApiKey() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (id: string) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/keys/${id}/revoke`, {
|
||||
method: "POST",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
@@ -665,7 +604,7 @@ export function useRevokeApiKey() {
|
||||
},
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: apiKeys.keys(user?.address as string),
|
||||
queryKey: apiKeys.keys(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -728,13 +667,11 @@ export const useUpdatePolicies = () => {
|
||||
};
|
||||
|
||||
export function useRevokeAuthorizedWallet() {
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (variables: { authorizedWalletId: string }) => {
|
||||
invariant(user?.address, "walletAddress is required");
|
||||
|
||||
const { authorizedWalletId } = variables;
|
||||
|
||||
const res = await fetch(
|
||||
@@ -758,17 +695,16 @@ export function useRevokeAuthorizedWallet() {
|
||||
},
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: authorizedWallets.authorizedWallets(user?.address as string),
|
||||
queryKey: authorizedWallets.authorizedWallets(address || ""),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useAuthorizedWallets() {
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
|
||||
const address = useActiveAccount()?.address;
|
||||
return useQuery({
|
||||
queryKey: authorizedWallets.authorizedWallets(user?.address as string),
|
||||
queryKey: authorizedWallets.authorizedWallets(address || ""),
|
||||
queryFn: async () => {
|
||||
const res = await fetch(`${THIRDWEB_API_HOST}/v1/authorized-wallets`, {
|
||||
method: "GET",
|
||||
@@ -785,7 +721,7 @@ export function useAuthorizedWallets() {
|
||||
|
||||
return json.data as AuthorizedWallet[];
|
||||
},
|
||||
enabled: !!user?.address && isLoggedIn,
|
||||
enabled: !!address,
|
||||
gcTime: 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import { THIRDWEB_EWS_API_HOST } from "constants/urls";
|
||||
import { useActiveAccount } from "thirdweb/react";
|
||||
import type { WalletUser } from "thirdweb/wallets";
|
||||
import { embeddedWalletsKeys } from "../cache-keys";
|
||||
import { useLoggedInUser } from "./useLoggedInUser";
|
||||
|
||||
const fetchAccountList = ({
|
||||
jwt,
|
||||
@@ -38,28 +38,37 @@ const fetchAccountList = ({
|
||||
};
|
||||
};
|
||||
|
||||
export function useEmbeddedWallets(clientId: string, page: number) {
|
||||
const { user, isLoggedIn } = useLoggedInUser();
|
||||
export function useEmbeddedWallets(params: {
|
||||
clientId: string;
|
||||
page: number;
|
||||
authToken: string;
|
||||
}) {
|
||||
const { clientId, page, authToken } = params;
|
||||
const address = useActiveAccount()?.address;
|
||||
|
||||
return useQuery({
|
||||
queryKey: embeddedWalletsKeys.embeddedWallets(
|
||||
user?.address as string,
|
||||
address || "",
|
||||
clientId,
|
||||
page,
|
||||
),
|
||||
queryFn: fetchAccountList({
|
||||
jwt: user?.jwt ?? "",
|
||||
jwt: authToken,
|
||||
clientId,
|
||||
pageNumber: page,
|
||||
}),
|
||||
placeholderData: keepPreviousData,
|
||||
enabled: !!user?.address && isLoggedIn && !!clientId,
|
||||
enabled: !!address && !!clientId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAllEmbeddedWallets() {
|
||||
export function useAllEmbeddedWallets(params: {
|
||||
authToken: string;
|
||||
}) {
|
||||
const { authToken } = params;
|
||||
const queryClient = useQueryClient();
|
||||
const { user } = useLoggedInUser();
|
||||
const address = useActiveAccount()?.address;
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
clientId,
|
||||
@@ -72,12 +81,12 @@ export function useAllEmbeddedWallets() {
|
||||
totalPages: number;
|
||||
}>({
|
||||
queryKey: embeddedWalletsKeys.embeddedWallets(
|
||||
user?.address as string,
|
||||
address || "",
|
||||
clientId,
|
||||
page,
|
||||
),
|
||||
queryFn: fetchAccountList({
|
||||
jwt: user?.jwt ?? "",
|
||||
jwt: authToken,
|
||||
clientId,
|
||||
pageNumber: page,
|
||||
}),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,147 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { isLoginRequired } from "@/constants/auth";
|
||||
import { useDashboardRouter } from "@/lib/DashboardRouter";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { EnsureLoginResponse } from "app/api/auth/ensure-login/route";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
useActiveAccount,
|
||||
useActiveWalletConnectionStatus,
|
||||
} from "thirdweb/react";
|
||||
|
||||
// define "TW_AUTH_TOKEN" to exist on the window object
|
||||
declare global {
|
||||
interface Window {
|
||||
TW_AUTH_TOKEN?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export function useLoggedInUser(): {
|
||||
isPending: boolean;
|
||||
isLoggedIn: boolean;
|
||||
user: { address: string; jwt?: string } | null;
|
||||
} {
|
||||
const router = useDashboardRouter();
|
||||
const pathname = usePathname();
|
||||
const connectedAddress = useActiveAccount()?.address;
|
||||
const connectionStatus = useActiveWalletConnectionStatus();
|
||||
// this is to work around the fact that `connectionStatus` ends up as "disconnected"
|
||||
// which we *do* care about, but only after we have been "connecting" at least once
|
||||
const [statusWasEverConnecting, setStatusWasEverConnecting] = useState(
|
||||
connectionStatus === "connecting" || connectionStatus === "connected",
|
||||
);
|
||||
// needs to be a useEffect for now
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
useEffect(() => {
|
||||
if (connectionStatus === "connecting" || connectionStatus === "connected") {
|
||||
setStatusWasEverConnecting(true);
|
||||
}
|
||||
}, [connectionStatus]);
|
||||
|
||||
const query = useQuery({
|
||||
// enabled if:
|
||||
// - there is a pathname
|
||||
// - we are not already currently connecting
|
||||
// - the pathname requires login
|
||||
enabled:
|
||||
!!pathname &&
|
||||
connectionStatus !== "connecting" &&
|
||||
statusWasEverConnecting &&
|
||||
isLoginRequired(pathname),
|
||||
// the last "persist", part of the queryKey, is to make sure that we do not cache this query in indexDB
|
||||
// convention in v4 of the SDK that we are (ab)using here
|
||||
queryKey: ["logged_in_user", connectedAddress, { persist: false }],
|
||||
retry: false,
|
||||
queryFn: async () => {
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
if (connectedAddress) {
|
||||
searchParams.set("address", connectedAddress);
|
||||
}
|
||||
const res = await fetch(
|
||||
`/api/auth/ensure-login?${searchParams.toString()}`,
|
||||
{
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
|
||||
return (await res.json()) as EnsureLoginResponse;
|
||||
},
|
||||
});
|
||||
|
||||
// legit use-case for now
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
useEffect(() => {
|
||||
if (query.data?.isLoggedIn === false) {
|
||||
// not using useSearchParams hook here to avoid adding Suspense boundaries everywhere this hook is being used
|
||||
const currentHref = new URL(window.location.href);
|
||||
const currentPathname = currentHref.pathname;
|
||||
const currentSearchParams = currentHref.searchParams.toString();
|
||||
router.replace(
|
||||
buildLoginPath(
|
||||
`${currentPathname}${currentSearchParams ? `?${currentSearchParams}` : ""}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}, [query.data?.isLoggedIn, router]);
|
||||
|
||||
// if we are "disconnected" we are not logged in
|
||||
if (connectionStatus === "disconnected") {
|
||||
return {
|
||||
isPending: false,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
// if we are still connecting, we are "loading"
|
||||
if (connectionStatus === "connecting") {
|
||||
return {
|
||||
isPending: true,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
// same if we do not yet have a path
|
||||
if (!pathname) {
|
||||
return {
|
||||
isPending: true,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
// if we do not have an address we are not logged in
|
||||
if (!connectedAddress) {
|
||||
return {
|
||||
isPending: false,
|
||||
isLoggedIn: false,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
// if we are not on a logged in path, we can simply return the connected address
|
||||
if (!isLoginRequired(pathname)) {
|
||||
return {
|
||||
isPending: false,
|
||||
isLoggedIn: true,
|
||||
user: { address: connectedAddress },
|
||||
};
|
||||
}
|
||||
|
||||
// otherwise we return the query data
|
||||
return {
|
||||
isPending: query.isPending,
|
||||
isLoggedIn: query.data ? query.data.isLoggedIn : false,
|
||||
user: query.data?.isLoggedIn
|
||||
? { address: connectedAddress, jwt: query.data.jwt }
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
function buildLoginPath(pathname: string | undefined): string {
|
||||
return `/login${pathname ? `?next=${encodeURIComponent(pathname)}` : ""}`;
|
||||
}
|
||||
+13
-31
@@ -7,8 +7,7 @@ import {
|
||||
TURNSTILE_SITE_KEY,
|
||||
} from "@/constants/env";
|
||||
import { useThirdwebClient } from "@/constants/thirdweb.client";
|
||||
import { useAccount } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { useLoggedInUser } from "@3rdweb-sdk/react/hooks/useLoggedInUser";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { Turnstile } from "@marsidev/react-turnstile";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { CanClaimResponseType } from "app/api/testnet-faucet/can-claim/CanClaimResponseType";
|
||||
@@ -48,9 +47,11 @@ const claimFaucetSchema = z.object({
|
||||
export function FaucetButton({
|
||||
chain,
|
||||
amount,
|
||||
twAccount,
|
||||
}: {
|
||||
chain: ChainMetadata;
|
||||
amount: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const client = useThirdwebClient();
|
||||
@@ -116,9 +117,6 @@ export function FaucetButton({
|
||||
},
|
||||
});
|
||||
|
||||
const accountQuery = useAccount();
|
||||
const userQuery = useLoggedInUser();
|
||||
|
||||
const canClaimFaucetQuery = useQuery({
|
||||
queryKey: ["testnet-faucet-can-claim", chainId],
|
||||
queryFn: async () => {
|
||||
@@ -140,7 +138,7 @@ export function FaucetButton({
|
||||
const form = useForm<z.infer<typeof claimFaucetSchema>>();
|
||||
|
||||
// Force users to log in to claim the faucet
|
||||
if (!address || !userQuery.user) {
|
||||
if (!address || !twAccount) {
|
||||
return (
|
||||
<Button variant="primary" className="w-full" asChild>
|
||||
<Link
|
||||
@@ -152,18 +150,16 @@ export function FaucetButton({
|
||||
);
|
||||
}
|
||||
|
||||
if (accountQuery.isPending) {
|
||||
if (!isOnboardingComplete(twAccount)) {
|
||||
return (
|
||||
<Button variant="outline" className="w-full gap-2">
|
||||
Loading account <Spinner className="size-3" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (!accountQuery.data) {
|
||||
return (
|
||||
<Button variant="outline" className="w-full gap-2" disabled>
|
||||
Failed to load account
|
||||
<Button asChild className="w-full">
|
||||
<Link
|
||||
href={
|
||||
pathname ? `/login?next=${encodeURIComponent(pathname)}` : "/login"
|
||||
}
|
||||
>
|
||||
Verify your Email
|
||||
</Link>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -203,20 +199,6 @@ export function FaucetButton({
|
||||
);
|
||||
}
|
||||
|
||||
if (!isOnboardingComplete(accountQuery.data)) {
|
||||
return (
|
||||
<Button asChild className="w-full">
|
||||
<Link
|
||||
href={
|
||||
pathname ? `/login?next=${encodeURIComponent(pathname)}` : "/login"
|
||||
}
|
||||
>
|
||||
Verify your Email
|
||||
</Link>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
const claimFunds = (values: z.infer<typeof claimFaucetSchema>) => {
|
||||
// Instead of having a dedicated endpoint (/api/verify-token),
|
||||
// we can just attach the token in the payload and send it to the claim-faucet endpoint, to avoid a round-trip request
|
||||
|
||||
+11
-3
@@ -1,3 +1,4 @@
|
||||
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";
|
||||
@@ -5,8 +6,11 @@ import { FaucetButton } from "../client/FaucetButton";
|
||||
import { GiftIcon } from "../icons/GiftIcon";
|
||||
import { SectionTitle } from "./SectionTitle";
|
||||
|
||||
export async function FaucetSection(props: { chain: ChainMetadata }) {
|
||||
const { chain } = props;
|
||||
export async function FaucetSection(props: {
|
||||
chain: ChainMetadata;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const { chain, twAccount } = props;
|
||||
|
||||
// Check eligibilty.
|
||||
const sanitizedChainName = chain.name.replace("Mainnet", "").trim();
|
||||
@@ -40,7 +44,11 @@ export async function FaucetSection(props: { chain: ChainMetadata }) {
|
||||
|
||||
<div className="h-8" />
|
||||
|
||||
<FaucetButton chain={chain} amount={amountToGive} />
|
||||
<FaucetButton
|
||||
chain={chain}
|
||||
amount={amountToGive}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -189,7 +189,7 @@ export default async function ChainPageLayout(props: {
|
||||
|
||||
{/* description */}
|
||||
{chainMetadata?.about && (
|
||||
<p className="mb-2 text-muted-foreground text-sm lg:text-base">
|
||||
<p className="mb-2 whitespace-pre-line text-muted-foreground text-sm lg:text-base">
|
||||
{chainMetadata.about}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { fetchTopContracts } from "lib/search";
|
||||
import { ArrowRightIcon, CircleAlertIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getRawAccount } from "../../../../account/settings/getAccount";
|
||||
import { TrendingContractSection } from "../../../trending/components/trending-table";
|
||||
import { getChain, getChainMetadata } from "../../utils";
|
||||
import { BuyFundsSection } from "./components/server/BuyFundsSection";
|
||||
@@ -20,11 +21,14 @@ export default async function Page(props: {
|
||||
const chainMetadata = await getChainMetadata(chain.chainId);
|
||||
const isDeprecated = chain.status === "deprecated";
|
||||
|
||||
const topContracts = await fetchTopContracts({
|
||||
chainId: chain.chainId,
|
||||
perPage: 3,
|
||||
timeRange: "month",
|
||||
});
|
||||
const [account, topContracts] = await Promise.all([
|
||||
getRawAccount(),
|
||||
fetchTopContracts({
|
||||
chainId: chain.chainId,
|
||||
perPage: 3,
|
||||
timeRange: "month",
|
||||
}),
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-10">
|
||||
@@ -48,7 +52,7 @@ export default async function Page(props: {
|
||||
|
||||
{/* Faucet / Buy Funds */}
|
||||
{chain.testnet ? (
|
||||
<FaucetSection chain={chain} />
|
||||
<FaucetSection chain={chain} twAccount={account} />
|
||||
) : chain.services.find((c) => c.service === "pay" && c.enabled) ? (
|
||||
<BuyFundsSection chain={chain} />
|
||||
) : null}
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -12,12 +13,14 @@ interface CancelTabProps {
|
||||
id: string;
|
||||
contract: ThirdwebContract;
|
||||
isAuction?: boolean;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const CancelTab: React.FC<CancelTabProps> = ({
|
||||
id,
|
||||
contract,
|
||||
isAuction,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const { idToChain } = useAllChainsData();
|
||||
@@ -29,6 +32,7 @@ export const CancelTab: React.FC<CancelTabProps> = ({
|
||||
return (
|
||||
<div className="flex flex-col gap-3 pt-3">
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
isPending={cancelQuery.isPending}
|
||||
|
||||
+4
@@ -9,6 +9,7 @@ import {
|
||||
SheetTrigger,
|
||||
} from "@/components/ui/sheet";
|
||||
import { ListerOnly } from "@3rdweb-sdk/react/components/roles/lister-only";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
@@ -19,12 +20,14 @@ interface CreateListingButtonProps {
|
||||
contract: ThirdwebContract;
|
||||
createText?: string;
|
||||
type?: "direct-listings" | "english-auctions";
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
|
||||
createText = "Create",
|
||||
type,
|
||||
contract,
|
||||
twAccount,
|
||||
...restButtonProps
|
||||
}) => {
|
||||
const address = useActiveAccount()?.address;
|
||||
@@ -43,6 +46,7 @@ export const CreateListingButton: React.FC<CreateListingButtonProps> = ({
|
||||
<SheetTitle className="mb-5 text-left">{createText}</SheetTitle>
|
||||
</SheetHeader>
|
||||
<CreateListingsForm
|
||||
twAccount={twAccount}
|
||||
contract={contract}
|
||||
type={type}
|
||||
actionText={createText}
|
||||
|
||||
+4
@@ -1,6 +1,7 @@
|
||||
import { Alert, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { useDashboardOwnedNFTs } from "@3rdweb-sdk/react/hooks/useDashboardOwnedNFTs";
|
||||
import { useWalletNFTs } from "@3rdweb-sdk/react/hooks/useWalletNFTs";
|
||||
import {
|
||||
@@ -78,6 +79,7 @@ type CreateListingsFormProps = {
|
||||
actionText: string;
|
||||
setOpen: Dispatch<SetStateAction<boolean>>;
|
||||
type?: "direct-listings" | "english-auctions";
|
||||
twAccount: Account | undefined;
|
||||
};
|
||||
|
||||
const auctionTimes = [
|
||||
@@ -95,6 +97,7 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
|
||||
type,
|
||||
actionText,
|
||||
setOpen,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const chainId = contract.chain.id;
|
||||
@@ -536,6 +539,7 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
|
||||
Cancel
|
||||
</Button>
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
isPending={isFormLoading}
|
||||
transactionCount={2}
|
||||
|
||||
+4
@@ -13,6 +13,7 @@ import type {
|
||||
import { useActiveAccount } from "thirdweb/react";
|
||||
|
||||
import { CodeClient } from "@/components/ui/code/code.client";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { NFTMediaWithEmptyState } from "tw-components/nft-media";
|
||||
import { CancelTab } from "./cancel-tab";
|
||||
import { LISTING_STATUS } from "./types";
|
||||
@@ -22,6 +23,7 @@ interface NFTDrawerProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
data: DirectListing | EnglishAuction | null;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ListingDrawer: React.FC<NFTDrawerProps> = ({
|
||||
@@ -29,6 +31,7 @@ export const ListingDrawer: React.FC<NFTDrawerProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
data,
|
||||
twAccount,
|
||||
}) => {
|
||||
const address = useActiveAccount()?.address;
|
||||
const prevData = usePrevious(data);
|
||||
@@ -162,6 +165,7 @@ export const ListingDrawer: React.FC<NFTDrawerProps> = ({
|
||||
contract={contract}
|
||||
id={renderData.id.toString()}
|
||||
isAuction={renderData.type === "english-auction"}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
)}
|
||||
</SheetContent>
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
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,
|
||||
@@ -98,6 +99,7 @@ interface MarketplaceTableProps {
|
||||
start: number;
|
||||
}>
|
||||
>;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
|
||||
@@ -109,6 +111,7 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
|
||||
totalCountQuery,
|
||||
queryParams,
|
||||
setQueryParams,
|
||||
twAccount,
|
||||
}) => {
|
||||
const [listingsToShow, setListingsToShow_] = useState<"all" | "valid">("all");
|
||||
|
||||
@@ -213,6 +216,7 @@ export const MarketplaceTable: React.FC<MarketplaceTableProps> = ({
|
||||
data={tokenRow}
|
||||
isOpen={!!tokenRow}
|
||||
onClose={() => setTokenRow(null)}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
<Table {...getTableProps()}>
|
||||
<Thead>
|
||||
|
||||
+8
-1
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { ContractDirectListingsPage } from "./ContractDirectListingsPage";
|
||||
|
||||
export function ContractDirectListingsPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -23,5 +25,10 @@ export function ContractDirectListingsPageClient(props: {
|
||||
return <RedirectToContractOverview contract={props.contract} />;
|
||||
}
|
||||
|
||||
return <ContractDirectListingsPage contract={props.contract} />;
|
||||
return (
|
||||
<ContractDirectListingsPage
|
||||
contract={props.contract}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,16 +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;
|
||||
}
|
||||
|
||||
export const ContractDirectListingsPage: React.FC<
|
||||
ContractDirectListingsPageProps
|
||||
> = ({ contract }) => {
|
||||
> = ({ contract, twAccount }) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
@@ -20,11 +22,12 @@ export const ContractDirectListingsPage: React.FC<
|
||||
contract={contract}
|
||||
type="direct-listings"
|
||||
createText="Create Direct Listing"
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DirectListingsTable contract={contract} />
|
||||
<DirectListingsTable contract={contract} twAccount={twAccount} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { useState } from "react";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import {
|
||||
@@ -12,12 +13,14 @@ import { MarketplaceTable } from "../../components/marketplace-table";
|
||||
|
||||
interface DirectListingsTableProps {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
|
||||
|
||||
export const DirectListingsTable: React.FC<DirectListingsTableProps> = ({
|
||||
contract,
|
||||
twAccount,
|
||||
}) => {
|
||||
const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE);
|
||||
const getAllQueryResult = useReadContract(getAllListings, {
|
||||
@@ -40,6 +43,7 @@ export const DirectListingsTable: React.FC<DirectListingsTableProps> = ({
|
||||
totalCountQuery={totalCountQuery}
|
||||
queryParams={queryParams}
|
||||
setQueryParams={setQueryParams}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+11
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../../_utils/getContractPageMetadata";
|
||||
import { ContractDirectListingsPage } from "./ContractDirectListingsPage";
|
||||
@@ -12,6 +13,7 @@ export default async function Page(props: {
|
||||
}>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const account = await getRawAccount();
|
||||
const info = await getContractPageParamsInfo(params);
|
||||
|
||||
if (!info) {
|
||||
@@ -19,7 +21,12 @@ export default async function Page(props: {
|
||||
}
|
||||
|
||||
if (info.chainMetadata.chainId === localhost.id) {
|
||||
return <ContractDirectListingsPageClient contract={info.contract} />;
|
||||
return (
|
||||
<ContractDirectListingsPageClient
|
||||
contract={info.contract}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isDirectListingSupported } = await getContractPageMetadata(
|
||||
@@ -30,5 +37,7 @@ export default async function Page(props: {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <ContractDirectListingsPage contract={info.contract} />;
|
||||
return (
|
||||
<ContractDirectListingsPage contract={info.contract} twAccount={account} />
|
||||
);
|
||||
}
|
||||
|
||||
+8
-1
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage";
|
||||
|
||||
export function ContractEnglishAuctionsPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -23,5 +25,10 @@ export function ContractEnglishAuctionsPageClient(props: {
|
||||
return <RedirectToContractOverview contract={props.contract} />;
|
||||
}
|
||||
|
||||
return <ContractEnglishAuctionsPage contract={props.contract} />;
|
||||
return (
|
||||
<ContractEnglishAuctionsPage
|
||||
contract={props.contract}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,16 +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;
|
||||
}
|
||||
|
||||
export const ContractEnglishAuctionsPage: React.FC<
|
||||
ContractEnglishAuctionsProps
|
||||
> = ({ contract }) => {
|
||||
> = ({ contract, twAccount }) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
@@ -20,11 +22,12 @@ export const ContractEnglishAuctionsPage: React.FC<
|
||||
contract={contract}
|
||||
type="english-auctions"
|
||||
createText="Create English Auction"
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EnglishAuctionsTable contract={contract} />
|
||||
<EnglishAuctionsTable contract={contract} twAccount={twAccount} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { useState } from "react";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import {
|
||||
@@ -12,12 +13,14 @@ import { MarketplaceTable } from "../../components/marketplace-table";
|
||||
|
||||
interface EnglishAuctionsTableProps {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const DEFAULT_QUERY_STATE = { count: 50, start: 0 };
|
||||
|
||||
export const EnglishAuctionsTable: React.FC<EnglishAuctionsTableProps> = ({
|
||||
contract,
|
||||
twAccount,
|
||||
}) => {
|
||||
const [queryParams, setQueryParams] = useState(DEFAULT_QUERY_STATE);
|
||||
const getAllQueryResult = useReadContract(getAllAuctions, {
|
||||
@@ -34,6 +37,7 @@ export const EnglishAuctionsTable: React.FC<EnglishAuctionsTableProps> = ({
|
||||
|
||||
return (
|
||||
<MarketplaceTable
|
||||
twAccount={twAccount}
|
||||
contract={contract}
|
||||
getAllQueryResult={getAllQueryResult}
|
||||
getValidQueryResult={getValidQueryResult}
|
||||
|
||||
+15
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../../_utils/getContractPageMetadata";
|
||||
import { ContractEnglishAuctionsPage } from "./ContractEnglishAuctionsPage";
|
||||
@@ -18,8 +19,15 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const twAccount = await getRawAccount();
|
||||
|
||||
if (info.chainMetadata.chainId === localhost.id) {
|
||||
return <ContractEnglishAuctionsPageClient contract={info.contract} />;
|
||||
return (
|
||||
<ContractEnglishAuctionsPageClient
|
||||
contract={info.contract}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isEnglishAuctionSupported } = await getContractPageMetadata(
|
||||
@@ -30,5 +38,10 @@ export default async function Page(props: {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <ContractEnglishAuctionsPage contract={info.contract} />;
|
||||
return (
|
||||
<ContractEnglishAuctionsPage
|
||||
contract={info.contract}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+5
@@ -3,6 +3,7 @@
|
||||
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,
|
||||
@@ -186,6 +187,7 @@ interface ClaimConditionsFormProps {
|
||||
isColumn?: true;
|
||||
isErc20: boolean;
|
||||
isMultiPhase: boolean;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
|
||||
@@ -194,6 +196,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
|
||||
isColumn,
|
||||
isErc20,
|
||||
isMultiPhase,
|
||||
twAccount,
|
||||
}) => {
|
||||
// assume 1155 if we have a tokenId
|
||||
const isErc1155 = tokenId !== undefined;
|
||||
@@ -627,6 +630,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
|
||||
|
||||
{controlledFields.some((field) => field.fromSdk) && (
|
||||
<ResetClaimEligibility
|
||||
twAccount={twAccount}
|
||||
isErc20={isErc20}
|
||||
contract={contract}
|
||||
tokenId={tokenId}
|
||||
@@ -646,6 +650,7 @@ export const ClaimConditionsForm: React.FC<ClaimConditionsFormProps> = ({
|
||||
hasRemovedPhases ||
|
||||
!isMultiPhase ? (
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
disabled={claimConditionsQuery.isPending}
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { ClaimConditionsForm } from "./claim-conditions-form/index";
|
||||
|
||||
@@ -8,12 +9,14 @@ interface ClaimConditionsProps {
|
||||
tokenId?: string;
|
||||
isColumn?: true;
|
||||
isERC20: boolean;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
export const ClaimConditions: React.FC<ClaimConditionsProps> = ({
|
||||
contract,
|
||||
tokenId,
|
||||
isColumn,
|
||||
isERC20,
|
||||
twAccount,
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-6">
|
||||
@@ -29,6 +32,7 @@ export const ClaimConditions: React.FC<ClaimConditionsProps> = ({
|
||||
|
||||
{/* Set Claim Conditions */}
|
||||
<ClaimConditionsForm
|
||||
twAccount={twAccount}
|
||||
isErc20={isERC20}
|
||||
contract={contract}
|
||||
tokenId={tokenId}
|
||||
|
||||
+4
@@ -2,6 +2,7 @@
|
||||
|
||||
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 { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { useTxNotifications } from "hooks/useTxNotifications";
|
||||
@@ -16,12 +17,14 @@ interface ResetClaimEligibilityProps {
|
||||
isErc20: boolean;
|
||||
contract: ThirdwebContract;
|
||||
tokenId?: string;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
|
||||
contract,
|
||||
tokenId,
|
||||
isErc20,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
|
||||
@@ -89,6 +92,7 @@ export const ResetClaimEligibility: React.FC<ResetClaimEligibilityProps> = ({
|
||||
return (
|
||||
<AdminOnly contract={contract} fallback={<div className="pb-5" />}>
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
transactionCount={1}
|
||||
type="button"
|
||||
isPending={sendTxMutation.isPending}
|
||||
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
"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,6 +11,7 @@ import { AccountPage } from "./AccountPage";
|
||||
export function AccountPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
chainMetadata: ChainMetadata;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -29,6 +31,7 @@ export function AccountPageClient(props: {
|
||||
<AccountPage
|
||||
contract={props.contract}
|
||||
chainMetadata={props.chainMetadata}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -10,11 +11,13 @@ import { NftsOwned } from "./components/nfts-owned";
|
||||
interface AccountPageProps {
|
||||
contract: ThirdwebContract;
|
||||
chainMetadata: ChainMetadata;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const AccountPage: React.FC<AccountPageProps> = ({
|
||||
contract,
|
||||
chainMetadata,
|
||||
twAccount,
|
||||
}) => {
|
||||
const symbol = chainMetadata.nativeCurrency.symbol || "Native Token";
|
||||
|
||||
@@ -30,6 +33,7 @@ export const AccountPage: React.FC<AccountPageProps> = ({
|
||||
|
||||
{chainMetadata && (
|
||||
<DepositNative
|
||||
twAccount={twAccount}
|
||||
address={contract.address}
|
||||
symbol={symbol}
|
||||
chain={chainMetadata}
|
||||
|
||||
+4
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useThirdwebClient } from "@/constants/thirdweb.client";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { Input } from "@chakra-ui/react";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import { useV5DashboardChain } from "lib/v5-adapter";
|
||||
@@ -14,12 +15,14 @@ interface DepositNativeProps {
|
||||
address: string;
|
||||
symbol: string;
|
||||
chain: StoredChain;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const DepositNative: React.FC<DepositNativeProps> = ({
|
||||
address,
|
||||
symbol,
|
||||
chain,
|
||||
twAccount,
|
||||
}) => {
|
||||
const client = useThirdwebClient();
|
||||
const { mutate: transfer, isPending } = useSendAndConfirmTransaction();
|
||||
@@ -46,6 +49,7 @@ export const DepositNative: React.FC<DepositNativeProps> = ({
|
||||
value={amount}
|
||||
/>
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={v5Chain.id}
|
||||
transactionCount={1}
|
||||
isPending={isPending}
|
||||
|
||||
+16
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
import { AccountPage } from "./AccountPage";
|
||||
@@ -19,9 +20,16 @@ export default async function Page(props: {
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
return (
|
||||
<AccountPageClient contract={contract} chainMetadata={chainMetadata} />
|
||||
<AccountPageClient
|
||||
contract={contract}
|
||||
chainMetadata={chainMetadata}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,5 +39,11 @@ export default async function Page(props: {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <AccountPage contract={contract} chainMetadata={chainMetadata} />;
|
||||
return (
|
||||
<AccountPage
|
||||
contract={contract}
|
||||
chainMetadata={chainMetadata}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { AccountsPage } from "./AccountsPage";
|
||||
|
||||
export function AccountsPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -23,5 +25,5 @@ export function AccountsPageClient(props: {
|
||||
return <RedirectToContractOverview contract={props.contract} />;
|
||||
}
|
||||
|
||||
return <AccountsPage contract={props.contract} />;
|
||||
return <AccountsPage contract={props.contract} twAccount={props.twAccount} />;
|
||||
}
|
||||
|
||||
+7
-2
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -9,9 +10,13 @@ import { CreateAccountButton } from "./components/create-account-button";
|
||||
|
||||
interface AccountsPageProps {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const AccountsPage: React.FC<AccountsPageProps> = ({ contract }) => {
|
||||
export const AccountsPage: React.FC<AccountsPageProps> = ({
|
||||
contract,
|
||||
twAccount,
|
||||
}) => {
|
||||
return (
|
||||
<Flex direction="column" gap={6}>
|
||||
<Flex
|
||||
@@ -35,7 +40,7 @@ export const AccountsPage: React.FC<AccountsPageProps> = ({ contract }) => {
|
||||
>
|
||||
View Documentation
|
||||
</TrackedLinkButton>
|
||||
<CreateAccountButton contract={contract} />
|
||||
<CreateAccountButton contract={contract} twAccount={twAccount} />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
<AccountsCount contract={contract} />
|
||||
|
||||
+4
@@ -2,6 +2,7 @@
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { Tooltip } from "@chakra-ui/react";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
@@ -14,10 +15,12 @@ import {
|
||||
|
||||
interface CreateAccountButtonProps {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
|
||||
contract,
|
||||
twAccount,
|
||||
...restButtonProps
|
||||
}) => {
|
||||
const sendTxMutation = useSendAndConfirmTransaction();
|
||||
@@ -72,6 +75,7 @@ export const CreateAccountButton: React.FC<CreateAccountButtonProps> = ({
|
||||
|
||||
return (
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
onClick={() => {
|
||||
const tx = ERC4337Ext.createAccount({
|
||||
|
||||
+5
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
import { AccountsPage } from "./AccountsPage";
|
||||
@@ -20,8 +21,10 @@ export default async function Page(props: {
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (chainMetadata.chainId === localhost.id) {
|
||||
return <AccountsPageClient contract={contract} />;
|
||||
return <AccountsPageClient contract={contract} twAccount={account} />;
|
||||
}
|
||||
|
||||
const { isAccountFactory } = await getContractPageMetadata(contract);
|
||||
@@ -30,5 +33,5 @@ export default async function Page(props: {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <AccountsPage contract={contract} />;
|
||||
return <AccountsPage contract={contract} twAccount={account} />;
|
||||
}
|
||||
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
|
||||
|
||||
export function ClaimConditionsClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -33,6 +35,7 @@ export function ClaimConditionsClient(props: {
|
||||
<ClaimConditions
|
||||
contract={props.contract}
|
||||
isERC20={supportedERCs.isERC20}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { ClaimConditions } from "../_components/claim-conditions/claim-conditions";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
@@ -19,8 +20,10 @@ export default async function Page(props: {
|
||||
}
|
||||
const { contract, chainMetadata } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (chainMetadata.chainId === localhost.id) {
|
||||
return <ClaimConditionsClient contract={contract} />;
|
||||
return <ClaimConditionsClient contract={contract} twAccount={account} />;
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -34,6 +37,10 @@ export default async function Page(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<ClaimConditions contract={contract} isERC20={supportedERCs.isERC20} />
|
||||
<ClaimConditions
|
||||
contract={contract}
|
||||
isERC20={supportedERCs.isERC20}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { EmbedSetup } from "./embed-setup";
|
||||
|
||||
export function EmbedSetupClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -27,6 +29,7 @@ export function EmbedSetupClient(props: {
|
||||
<EmbedSetup
|
||||
contract={props.contract}
|
||||
ercOrMarketplace={metadataQuery.data.embedType}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+11
-2
@@ -2,7 +2,11 @@
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CodeClient } from "@/components/ui/code/code.client";
|
||||
import { useApiKeys, useCreateApiKey } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import {
|
||||
type Account,
|
||||
useApiKeys,
|
||||
useCreateApiKey,
|
||||
} from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { Flex, FormControl, Input, Select } from "@chakra-ui/react";
|
||||
import { LazyCreateAPIKeyDialog } from "components/settings/ApiKeys/Create/LazyCreateAPIKeyDialog";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
@@ -22,6 +26,7 @@ import { Card, FormHelperText, FormLabel, Heading, Text } from "tw-components";
|
||||
interface EmbedSetupProps {
|
||||
contract: ThirdwebContract;
|
||||
ercOrMarketplace: string;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
// MAKE SURE THIS IS v1 embed hashes!!
|
||||
@@ -203,10 +208,13 @@ function configureChain(
|
||||
export const EmbedSetup: React.FC<EmbedSetupProps> = ({
|
||||
contract,
|
||||
ercOrMarketplace,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
|
||||
const apiKeys = useApiKeys();
|
||||
const apiKeys = useApiKeys({
|
||||
isLoggedIn: !!twAccount,
|
||||
});
|
||||
const createKeyMutation = useCreateApiKey();
|
||||
|
||||
const validApiKey = (apiKeys.data || []).find(
|
||||
@@ -313,6 +321,7 @@ export const EmbedSetup: React.FC<EmbedSetupProps> = ({
|
||||
});
|
||||
apiKeys.refetch();
|
||||
}}
|
||||
enableNebulaServiceByDefault={false}
|
||||
/>
|
||||
|
||||
<Flex gap={8} direction={{ base: "column", md: "row" }}>
|
||||
|
||||
+11
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
import { EmbedSetupClient } from "./EmbedSetup.client";
|
||||
@@ -18,9 +19,11 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
const { contract } = info;
|
||||
if (contract.chain.id === localhost.id) {
|
||||
return <EmbedSetupClient contract={contract} />;
|
||||
return <EmbedSetupClient contract={contract} twAccount={account} />;
|
||||
}
|
||||
|
||||
const { embedType } = await getContractPageMetadata(contract);
|
||||
@@ -29,5 +32,11 @@ export default async function Page(props: {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <EmbedSetup contract={contract} ercOrMarketplace={embedType} />;
|
||||
return (
|
||||
<EmbedSetup
|
||||
contract={contract}
|
||||
ercOrMarketplace={embedType}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -9,11 +10,13 @@ import { ContractExplorerPage } from "./ContractExplorerPage";
|
||||
interface ContractExplorePageProps {
|
||||
contract: ThirdwebContract;
|
||||
chainMetadata: ChainMetadata;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ContractExplorerPageClient: React.FC<ContractExplorePageProps> = ({
|
||||
contract,
|
||||
chainMetadata,
|
||||
twAccount,
|
||||
}) => {
|
||||
const abiQuery = useResolveContractABI(contract);
|
||||
|
||||
@@ -26,6 +29,7 @@ export const ContractExplorerPageClient: React.FC<ContractExplorePageProps> = ({
|
||||
abi={abiQuery.data}
|
||||
contract={contract}
|
||||
chainMetadata={chainMetadata}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+4
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -10,12 +11,14 @@ interface ContractExplorePageProps {
|
||||
contract: ThirdwebContract;
|
||||
abi: Abi | undefined;
|
||||
chainMetadata: ChainMetadata;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ContractExplorerPage: React.FC<ContractExplorePageProps> = ({
|
||||
contract,
|
||||
abi,
|
||||
chainMetadata,
|
||||
twAccount,
|
||||
}) => {
|
||||
if (!abi) {
|
||||
return (
|
||||
@@ -38,6 +41,7 @@ export const ContractExplorerPage: React.FC<ContractExplorePageProps> = ({
|
||||
onlyFunctions
|
||||
functions={functions}
|
||||
contract={contract}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-2 text-center">
|
||||
|
||||
+5
@@ -1,6 +1,7 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { resolveContractAbi } from "thirdweb/contract";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { ContractExplorerPage } from "./ContractExplorerPage";
|
||||
import { ContractExplorerPageClient } from "./ContractExplorerPage.client";
|
||||
@@ -20,11 +21,14 @@ export default async function Page(props: {
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
return (
|
||||
<ContractExplorerPageClient
|
||||
contract={contract}
|
||||
chainMetadata={chainMetadata}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -36,6 +40,7 @@ export default async function Page(props: {
|
||||
contract={contract}
|
||||
abi={abi}
|
||||
chainMetadata={chainMetadata}
|
||||
twAccount={account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+8
-1
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { ContractEditModulesPage } from "./ContractEditModulesPage";
|
||||
|
||||
export function ContractEditModulesPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -23,5 +25,10 @@ export function ContractEditModulesPageClient(props: {
|
||||
return <RedirectToContractOverview contract={props.contract} />;
|
||||
}
|
||||
|
||||
return <ContractEditModulesPage contract={props.contract} />;
|
||||
return (
|
||||
<ContractEditModulesPage
|
||||
contract={props.contract}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+5
-1
@@ -2,6 +2,7 @@
|
||||
|
||||
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";
|
||||
@@ -11,11 +12,12 @@ import { InstallModuleForm } from "./components/ModuleForm";
|
||||
|
||||
interface ContractEditModulesPageProps {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ContractEditModulesPage: React.FC<
|
||||
ContractEditModulesPageProps
|
||||
> = ({ contract }) => {
|
||||
> = ({ contract, twAccount }) => {
|
||||
const account = useActiveAccount();
|
||||
|
||||
const installedModulesQuery = useReadContract(getInstalledModules, {
|
||||
@@ -66,6 +68,7 @@ export const ContractEditModulesPage: React.FC<
|
||||
</div>
|
||||
<div className="h-10" />
|
||||
<InstallModuleForm
|
||||
twAccount={twAccount}
|
||||
contract={contract}
|
||||
refetchModules={() => installedModulesQuery.refetch()}
|
||||
account={account}
|
||||
@@ -97,6 +100,7 @@ export const ContractEditModulesPage: React.FC<
|
||||
refetchModules={() => installedModulesQuery.refetch()}
|
||||
contract={contract}
|
||||
ownerAccount={isOwner ? account : undefined}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
+5
@@ -16,6 +16,7 @@ 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";
|
||||
@@ -105,6 +106,7 @@ export function BatchMetadataModuleUI(
|
||||
isOwnerAccount: boolean;
|
||||
uploadMetadata: (values: UploadMetadataFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
},
|
||||
) {
|
||||
return (
|
||||
@@ -119,6 +121,7 @@ export function BatchMetadataModuleUI(
|
||||
<AccordionContent className="px-1">
|
||||
{props.isOwnerAccount && (
|
||||
<UploadMetadataNFTSection
|
||||
twAccount={props.twAccount}
|
||||
uploadMetadata={props.uploadMetadata}
|
||||
contractChainId={props.contractChainId}
|
||||
/>
|
||||
@@ -162,6 +165,7 @@ export function BatchMetadataModuleUI(
|
||||
function UploadMetadataNFTSection(props: {
|
||||
uploadMetadata: (values: UploadMetadataFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<UploadMetadataFormValues>({
|
||||
resolver: zodResolver(uploadMetadataFormSchema),
|
||||
@@ -260,6 +264,7 @@ function UploadMetadataNFTSection(props: {
|
||||
|
||||
<div className="flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24"
|
||||
disabled={uploadMetadataMutation.isPending}
|
||||
|
||||
+11
@@ -23,6 +23,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ToolTipLabel } from "@/components/ui/tooltip";
|
||||
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";
|
||||
@@ -323,6 +324,7 @@ export function ClaimableModuleUI(
|
||||
| undefined;
|
||||
isLoading: boolean;
|
||||
};
|
||||
twAccount: Account | undefined;
|
||||
},
|
||||
) {
|
||||
return (
|
||||
@@ -341,6 +343,7 @@ export function ClaimableModuleUI(
|
||||
mint={props.mintSection.mint}
|
||||
name={props.name}
|
||||
contractChainId={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
@@ -369,6 +372,7 @@ export function ClaimableModuleUI(
|
||||
{props.name !== "ClaimableERC1155" || props.isValidTokenId ? (
|
||||
props.claimConditionSection.data ? (
|
||||
<ClaimConditionSection
|
||||
twAccount={props.twAccount}
|
||||
isOwnerAccount={props.isOwnerAccount}
|
||||
claimCondition={
|
||||
props.claimConditionSection.data.claimCondition
|
||||
@@ -402,6 +406,7 @@ export function ClaimableModuleUI(
|
||||
<AccordionContent className="px-1">
|
||||
{props.primarySaleRecipientSection.data ? (
|
||||
<PrimarySaleRecipientSection
|
||||
twAccount={props.twAccount}
|
||||
isOwnerAccount={props.isOwnerAccount}
|
||||
primarySaleRecipient={
|
||||
props.primarySaleRecipientSection.data.primarySaleRecipient
|
||||
@@ -466,6 +471,7 @@ function ClaimConditionSection(props: {
|
||||
tokenDecimals?: number | undefined;
|
||||
tokenId: string;
|
||||
noClaimConditionSet: boolean;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const { idToChain } = useAllChainsData();
|
||||
const chain = idToChain.get(props.chainId);
|
||||
@@ -716,6 +722,7 @@ function ClaimConditionSection(props: {
|
||||
|
||||
<div className="flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24"
|
||||
disabled={updateMutation.isPending || !props.isOwnerAccount}
|
||||
@@ -748,6 +755,7 @@ function PrimarySaleRecipientSection(props: {
|
||||
update: (values: PrimarySaleRecipientFormValues) => Promise<void>;
|
||||
isOwnerAccount: boolean;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<PrimarySaleRecipientFormValues>({
|
||||
resolver: zodResolver(primarySaleRecipientFormSchema),
|
||||
@@ -796,6 +804,7 @@ function PrimarySaleRecipientSection(props: {
|
||||
|
||||
<div className="flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24 gap-2"
|
||||
disabled={
|
||||
@@ -830,6 +839,7 @@ function MintNFTSection(props: {
|
||||
mint: (values: MintFormValues) => Promise<void>;
|
||||
name: string;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<MintFormValues>({
|
||||
resolver: zodResolver(mintFormSchema),
|
||||
@@ -918,6 +928,7 @@ function MintNFTSection(props: {
|
||||
isPending={mintMutation.isPending}
|
||||
txChainID={props.contractChainId}
|
||||
transactionCount={1}
|
||||
twAccount={props.twAccount}
|
||||
>
|
||||
Mint
|
||||
</TransactionButton>
|
||||
|
||||
+3
@@ -2,6 +2,7 @@
|
||||
|
||||
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 { ContractOptions } from "thirdweb";
|
||||
import type { Account } from "thirdweb/wallets";
|
||||
@@ -16,6 +17,7 @@ export const InstalledModulesTable = (props: {
|
||||
};
|
||||
refetchModules: () => void;
|
||||
ownerAccount: Account | undefined;
|
||||
twAccount: TWAccount | undefined;
|
||||
}) => {
|
||||
const { installedModules, ownerAccount } = props;
|
||||
|
||||
@@ -57,6 +59,7 @@ export const InstalledModulesTable = (props: {
|
||||
contract={props.contract}
|
||||
onRemoveModule={props.refetchModules}
|
||||
ownerAccount={ownerAccount}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
+8
@@ -20,6 +20,7 @@ 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";
|
||||
@@ -194,6 +195,7 @@ export function MintableModuleUI(
|
||||
name: string;
|
||||
isBatchMetadataInstalled: boolean;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
},
|
||||
) {
|
||||
return (
|
||||
@@ -212,6 +214,7 @@ export function MintableModuleUI(
|
||||
<AccordionContent className="px-1">
|
||||
{props.isOwnerAccount && (
|
||||
<MintNFTSection
|
||||
twAccount={props.twAccount}
|
||||
mint={props.mint}
|
||||
name={props.name}
|
||||
isBatchMetadataInstalled={props.isBatchMetadataInstalled}
|
||||
@@ -242,6 +245,7 @@ export function MintableModuleUI(
|
||||
primarySaleRecipient={props.primarySaleRecipient}
|
||||
update={props.updatePrimaryRecipient}
|
||||
contractChainId={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
@@ -261,6 +265,7 @@ function PrimarySalesSection(props: {
|
||||
update: (values: UpdateFormValues) => Promise<void>;
|
||||
isOwnerAccount: boolean;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<UpdateFormValues>({
|
||||
resolver: zodResolver(primarySaleRecipientFormSchema),
|
||||
@@ -310,6 +315,7 @@ function PrimarySalesSection(props: {
|
||||
|
||||
<div className="mt-4 flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24"
|
||||
disabled={updateMutation.isPending || !props.isOwnerAccount}
|
||||
@@ -339,6 +345,7 @@ function MintNFTSection(props: {
|
||||
name: string;
|
||||
isBatchMetadataInstalled: boolean;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<MintFormValues>({
|
||||
resolver: zodResolver(mintFormSchema),
|
||||
@@ -524,6 +531,7 @@ function MintNFTSection(props: {
|
||||
|
||||
<div className="flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24"
|
||||
disabled={mintMutation.isPending}
|
||||
|
||||
+3
@@ -2,6 +2,7 @@
|
||||
|
||||
import { Spinner } from "@/components/ui/Spinner/Spinner";
|
||||
import { useThirdwebClient } from "@/constants/thirdweb.client";
|
||||
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { FormControl, Input, Select, Skeleton, Spacer } from "@chakra-ui/react";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
@@ -53,6 +54,7 @@ type InstallModuleFormProps = {
|
||||
data?: string[];
|
||||
isPending: boolean;
|
||||
};
|
||||
twAccount: TWAccount | undefined;
|
||||
};
|
||||
|
||||
export type InstallModuleForm = UseFormReturn<FormData>;
|
||||
@@ -383,6 +385,7 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
|
||||
{/* Submit */}
|
||||
<div className="flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
isPending={installMutation.isPending}
|
||||
|
||||
+5
@@ -16,6 +16,7 @@ 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";
|
||||
@@ -82,6 +83,7 @@ export function OpenEditionMetadataModuleUI(
|
||||
isOwnerAccount: boolean;
|
||||
setSharedMetadata: (values: SetSharedMetadataFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
},
|
||||
) {
|
||||
return (
|
||||
@@ -97,6 +99,7 @@ export function OpenEditionMetadataModuleUI(
|
||||
<SetSharedMetadataSection
|
||||
setSharedMetadata={props.setSharedMetadata}
|
||||
contractChainId={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
)}
|
||||
{!props.isOwnerAccount && (
|
||||
@@ -118,6 +121,7 @@ export function OpenEditionMetadataModuleUI(
|
||||
function SetSharedMetadataSection(props: {
|
||||
setSharedMetadata: (values: SetSharedMetadataFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<SetSharedMetadataFormValues>({
|
||||
resolver: zodResolver(setSharedMetadataFormSchema),
|
||||
@@ -215,6 +219,7 @@ function SetSharedMetadataSection(props: {
|
||||
isPending={setSharedMetadataMutation.isPending}
|
||||
transactionCount={1}
|
||||
txChainID={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
>
|
||||
Update
|
||||
</TransactionButton>
|
||||
|
||||
+11
@@ -16,6 +16,7 @@ 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";
|
||||
@@ -166,6 +167,7 @@ export function RoyaltyModuleUI(
|
||||
) => Promise<void>;
|
||||
setRoyaltyInfoForToken: (values: RoyaltyInfoFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
},
|
||||
) {
|
||||
return (
|
||||
@@ -184,6 +186,7 @@ export function RoyaltyModuleUI(
|
||||
<RoyaltyInfoPerTokenSection
|
||||
setRoyaltyInfoForToken={props.setRoyaltyInfoForToken}
|
||||
contractChainId={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
) : (
|
||||
<Alert variant="info">
|
||||
@@ -206,6 +209,7 @@ export function RoyaltyModuleUI(
|
||||
update={props.setDefaultRoyaltyInfo}
|
||||
defaultRoyaltyInfo={props.defaultRoyaltyInfo}
|
||||
contractChainId={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
) : (
|
||||
<Alert variant="info">
|
||||
@@ -228,6 +232,7 @@ export function RoyaltyModuleUI(
|
||||
update={props.setTransferValidator}
|
||||
transferValidator={props.transferValidator}
|
||||
contractChainId={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
) : (
|
||||
<Alert variant="info">
|
||||
@@ -264,6 +269,7 @@ export type RoyaltyInfoFormValues = z.infer<typeof royaltyInfoFormSchema>;
|
||||
function RoyaltyInfoPerTokenSection(props: {
|
||||
setRoyaltyInfoForToken: (values: RoyaltyInfoFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<RoyaltyInfoFormValues>({
|
||||
resolver: zodResolver(royaltyInfoFormSchema),
|
||||
@@ -343,6 +349,7 @@ function RoyaltyInfoPerTokenSection(props: {
|
||||
|
||||
<div className="mt-4 flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24"
|
||||
disabled={setRoyaltyInfoForTokenMutation.isPending}
|
||||
@@ -376,6 +383,7 @@ function DefaultRoyaltyInfoSection(props: {
|
||||
defaultRoyaltyInfo?: readonly [string, number];
|
||||
update: (values: DefaultRoyaltyFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const [defaultRoyaltyRecipient, defaultRoyaltyPercentage] =
|
||||
props.defaultRoyaltyInfo || [];
|
||||
@@ -452,6 +460,7 @@ function DefaultRoyaltyInfoSection(props: {
|
||||
transactionCount={1}
|
||||
isPending={updateMutation.isPending}
|
||||
txChainID={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
>
|
||||
Update
|
||||
</TransactionButton>
|
||||
@@ -474,6 +483,7 @@ function TransferValidatorSection(props: {
|
||||
transferValidator: string | undefined;
|
||||
update: (values: TransferValidatorFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const form = useForm<TransferValidatorFormValues>({
|
||||
resolver: zodResolver(transferValidatorFormSchema),
|
||||
@@ -521,6 +531,7 @@ function TransferValidatorSection(props: {
|
||||
|
||||
<div className="mt-4 flex justify-end">
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
size="sm"
|
||||
className="min-w-24 gap-2"
|
||||
disabled={updateMutation.isPending}
|
||||
|
||||
+3
@@ -14,6 +14,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { ToolTipLabel } from "@/components/ui/tooltip";
|
||||
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";
|
||||
@@ -136,6 +137,7 @@ export function TransferableModuleUI(
|
||||
isOwnerAccount: boolean;
|
||||
update: (values: TransferableModuleFormValues) => Promise<void>;
|
||||
contractChainId: number;
|
||||
twAccount: Account | undefined;
|
||||
},
|
||||
) {
|
||||
const form = useForm<TransferableModuleFormValues>({
|
||||
@@ -198,6 +200,7 @@ export function TransferableModuleUI(
|
||||
(props.isRestricted ? 0 : 1) + allowListLength
|
||||
}
|
||||
txChainID={props.contractChainId}
|
||||
twAccount={props.twAccount}
|
||||
>
|
||||
Update
|
||||
</TransactionButton>
|
||||
|
||||
+2
@@ -11,6 +11,7 @@ import {
|
||||
ErrorProvider,
|
||||
type TransactionError,
|
||||
} from "../../../../../../../contexts/error-handler";
|
||||
import { accountStub } from "../../../../../../../stories/stubs";
|
||||
import {
|
||||
BatchMetadataModuleUI,
|
||||
type UploadMetadataFormValues,
|
||||
@@ -96,6 +97,7 @@ function Component() {
|
||||
|
||||
<BadgeContainer label="Default">
|
||||
<BatchMetadataModuleUI
|
||||
twAccount={accountStub()}
|
||||
contractInfo={contractInfo}
|
||||
moduleAddress="0x0000000000000000000000000000000000000000"
|
||||
uploadMetadata={uploadMetadataStub}
|
||||
|
||||
+2
@@ -16,6 +16,7 @@ import { mobileViewport } from "stories/utils";
|
||||
import { NATIVE_TOKEN_ADDRESS, ZERO_ADDRESS } from "thirdweb";
|
||||
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
|
||||
import { checksumAddress } from "thirdweb/utils";
|
||||
import { accountStub } from "../../../../../../../stories/stubs";
|
||||
import {
|
||||
type ClaimConditionFormValues,
|
||||
type ClaimConditionValue,
|
||||
@@ -156,6 +157,7 @@ function Component() {
|
||||
</div>
|
||||
|
||||
<ClaimableModuleUI
|
||||
twAccount={accountStub()}
|
||||
contractInfo={contractInfo}
|
||||
moduleAddress="0x0000000000000000000000000000000000000000"
|
||||
primarySaleRecipientSection={{
|
||||
|
||||
+6
@@ -14,6 +14,7 @@ import { useState } from "react";
|
||||
import { Toaster, toast } from "sonner";
|
||||
import { BadgeContainer, mobileViewport } from "stories/utils";
|
||||
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
|
||||
import { accountStub } from "../../../../../../../stories/stubs";
|
||||
import {
|
||||
type MintFormValues,
|
||||
MintableModuleUI,
|
||||
@@ -79,6 +80,8 @@ function Component() {
|
||||
version: "1.0.0",
|
||||
};
|
||||
|
||||
const twAccount = accountStub();
|
||||
|
||||
return (
|
||||
<ThirdwebProvider>
|
||||
<div className="container flex max-w-[1150px] flex-col gap-10 py-10">
|
||||
@@ -115,6 +118,7 @@ function Component() {
|
||||
|
||||
<BadgeContainer label="Empty Primary Sale Recipient">
|
||||
<MintableModuleUI
|
||||
twAccount={twAccount}
|
||||
contractInfo={contractInfo}
|
||||
moduleAddress="0x0000000000000000000000000000000000000000"
|
||||
isPending={false}
|
||||
@@ -134,6 +138,7 @@ function Component() {
|
||||
|
||||
<BadgeContainer label="Filled Primary Sale Recipient">
|
||||
<MintableModuleUI
|
||||
twAccount={twAccount}
|
||||
contractInfo={contractInfo}
|
||||
moduleAddress="0x0000000000000000000000000000000000000000"
|
||||
isPending={false}
|
||||
@@ -153,6 +158,7 @@ function Component() {
|
||||
|
||||
<BadgeContainer label="Pending">
|
||||
<MintableModuleUI
|
||||
twAccount={twAccount}
|
||||
contractInfo={contractInfo}
|
||||
moduleAddress="0x0000000000000000000000000000000000000000"
|
||||
isPending={true}
|
||||
|
||||
+4
@@ -14,6 +14,7 @@ import {
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { TransactionButton } from "components/buttons/TransactionButton";
|
||||
@@ -43,6 +44,7 @@ type ModuleCardProps = {
|
||||
version?: string;
|
||||
publisher?: string;
|
||||
}[];
|
||||
twAccount: TWAccount | undefined;
|
||||
};
|
||||
|
||||
export function ModuleCard(props: ModuleCardProps) {
|
||||
@@ -106,6 +108,7 @@ export function ModuleCard(props: ModuleCardProps) {
|
||||
)}
|
||||
>
|
||||
<ModuleInstance
|
||||
twAccount={props.twAccount}
|
||||
contract={contract}
|
||||
contractInfo={{
|
||||
name: contractInfo.name,
|
||||
@@ -158,6 +161,7 @@ export function ModuleCard(props: ModuleCardProps) {
|
||||
</Button>
|
||||
|
||||
<TransactionButton
|
||||
twAccount={props.twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
isPending={uninstallMutation.isPending}
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { Account as TWAccount } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { lazy } from "react";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import type { Account } from "thirdweb/wallets";
|
||||
@@ -24,6 +25,7 @@ export type ModuleInstanceProps = Omit<
|
||||
version?: string;
|
||||
publisher?: string;
|
||||
}[];
|
||||
twAccount: TWAccount | undefined;
|
||||
};
|
||||
|
||||
export function ModuleInstance(props: ModuleInstanceProps) {
|
||||
|
||||
+2
@@ -6,6 +6,7 @@ import { useState } from "react";
|
||||
import { Toaster, toast } from "sonner";
|
||||
import { BadgeContainer, mobileViewport } from "stories/utils";
|
||||
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
|
||||
import { accountStub } from "../../../../../../../stories/stubs";
|
||||
import {
|
||||
OpenEditionMetadataModuleUI,
|
||||
type SetSharedMetadataFormValues,
|
||||
@@ -94,6 +95,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={accountStub()}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
|
||||
+7
@@ -6,6 +6,7 @@ import { useState } from "react";
|
||||
import { Toaster, toast } from "sonner";
|
||||
import { BadgeContainer, mobileViewport } from "stories/utils";
|
||||
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
|
||||
import { accountStub } from "../../../../../../../stories/stubs";
|
||||
import {
|
||||
type DefaultRoyaltyFormValues,
|
||||
type RoyaltyInfoFormValues,
|
||||
@@ -74,6 +75,8 @@ function Component() {
|
||||
version: "1.0.0",
|
||||
};
|
||||
|
||||
const twAccount = accountStub();
|
||||
|
||||
return (
|
||||
<ThirdwebProvider>
|
||||
<div className="container flex max-w-[1150px] flex-col gap-10 py-10">
|
||||
@@ -104,6 +107,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
@@ -122,6 +126,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
@@ -140,6 +145,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
@@ -159,6 +165,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
|
||||
+4
@@ -6,6 +6,7 @@ import { useState } from "react";
|
||||
import { Toaster, toast } from "sonner";
|
||||
import { BadgeContainer, mobileViewport } from "stories/utils";
|
||||
import { ConnectButton, ThirdwebProvider } from "thirdweb/react";
|
||||
import { accountStub } from "../../../../../../../stories/stubs";
|
||||
import {
|
||||
type TransferableModuleFormValues,
|
||||
TransferableModuleUI,
|
||||
@@ -99,6 +100,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={accountStub()}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
@@ -116,6 +118,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={accountStub()}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
@@ -133,6 +136,7 @@ function Component() {
|
||||
}}
|
||||
isOwnerAccount={isOwner}
|
||||
contractChainId={1}
|
||||
twAccount={accountStub()}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
|
||||
|
||||
+7
-2
@@ -1,5 +1,6 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
import { ContractEditModulesPage } from "./ContractEditModulesPage";
|
||||
@@ -20,8 +21,12 @@ export default async function Page(props: {
|
||||
|
||||
const { contract } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
return <ContractEditModulesPageClient contract={contract} />;
|
||||
return (
|
||||
<ContractEditModulesPageClient contract={contract} twAccount={account} />
|
||||
);
|
||||
}
|
||||
|
||||
const { isModularCore } = await getContractPageMetadata(contract);
|
||||
@@ -30,5 +35,5 @@ export default async function Page(props: {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <ContractEditModulesPage contract={contract} />;
|
||||
return <ContractEditModulesPage contract={contract} twAccount={account} />;
|
||||
}
|
||||
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -8,6 +9,7 @@ import { ContractNFTPage } from "./ContractNFTPage";
|
||||
|
||||
export function ContractNFTPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
@@ -30,6 +32,7 @@ export function ContractNFTPageClient(props: {
|
||||
contract={props.contract}
|
||||
isErc721={supportedERCs.isERC721}
|
||||
functionSelectors={functionSelectors}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+22
-5
@@ -1,3 +1,4 @@
|
||||
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";
|
||||
@@ -15,12 +16,14 @@ interface NftOverviewPageProps {
|
||||
isErc721: boolean;
|
||||
tokenId?: string;
|
||||
functionSelectors: string[];
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
|
||||
contract,
|
||||
isErc721,
|
||||
functionSelectors,
|
||||
twAccount,
|
||||
}) => {
|
||||
const isERC721ClaimToSupported =
|
||||
ERC721Ext.isClaimToSupported(functionSelectors);
|
||||
@@ -56,25 +59,39 @@ export const ContractNFTPage: React.FC<NftOverviewPageProps> = ({
|
||||
<div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
|
||||
<h2 className="font-semibold text-2xl tracking-tight">Contract NFTs</h2>
|
||||
<div className="grid grid-cols-2 gap-2 md:flex">
|
||||
{isRevealable && <NFTRevealButton contract={contract} />}
|
||||
{isRevealable && (
|
||||
<NFTRevealButton contract={contract} twAccount={twAccount} />
|
||||
)}
|
||||
{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
|
||||
*/
|
||||
<NFTClaimButton contract={contract} />
|
||||
<NFTClaimButton contract={contract} twAccount={twAccount} />
|
||||
)}
|
||||
{isMintToSupported && (
|
||||
<NFTMintButton contract={contract} isErc721={isErc721} />
|
||||
<NFTMintButton
|
||||
contract={contract}
|
||||
isErc721={isErc721}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
)}
|
||||
{isSetSharedMetadataSupported && (
|
||||
<NFTSharedMetadataButton contract={contract} />
|
||||
<NFTSharedMetadataButton
|
||||
contract={contract}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
)}
|
||||
{isLazyMintable && (
|
||||
<NFTLazyMintButton contract={contract} isErc721={isErc721} />
|
||||
<NFTLazyMintButton
|
||||
contract={contract}
|
||||
isErc721={isErc721}
|
||||
twAccount={twAccount}
|
||||
/>
|
||||
)}
|
||||
{isLazyMintable && (
|
||||
<BatchLazyMintButton
|
||||
twAccount={twAccount}
|
||||
canCreateDelayedRevealBatch={canCreateDelayedRevealBatch}
|
||||
isErc721={isErc721}
|
||||
contract={contract}
|
||||
|
||||
+3
@@ -1,5 +1,6 @@
|
||||
"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,6 +10,7 @@ import { TokenIdPage } from "./token-id";
|
||||
export function TokenIdPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
tokenId: string;
|
||||
twAccount: Account | undefined;
|
||||
}) {
|
||||
const { contract } = props;
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
@@ -32,6 +34,7 @@ export function TokenIdPageClient(props: {
|
||||
contract={contract}
|
||||
isErc721={supportedERCs.isERC721}
|
||||
tokenId={props.tokenId}
|
||||
twAccount={props.twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+8
-1
@@ -9,6 +9,7 @@ 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 { UploadIcon } from "lucide-react";
|
||||
@@ -27,12 +28,17 @@ import {
|
||||
interface AirdropTabProps {
|
||||
contract: ThirdwebContract;
|
||||
tokenId: string;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component must only take in ERC1155 contracts
|
||||
*/
|
||||
const AirdropTab: React.FC<AirdropTabProps> = ({ contract, tokenId }) => {
|
||||
const AirdropTab: React.FC<AirdropTabProps> = ({
|
||||
contract,
|
||||
tokenId,
|
||||
twAccount,
|
||||
}) => {
|
||||
const address = useActiveAccount()?.address;
|
||||
const { handleSubmit, setValue, watch, reset } = useForm<{
|
||||
addresses: AirdropAddressInput[];
|
||||
@@ -155,6 +161,7 @@ const AirdropTab: React.FC<AirdropTabProps> = ({ contract, tokenId }) => {
|
||||
more, please do it in multiple transactions.
|
||||
</p>
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
isPending={sendAndConfirmTx.isPending}
|
||||
|
||||
+4
-1
@@ -1,5 +1,6 @@
|
||||
"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 { useTrack } from "hooks/analytics/useTrack";
|
||||
@@ -24,9 +25,10 @@ import {
|
||||
interface BurnTabProps {
|
||||
tokenId: string;
|
||||
contract: ThirdwebContract;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const BurnTab: React.FC<BurnTabProps> = ({ contract, tokenId }) => {
|
||||
const BurnTab: React.FC<BurnTabProps> = ({ contract, tokenId, twAccount }) => {
|
||||
const account = useActiveAccount();
|
||||
const trackEvent = useTrack();
|
||||
const {
|
||||
@@ -135,6 +137,7 @@ const BurnTab: React.FC<BurnTabProps> = ({ contract, tokenId }) => {
|
||||
type="submit"
|
||||
className="self-end"
|
||||
txChainID={contract.chain.id}
|
||||
twAccount={twAccount}
|
||||
>
|
||||
Burn
|
||||
</TransactionButton>
|
||||
|
||||
+8
-1
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -14,9 +15,14 @@ import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
|
||||
interface ClaimTabProps {
|
||||
contract: ThirdwebContract;
|
||||
tokenId: string;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const ClaimTabERC1155: React.FC<ClaimTabProps> = ({ contract, tokenId }) => {
|
||||
const ClaimTabERC1155: React.FC<ClaimTabProps> = ({
|
||||
contract,
|
||||
tokenId,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const address = useActiveAccount()?.address;
|
||||
const form = useForm<{ to: string; amount: string }>({
|
||||
@@ -124,6 +130,7 @@ const ClaimTabERC1155: React.FC<ClaimTabProps> = ({ contract, tokenId }) => {
|
||||
isPending={form.formState.isSubmitting}
|
||||
type="submit"
|
||||
className="self-end"
|
||||
twAccount={twAccount}
|
||||
>
|
||||
Claim
|
||||
</TransactionButton>
|
||||
|
||||
+8
-1
@@ -10,6 +10,7 @@ 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";
|
||||
@@ -23,6 +24,7 @@ import { z } from "zod";
|
||||
interface MintSupplyTabProps {
|
||||
contract: ThirdwebContract;
|
||||
tokenId: string;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const mintAdditionalSupplyFormSchema = z.object({
|
||||
@@ -32,7 +34,11 @@ const mintAdditionalSupplyFormSchema = z.object({
|
||||
amount: z.coerce.number().int().min(1, "Amount must be at least 1"),
|
||||
});
|
||||
|
||||
const MintSupplyTab: React.FC<MintSupplyTabProps> = ({ contract, tokenId }) => {
|
||||
const MintSupplyTab: React.FC<MintSupplyTabProps> = ({
|
||||
contract,
|
||||
tokenId,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const address = useActiveAccount()?.address;
|
||||
|
||||
@@ -126,6 +132,7 @@ const MintSupplyTab: React.FC<MintSupplyTabProps> = ({ contract, tokenId }) => {
|
||||
isPending={sendAndConfirmTx.isPending}
|
||||
type="submit"
|
||||
className="self-end"
|
||||
twAccount={twAccount}
|
||||
>
|
||||
Mint
|
||||
</TransactionButton>
|
||||
|
||||
+9
-1
@@ -1,5 +1,6 @@
|
||||
"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";
|
||||
@@ -15,12 +16,18 @@ import {
|
||||
useSendAndConfirmTransaction,
|
||||
} from "thirdweb/react";
|
||||
import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
|
||||
|
||||
interface TransferTabProps {
|
||||
contract: ThirdwebContract;
|
||||
tokenId: string;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const TransferTab: React.FC<TransferTabProps> = ({ contract, tokenId }) => {
|
||||
const TransferTab: React.FC<TransferTabProps> = ({
|
||||
contract,
|
||||
tokenId,
|
||||
twAccount,
|
||||
}) => {
|
||||
const account = useActiveAccount();
|
||||
|
||||
const trackEvent = useTrack();
|
||||
@@ -119,6 +126,7 @@ const TransferTab: React.FC<TransferTabProps> = ({ contract, tokenId }) => {
|
||||
)}
|
||||
</div>
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
isPending={isPending || checking1155}
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
@@ -52,6 +53,7 @@ type UpdateNftMetadataForm = {
|
||||
* else (NFT Collection, Edition) -> use `setTokenURI`
|
||||
*/
|
||||
useUpdateMetadata: boolean;
|
||||
twAccount: Account | undefined;
|
||||
};
|
||||
|
||||
export const UpdateNftMetadata: React.FC<UpdateNftMetadataForm> = ({
|
||||
@@ -59,6 +61,7 @@ export const UpdateNftMetadata: React.FC<UpdateNftMetadataForm> = ({
|
||||
nft,
|
||||
useUpdateMetadata,
|
||||
setOpen,
|
||||
twAccount,
|
||||
}) => {
|
||||
const trackEvent = useTrack();
|
||||
const address = useActiveAccount()?.address;
|
||||
@@ -386,6 +389,7 @@ export const UpdateNftMetadata: React.FC<UpdateNftMetadataForm> = ({
|
||||
Cancel
|
||||
</Button>
|
||||
<TransactionButton
|
||||
twAccount={twAccount}
|
||||
txChainID={contract.chain.id}
|
||||
transactionCount={1}
|
||||
isPending={sendAndConfirmTx.isPending}
|
||||
|
||||
+4
@@ -6,6 +6,7 @@ import {
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@/components/ui/sheet";
|
||||
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { useState } from "react";
|
||||
import type { NFT, ThirdwebContract } from "thirdweb";
|
||||
import { UpdateNftMetadata } from "./update-metadata-form";
|
||||
@@ -18,12 +19,14 @@ interface UpdateMetadataTabProps {
|
||||
* else (NFT Collection, Edition) -> use `setTokenURI`
|
||||
*/
|
||||
useUpdateMetadata: boolean;
|
||||
twAccount: Account | undefined;
|
||||
}
|
||||
|
||||
const UpdateMetadataTab: React.FC<UpdateMetadataTabProps> = ({
|
||||
contract,
|
||||
nft,
|
||||
useUpdateMetadata,
|
||||
twAccount,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -37,6 +40,7 @@ const UpdateMetadataTab: React.FC<UpdateMetadataTabProps> = ({
|
||||
<SheetTitle className="text-left">Mint NFT</SheetTitle>
|
||||
</SheetHeader>
|
||||
<UpdateNftMetadata
|
||||
twAccount={twAccount}
|
||||
contract={contract}
|
||||
nft={nft}
|
||||
useUpdateMetadata={useUpdateMetadata}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user