Compare commits

..
Author SHA1 Message Date
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
6994310d16 Version Packages (#5741)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-17 01:08:47 +13:00
Joaquim VergesandGitHub e42ffc6a93 Fix migration to enclave wallets in react native (#5746) 2024-12-17 00:27:00 +13:00
gregfromstl 0ddc123807 [Dashboard] Fix: Nebula send transaction params (#5744)
<!-- start pr-codex -->

## PR-Codex overview
This PR enhances the `SendTransactionButton` component in `Chats.tsx` by introducing improved transaction handling and validation through the addition of a blockchain `chain` context and the `sendTransaction` method.

### Detailed summary
- Added import for `sendTransaction` from `thirdweb`.
- Introduced `chain` variable using `useV5DashboardChain`.
- Updated transaction validation to check for both `txData` and `chain`.
- Refactored transaction sending logic to use `sendTransaction` with detailed parameters.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-15 23:38:20 +00:00
gregfromstl f91f6310e9 [CI] Feature: Deploy typedoc to permalink (#5740)
<!-- start pr-codex -->

## PR-Codex overview
This PR introduces a new `TypeDoc` workflow for generating documentation and updates the `parse.mjs` script to process documentation data. It also modifies the `package.json` to include the `typedoc-better-json` dependency and adjusts the `pnpm-lock.yaml` accordingly.

### Detailed summary
- Added `TypeDoc` GitHub Actions workflow in `.github/workflows/typedoc.yml`.
- Created a new `parse.mjs` script in `packages/thirdweb/scripts` for transforming documentation data.
- Updated `package.json` to include `typedoc-better-json` as a dependency.
- Adjusted `pnpm-lock.yaml` to reflect changes in dependencies and versions.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-14 17:43:16 +00:00
gregfromstl dc1e07b5e4 [CI] Chore: Only run actions for contributors (#5739)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR enhances the GitHub workflows for pull requests by adding conditions for author associations and expanding issue types.

### Detailed summary
- In `.github/workflows/auto-assign.yml`, added checks to assign authors only if they are `MEMBER`, `OWNER`, or `COLLABORATOR`.
- In `.github/workflows/issue.yml`, expanded the `types` to include `ready_for_review`.
- Added logic to automatically pass checks for external contributors.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-14 17:38:51 +00:00
kien-ngo 2b5080d901 Playground: Examples for Wallet components (#5692)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new `Wallet` navigation link and implements a new `Wallet` page with components for displaying wallet icons and names, enhancing the user interface for crypto wallet applications.

### Detailed summary
- Added a new navigation link for `Wallet`.
- Created `page.tsx` for the `Wallet` section with metadata and layout.
- Implemented `APIHeader` for the `Wallet` page.
- Added three components: `WalletIconBasic`, `WalletNameBasic`, and `WalletNameFormat` in `wallet-examples.tsx`.
- Each component includes a description and code example for usage.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-14 11:27:08 +00:00
kien-ngo 5be197bac9 [SDK] Headless Wallet components | CNCT-2620 (#5691)
CNCT-2620

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces new headless components for wallet management in the `thirdweb` library, enhancing user interface options for wallet context, icons, and names.

### Detailed summary
- Added `WalletProvider`, `WalletIcon`, and `WalletName` components.
- Updated `ChainProvider` to mark it as `@beta`.
- Introduced utility functions `getQueryKeys` for both chain and wallet components.
- Updated documentation and tests for new wallet components.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-14 10:26:48 +00:00
MananTank 34165cb608 [DASH-616] Nebula - Add Alpha badge in sidebar (#5738)
## Problem solved

DASH-616
2024-12-13 21:33:58 +00:00
MananTank 9954b37b01 [DASH-624] Add tracking in insight playground (#5737)
## Problem solved

DASH-624

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `CodeBlockContainer` and related components to support an `onCopy` callback, allowing for custom actions when code is copied. It also introduces tracking for user interactions within the `BlueprintPlayground` component.

### Detailed summary
- Added `onCopy` prop to `CodeBlockContainer`, `RenderCode`, `PlainTextCodeBlock`, and `CodeClient`.
- Updated button click handler in `CodeBlockContainer` to call `onCopy` and pass the copied code.
- Integrated `useTrack` for tracking user events in `BlueprintPlayground`.
- Added tracking for "run", "copy-url", and "copy-response" actions in relevant components.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 21:10:48 +00:00
MananTank 9b58a086b6 [Dashboard] Nebula - Update context filters + Add OG image (#5736)
## Problem solved

DASH-615

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on enhancing the context filter functionality within the application by introducing support for `walletAddresses`, updating related types, and modifying components to accommodate these changes.

### Detailed summary
- Added `walletAddresses` to various types and API parameters.
- Updated `SessionContextFilter` to include `wallet_addresses`.
- Modified `ChatPageContent` to handle and display `walletAddresses`.
- Enhanced `ContextFiltersButton` to show wallet address badges.
- Updated form handling to include validation for `walletAddresses`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 20:08:49 +00:00
gregandGitHub 22bc557090 [CI] Chore: Update allowed teams (#5735) 2024-12-13 10:31:00 -08:00
jnsdls 80b54a937c use resolved ENS name if available (#5734)
fixes: DASH-572
2024-12-13 18:14:08 +00:00
Will CoryandGitHub be6433dcf2 docs: Add more context on guest mode (#5733)
Signed-off-by: Will Cory <[email protected]>
2024-12-13 09:42:05 -08:00
MananTank f6c040719c [DASH-612] Nebula: Add title and description meta (#5732)
DASH-612
2024-12-13 17:11:58 +00:00
MananTank 2db85824f0 [DASH-610] Fix useActiveWalletChain missing chain overrides added in dashboard (#5731)
Fixes: DASH-610
2024-12-13 16:59:01 +00:00
kien-ngo 498a26ebbc [Playground] Chain components (#5678)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces new `Chain` components to enhance the user interface for blockchain applications, allowing for the display of chain icons and names. It includes new pages and components that integrate these features into the existing playground web application.

### Detailed summary
- Added a new `Chain` entry in `navLinks.ts`.
- Created a new page in `page.tsx` for `Chain` components with metadata.
- Integrated `ThirdwebProvider`, `APIHeader`, `ChainIconBasic`, and `ChainNameBasic` components.
- Implemented `ChainIconBasic` and `ChainNameBasic` components in `chain-examples.tsx` to showcase chain icons and names.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 16:14:41 +00:00
kumaryash90 8761fe6102 Process deploy param ref only when non-empty (#5730)
PROT-935

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the handling of `implementationConstructorParams` and `initializeParams` by adding null checks to ensure that they are defined before processing. This improves the robustness of the code.

### Detailed summary
- Changed `processedImplParams` and `processedInitializeParams` from constants to `undefined` initially.
- Added checks to confirm that `implementationConstructorParams` and `initializeParams` are defined before processing.
- Retained the logic for processing parameters using `processRefDeployments`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 09:16:09 +00:00
Joaquim VergesandGitHub 77c8d0eff0 test: Add wallet connection hook tests (#5689) 2024-12-13 20:25:13 +13:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
29d32b8409 Version Packages (#5727)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-13 19:43:45 +13:00
jnsdls 67f63c3a48 remove client ID derivation from secret key (#5729)
fixes: DASH-621

we explicitly *DO NOT* want to derive from secretKey anymore - this would otherwise block multi secret key per project
2024-12-13 06:18:35 +00:00
jnsdls a0770238d4 allow passing null to service scope, do not check domains on secretKey auth (#5728)
fixes: DASH-621

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the authorization process by introducing an `authMethod` property and allowing `null` service scopes, while modifying billing statuses and service configuration types.

### Detailed summary
- Added `authMethod` to `teamAndProjectResponse`.
- Allowed passing `null` to `serviceScope` in `CoreServiceConfig`.
- Changed `billingStatus` from `"noCustomer"` to `"noPayment"`.
- Updated `TeamAndProjectResponse` to include `authMethod` type.
- Adjusted authorization logic for `secretKey` and null service scopes.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 06:12:14 +00:00
gregfromstl 90a16da48e [Service Utils] Feature: Accept Request in authorizeNode (#5724)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `authorizeNode` function by improving type handling for request headers and refactoring how headers are accessed. It introduces a type guard for distinguishing between header types and updates the way headers are retrieved in various functions.

### Detailed summary
- Updated `req` type in `AuthInput` to allow `Request`.
- Added `isNodeHeaders` function to differentiate between `IncomingHttpHeaders` and `Headers`.
- Refactored `getHeader` function to use `isNodeHeaders` for retrieving headers.
- Modified `extractAuthorizationData` to use `getHeader` for constructing the URL.
- Updated `logHttpRequest` to use `getHeader` for SDK-related headers.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 05:48:05 +00:00
Jonas DanielsandGitHub e8b1f494fd Remove pre-release script and simplify release workflows (#5726) 2024-12-12 21:31:14 -08:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
aa72ba2186 Version Packages (#5725)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-12 21:11:53 -08:00
Jonas DanielsandGitHub a57bfdb565 add dummy changeset (#5723) 2024-12-12 21:09:13 -08:00
Jonas DanielsandGitHub c432342ce9 do not manually change version before publishing (#5722) 2024-12-12 21:07:27 -08:00
kien-ngo 64f4103ef7 [Playground] Add Token components, fix links (#5677)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces a new `Token` navigation link, updates documentation links, and adds a new SVG file. It also implements a `Token` components page with multiple examples and components for displaying tokens, enhancing the UI for token-related functionalities.

### Detailed summary
- Added a new navigation link for `Token` in `navLinks.ts`.
- Updated documentation links in `page.tsx` for better references.
- Introduced a new SVG file for `usdc`.
- Created a new `Token` components page with examples for:
  - `TokenImageBasic`
  - `TokenImageOverride`
  - `TokenNameBasic`
  - `TokenSymbolBasic`
  - `TokenCard`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-13 02:43:35 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
d90857ee5e Version Packages (#5717)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-13 15:42:23 +13:00
Joaquim VergesandGitHub 8d2e2ad92d [SDK] Feature: Add wagmi adapter for in-app wallet (#5644) 2024-12-13 15:33:00 +13:00
Nicholas St. GermainandGitHub c91ca93eba fix ignore of mimeTime set in mediaRenderer (#5683) 2024-12-13 14:03:09 +13:00
YashandGitHub db6b0fc1f8 add onramp status for buy with fiat in portal (#5713) 2024-12-13 14:01:54 +13:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
203da76f32 Version Packages (#5714)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-12 14:39:38 -08:00
gregandGitHub cd55ada5b2 [SDK] Feature: Adds defaultCountryCode prop to ConnectButton (#5709) 2024-12-12 13:55:00 -08:00
joaquim-verges 3ec808dc78 Update /use and types to v2 (#5708)
## Problem solved

CNCT-2640

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the `service-utils` package to remove the `usageLimit` module, update types and endpoints to version 2, and enhance authorization logic by integrating team and project responses.

### Detailed summary
- Deleted `usageLimit` module files.
- Updated API endpoint and types to version 2.
- Modified `authorizeClient` and `authorizeService` functions to use `TeamAndProjectResponse`.
- Adjusted authorization tests to reflect new structure.
- Revised caching logic for keys.
- Enhanced rate limiting to use project IDs instead of account IDs.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 21:43:13 +00:00
MananTank 7d2547979d [Dashboard] Add Insight blueprint playground (#5631)
DASH-554

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on the removal of certain components related to `BlueprintsPage`, updates to the `useHeightObserver` function, and enhancements to the `BlueprintPlayground` functionality, including new utility functions for fetching blueprint data.

### Detailed summary
- Deleted components: `BlueprintsPage`, `BlueprintsExplorer`, `BlueprintsPageHeader`.
- Updated `useHeightObserver` to be an exported function.
- Changed height from `40px` to `32px` in CSS.
- Added new props (`scrollableContainerClassName`, `shadowColor`) to several components.
- Implemented a new utility for fetching blueprints and their specifications.
- Enhanced `BlueprintPlayground` to handle fetching and displaying blueprints.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 21:31:41 +00:00
gregfromstl bc368c5a26 [Portal] Fix: Broken Link (#5716)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the link in the `page.mdx` file for the wallet overview section, directing users to the correct "get started" guide for thirdweb wallets.

### Detailed summary
- Changed the `href` attribute from `/wallet/get-started/overview` to `/connect/wallet/get-started` in the `page.mdx` file.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 20:35:23 +00:00
gregfromstl 19487cb430 [Portal] Fix: Broken Link (#5715)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates a link in the documentation for the `Wallet Connection functions`. The link's path has been changed, likely to reflect an updated structure in the documentation.

### Detailed summary
- Changed the link from `/typescript/v5/functions#wallets` to `/typescript/v5/wallets` in the `page.mdx` file.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 20:16:18 +00:00
ElasticBottle 03b6d0d516 feat: add unlinking for in app wallet accounts (#5604)
https://linear.app/thirdweb/issue/CNCT-2497/unlink-auth-method-for-enclave-wallets

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces functionality to unlink accounts from the in-app wallet, allowing users to manage multiple linked profiles more effectively.

### Detailed summary
- Added `UnlinkParams` type for unlinking profiles.
- Implemented `unlinkProfile` method in `InAppNativeConnector` and `InAppWebConnector`.
- Updated `unlinkAccount` function to handle profile unlinking.
- Added `useUnlinkProfile` hook for React.
- Enhanced `LinkedProfilesScreen` to include unlink buttons when multiple profiles exist.
- Added tests for unlinking functionality.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 19:05:18 +00:00
MananTank f7e60419c9 [Nebula] Fix page not redirected to new chat page when deleting active chat (#5712)
Fixes: DASH-613

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on refactoring the usage of the `newChatPageUrlStore` by introducing a custom hook, `useNewChatPageLink`, to manage the chat page URL more effectively across different components.

### Detailed summary
- Added `useNewChatPageLink` hook in `useNewChatPageLink.ts` to retrieve the chat page URL.
- Updated imports of `useNewChatPageLink` in `NebulaMobileNav.tsx`, `ChatSidebarLink.tsx`, and `ChatSidebar.tsx`.
- Removed duplicate `useNewChatPageLink` function from `ChatSidebar.tsx`.
- Set `newChatPageUrlStore` value directly in `ChatPageContent.tsx` based on the current page type.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 18:19:17 +00:00
MananTank 08420456af [Nebula] Remove presence message when adding an error message (#5711)
Fixes: DASH-614

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving error handling in the `setMessages` function within the `ChatPageContent` component. It modifies how error messages are added to the message list, ensuring that the previous messages are preserved correctly.

### Detailed summary
- Refactored `setMessages` to create a new array `newMessages` based on previous messages.
- Added a condition to exclude the last message if its type is "presence".
- Pushed a new error message object containing the error details to `newMessages`.
- Returned `newMessages` from `setMessages`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 18:19:16 +00:00
MananTank 611ed239ec [Nebula] Fix context filters not prepopulated when viewing chat from history (#5710)
Fixes: DASH-611
2024-12-12 15:08:46 +00:00
jnsdls ce0da51041 fix category page crash (#5707)
fixes: DASH-607

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on modifying the `explore` functionality by removing the export of `ALL_CATEGORIES` from `data/explore` and adjusting the import statements in `page.tsx`.

### Detailed summary
- Removed the export of `ALL_CATEGORIES` from `apps/dashboard/src/data/explore.ts`.
- Updated `apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx` to only import `getCategory` from `data/explore`, excluding `ALL_CATEGORIES`.
- Deleted the `generateStaticParams` function that utilized `ALL_CATEGORIES`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-12 03:18:24 +00:00
Jonas DanielsandGitHub d738a9086d remove unused api routes (#5706) 2024-12-11 18:45:05 -08:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
1787539ac5 Version Packages (#5696)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-12 13:43:18 +13:00
MananTank a0fabb2059 [Dashboard] Minor UI adjustments (#5705)
Fixes: DASH-604, DASH-582

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving the functionality and UI of the `ChatBar`, modifying the `PublishedContractTable`, and disabling the invite button in the `InviteSection`.

### Detailed summary
- In `ChatBar.tsx`, added a condition to ignore the `Enter` key if the `Shift` key is pressed.
- In `PublishedContractTable.tsx`, changed the `span` to have a maximum width and normal whitespace handling.
- In `InviteSection.tsx`, set `inviteEnabled` to `false` and disabled the invite button.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 23:35:45 +00:00
MananTank 196f6779e8 Fix LoadingCard size on mobile in login page + match LoadingCard size to ConnectEmbed (#5704)
Fixes: DASH-586
2024-12-11 22:53:23 +00:00
jnsdls f701b2d0e6 handle empty address in fetchPublishedContracts (#5702)
partially fixes: DASH-572
2024-12-11 22:13:09 +00:00
jnsdls 3d6eb969da update dependencies (#5698)
closes: DASH-576
2024-12-11 21:13:21 +00:00
jnsdls 7b6e21fa4a update startup program URL path (#5701)
fixes: DASH-602

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating the routing paths in the `framer-rewrites.js` file for the dashboard application, specifically modifying the community and grants sections.

### Detailed summary
- Removed the route `"/community/ambassadors/startup-program"`.
- Added the route `"/community/startup-program"`.
- No other changes were made to the routing paths for community pages or grants.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 21:08:43 +00:00
MananTank 4573e2c1a8 [Dashboard] Remove useLoggedInUser and fix login loop (#5695)
Fixes: DASH-577
2024-12-11 20:13:24 +00:00
jnsdls 324e602df0 closes: DASH-600 (#5700)
closes: DASH-600
2024-12-11 19:45:22 +00:00
gregfromstl f778d306d6 [React] Feature: Export web getLastAuthProvider (#5697)
<!-- start pr-codex -->

## PR-Codex overview
This PR introduces a new utility function `getLastAuthProvider` to retrieve the most recently used authentication provider from local storage. It also exports the `LAST_AUTH_PROVIDER_STORAGE_KEY` for consistent access.

### Detailed summary
- Added `getLastAuthProvider` function in `packages/thirdweb/src/react/web/utils/storage.ts`.
- Exported `LAST_AUTH_PROVIDER_STORAGE_KEY` from `packages/thirdweb/src/react/core/utils/storage.ts`.
- Created tests for `getLastAuthProvider` in `packages/thirdweb/src/react/web/utils/storage.test.ts`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 19:23:48 +00:00
jakubkrehel 4831926a09 fix: project card design and nebula text (#5686)
Fixes: DASH-580

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on improving the UI layout and styling of the `nebula-waitlist-page-ui.client.tsx` and `TeamProjectsPage.tsx` components within the dashboard application. It enhances spacing and text presentation for a better user experience.

### Detailed summary
- Increased gap in `nebula-waitlist-page-ui.client.tsx` from `gap-3` to `gap-6`.
- Adjusted paragraph width in `nebula-waitlist-page-ui.client.tsx` for better text alignment.
- Reduced height of a div from `h-10` to `h-6` in `nebula-waitlist-page-ui.client.tsx`.
- Increased padding in project card from `p-4` to `p-5` in `TeamProjectsPage.tsx`.
- Changed the class of `CopyButton` from `hover:bg-background` to `hover:bg-secondary` in `TeamProjectsPage.tsx`.
- Adjusted text presentation and layout for project details in `TeamProjectsPage.tsx`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 19:17:22 +00:00
kien-ngo 2fccfc01af [SDK] Fix caching issue with headless UI when custom resolvers are used (#5657)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR addresses caching issues in headless components and enhances code coverage. It introduces the `getFunctionId` utility in various components to improve resolver handling for names and symbols.

### Detailed summary
- Added `getFunctionId` to various components for handling resolvers.
- Updated `NFTName`, `NFTDescription`, and `NFTMedia` to use `getFunctionId` instead of `toString()`.
- Enhanced `ChainIcon`, `TokenIcon`, `ChainName`, and `TokenName` to include resolver handling.
- Introduced `fetchChainName` and `fetchTokenName` for improved name resolution.
- Added tests for `fetchChainName`, `fetchTokenName`, `fetchTokenSymbol`, and their respective query keys.
- Updated `TokenSymbol` to utilize `fetchTokenSymbol` and improved resolver handling.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 18:23:19 +00:00
kumaryash90 d9fb5441bc [Dashboard | SDK] Feature: Ref values for address params in publish (#5360)
PROT-932
PROT-864

## Problem solved

https://linear.app/thirdweb/project/[contract-tooling]-deployment-publish-flow-revamp-0194e04e2a84/overview

Handle contract refs in publish form -- Resolve / deploy all linked contracts in publish metadata, and pass the addresses as constructor args for corresponding params.

It recursively deploys all referenced contracts through the ref chain, before deploying the main contract.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the contract deployment and parameter handling in the thirdweb platform, introducing dynamic parameters, improved UI components, and better error handling for contract interactions.

### Detailed summary
- Updated `deploy-with-abi.ts` to return the contract address instead of throwing an error if already deployed.
- Enhanced UI components in `string-input.tsx` and `raw-input.tsx` for better user experience.
- Added dynamic parameter handling in multiple components.
- Introduced new types for dynamic parameters in `deploy-metadata.ts`.
- Improved form handling in `FormFieldSetup` and related components.
- Added reference contract input fields in various components for dynamic contract deployments.
- Enhanced error handling and validation in the deployment forms.
- Updated component structure for better maintainability and clarity.

> The following files were skipped due to too many changes: `apps/dashboard/src/components/contract-components/contract-publish-form/contract-params-fieldset.tsx`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 18:09:49 +00:00
jnsdls b49dde33be more pages moving to framer (#5694)
closes: DASH-592, DASH-591, DASH-589, DASH-590

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on removing several components and pages related to open-source and community initiatives, while updating redirects and modifying navigation links to reflect a shift towards "Bounties" and other new paths.

### Detailed summary
- Deleted multiple files related to open-source and community initiatives.
- Added a redirect from `/open-source` to `/bounties`.
- Updated navigation data to replace "Open Source" with "Bounties".
- Removed references to "grant/superchain" and community pages from `PageId`.
- Removed the `LandingImages` component and its associated interface.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 18:04:46 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
46b20716a5 Version Packages (#5687)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-11 20:51:28 +13:00
Joaquim VergesandGitHub da5d8ec53b fix: Autoconnect previously linked wallets (#5688) 2024-12-11 19:58:24 +13:00
8020bdbf2b Adds Portuguese (Brazil) translation for wallet connection (#5674)
Signed-off-by: Joaquim Verges <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2024-12-11 15:43:14 +13:00
iuwqyir 3f56252298 change insight auth in docs (#5681)
## Problem solved

https://linear.app/thirdweb/issue/INSIGHT-480/improvements-required-for-insight-playground

Updated the authentication method in the Insight API documentation to use the `x-client-id` header instead of embedding the client ID in the URL path.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR updates the GitHub workflow and documentation related to the `insight` feature. It enhances the valid issue prefixes and provides clearer instructions for authentication in the API usage.

### Detailed summary
- Updated `VALID_ISSUE_PREFIXES` to include `INSIGHT`.
- Improved documentation in `apps/portal/src/app/insight/get-started/page.mdx`:
  - Corrected "clientId" to "client ID".
  - Added a note about using the `x-client-id` header for authentication.
- Modified the API fetch request to include headers for `client ID`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 00:47:32 +00:00
kien-ngo a9dd26cd57 [SDK] Add missing use-client directives to some components (#5676)
## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating various components in the `thirdweb` package, specifically related to `Account`, `NFT`, `Chain`, and `Token` functionalities. It introduces new imports and ensures that the components are utilizing the necessary contexts and types.

### Detailed summary
- In `Account/provider.tsx`: Added import for `Address`.
- In `NFT/provider.tsx`: Added import for `ThirdwebContract`.
- In `Account/blobbie.tsx`: Imported `Blobbie` and `useAccountContext`.
- In `Chain/icon.tsx`, `Token/icon.tsx`, `NFT/name.tsx`, and `NFT/media.tsx`: Imported `UseQueryOptions`, `useQuery`, and `getChainMetadata`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-11 00:29:03 +00:00
gregfromstl 5b24faf1f3 [SDK] Fix: Pay not respecting ecosystem AA (#5682)
<!-- start pr-codex -->

## PR-Codex overview
This PR focuses on enhancing the detection of ecosystem wallets in the `Pay` modal by modifying the logic in the `hasSponsoredTransactionsEnabled` function and adding comprehensive tests for various wallet configurations.

### Detailed summary
- Updated the condition in `hasSponsoredTransactionsEnabled` to check for ecosystem wallets.
- Added tests for `hasSponsoredTransactionsEnabled` covering:
  - Undefined wallet
  - Smart wallet with `sponsorGas` config
  - Smart wallet with `gasless` config
  - In-app wallet with `smartAccount` config
  - Regular wallet without smart account config

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-10 23:56:35 +00:00
MananTank 714602d13c Nebula scope changes in create project modal and project settings page (#5684)
DASH-578
2024-12-10 23:07:24 +00:00
Joe-Thirdweb c11ae45f89 Chain Page Updates for Creator & Metal (#5679)
## Problem solved

Short description of the bug fixed or feature added
Linear: Dash-573
Added to chains for custom chainlist pages

- Metal L2 (1750)
- Creator Chain (66665)
- Treasure Topaz (978658)

Enabled white space preserving for new lines on chain 'About' section.

<!-- start pr-codex -->

---

## PR-Codex overview
This PR introduces new assets and updates `chainMetaRecord` to enhance the dashboard with additional blockchain information, including two new chains: `Creator` and `Metal L2`, along with their respective metadata and call-to-action details.

### Detailed summary
- Added new images: `creatorBanner`, `creatorCTA`, `metalBanner`, `metalCTA`.
- Updated `layout.tsx` to use `whitespace-pre-line` for `chainMetadata.about`.
- Introduced `Creator` chain metadata with details and CTA.
- Introduced `Metal L2` chain metadata with details and CTA.
- Added `Treasure Topaz` and `Treasure Mainnet` chain metadata with details and CTA.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-10 22:25:12 +00:00
MananTank 462138dfae Add Nebula Chat UI (#5483)
DASH-444

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on adding new features and enhancements to the `Nebula` application, including updates to environment variables, improved session handling, and new UI components.

### Detailed summary
- Added `fetch-event-stream` dependency.
- Introduced `NEXT_PUBLIC_NEBULA_URL` in environment variables.
- Enhanced `loginRedirect` to handle optional paths.
- Updated `getValidAccount` to accept optional `pagePath`.
- Implemented new stores for session management.
- Improved UI components like `Chatbar`, `ChatPageLayout`, and `EmptyStateChatPageContent`.
- Added `isValidEncodedRedirectPath` function for redirect validation.
- Modified `NebulaWaitListPage` to directly use team data.
- Enhanced session handling functions in `api/session.ts`.
- Created new types for session management and context filters.
- Updated styles and class names for better UI consistency.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/Chats.stories.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/NebulaAccountButton.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ContextFilters.tsx`, `apps/dashboard/src/app/nebula-app/(app)/chat/history/ChatHistoryPage.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx`, `apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx`, `pnpm-lock.yaml`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-10 21:58:33 +00:00
Jakub KrehelandGitHub 67305d3bd2 fix: removed login navigation text (#5680) 2024-12-10 12:52:24 -08:00
Giselle Chacon NessiandGitHub 27d3a6eb3e updated manage billing section information (#5669) 2024-12-10 09:04:29 -08:00
kien-ngo f25d1d8da5 [Playground] Feature: Headless components (#5535)
CNCT-2588

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `playground-web` application by adding new components and improving existing ones related to NFT and account functionalities, showcasing headless UI components for better Web3 integration.

### Detailed summary
- Added `ArticleCardIndex` in `page.tsx`.
- Introduced new navigation link for "Headless UI".
- Expanded `Account` and `NFT` components with detailed examples.
- Improved `useQuery` parameters in `avatar.tsx` and `name.tsx`.
- Created several new components in `nft-examples.tsx` and `account-examples.tsx` for displaying NFT and account information.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-10 07:15:01 +00:00
jnsdls d35eaa6690 update dependencies (#5667)
closes: DASH-545

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on updating various package dependencies across multiple `package.json` files and the `pnpm-lock.yaml` file, ensuring compatibility with newer versions and fixing potential issues.

### Detailed summary
- Updated `@types/react` and `@types/react-dom` to `19.0.1` and `19.0.2` respectively in multiple packages.
- Bumped `shiki` version from `1.24.0` to `1.24.1` in several packages.
- Updated `sonner` from `1.7.0` to `1.7.1` in `apps/wallet-ui` and `apps/dashboard`.
- Incremented `@emotion/react` and `@emotion/styled` versions to `11.14.0`.
- Updated `framer-motion` from `11.13.1` to `11.13.3`.
- Updated `@playwright/test` from `1.49.0` to `1.49.1`.
- Updated `@dirtycajunrice/klee` from `1.0.5` to `1.0.6` in `apps/portal`.
- Adjusted dependencies in `pnpm-lock.yaml` to reflect the above changes.

> The following files were skipped due to too many changes: `pnpm-lock.yaml`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
2024-12-10 05:15:25 +00:00
550 changed files with 19215 additions and 10010 deletions
+2 -2
View File
@@ -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'
+5 -1
View File
@@ -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]
+13 -2
View File
@@ -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');
-8
View File
@@ -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
-8
View File
@@ -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
+50
View File
@@ -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`);
+4 -1
View File
@@ -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=""
+7
View File
@@ -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",
];
+1 -1
View File
@@ -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
View File
@@ -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",
+6
View File
@@ -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;
}
+12
View File
@@ -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",
}}
/>
);
}
+2
View File
@@ -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)}` : ""}`;
}
@@ -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
@@ -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}
@@ -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}
@@ -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}
@@ -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}
@@ -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>
@@ -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>
@@ -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}
/>
);
}
@@ -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>
);
};
@@ -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}
/>
);
};
@@ -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} />
);
}
@@ -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}
/>
);
}
@@ -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>
);
};
@@ -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}
@@ -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}
/>
);
}
@@ -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}
@@ -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}
@@ -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}
@@ -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}
/>
);
}
@@ -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}
@@ -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}
@@ -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}
/>
);
}
@@ -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} />;
}
@@ -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} />
@@ -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({
@@ -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} />;
}
@@ -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}
/>
);
}
@@ -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}
/>
);
}
@@ -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}
/>
);
}
@@ -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" }}>
@@ -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}
/>
);
}
@@ -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}
/>
);
};
@@ -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">
@@ -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}
/>
);
}
@@ -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}
/>
);
}
@@ -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>
);
@@ -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}
@@ -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>
@@ -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>
@@ -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}
@@ -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}
@@ -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>
@@ -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}
@@ -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>
@@ -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}
@@ -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={{
@@ -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}
@@ -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}
@@ -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) {
@@ -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>
@@ -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>
@@ -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>
@@ -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} />;
}
@@ -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}
/>
);
}
@@ -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}
@@ -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}
/>
);
}
@@ -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}
@@ -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>
@@ -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>
@@ -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>
@@ -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}
@@ -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}
@@ -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