Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbc2e092b8 | ||
|
|
4c3c840d88 | ||
|
|
cf606665fc | ||
|
|
3871068aa6 | ||
|
|
d00ca7fd2a | ||
|
|
9ee3c21eac | ||
|
|
ed3afd418c | ||
|
|
b90fa2cc29 | ||
|
|
2ab517aa49 | ||
|
|
e46dd0254f | ||
|
|
1a5627f72d | ||
|
|
b808e942fd | ||
|
|
7fdb1715e4 |
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 4.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
|
||||
- @thirdweb-dev/wallets@2.3.4
|
||||
|
||||
## 4.1.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
|
||||
- @thirdweb-dev/wallets@2.3.3
|
||||
|
||||
## 4.1.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "4.1.19",
|
||||
"version": "4.1.21",
|
||||
"main": "dist/thirdweb-dev-auth.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth.esm.js",
|
||||
"browser": {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.13.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
|
||||
- @thirdweb-dev/wallets@2.3.4
|
||||
- @thirdweb-dev/sdk@4.0.25
|
||||
- @thirdweb-dev/auth@4.1.21
|
||||
|
||||
## 0.13.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
|
||||
- @thirdweb-dev/sdk@4.0.24
|
||||
- @thirdweb-dev/wallets@2.3.3
|
||||
- @thirdweb-dev/auth@4.1.20
|
||||
|
||||
## 0.13.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.13.27",
|
||||
"version": "0.13.29",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-typescript",
|
||||
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-flow-strip-types",
|
||||
"@babel/plugin-transform-private-methods",
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# folders
|
||||
dist/
|
||||
|
||||
# files
|
||||
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["thirdweb"],
|
||||
plugins: ["better-tree-shaking"],
|
||||
rules: {
|
||||
"better-tree-shaking/no-top-level-side-effects": "error",
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: "eventemitter3",
|
||||
importNames: ["EventEmitter"],
|
||||
message:
|
||||
"Do not use named import for importing EventEmitter, Use default import instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
# @thirdweb-dev/payments
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2115](https://github.com/thirdweb-dev/js/pull/2115) [`d00ca7fd`](https://github.com/thirdweb-dev/js/commit/d00ca7fd2a0fa3e694bb37d1f486d3d4cf1ad79e) Thanks [@IDubuque](https://github.com/IDubuque)! - Update exports with new payments interface
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- [#1913](https://github.com/thirdweb-dev/js/pull/1913) [`ed3afd41`](https://github.com/thirdweb-dev/js/commit/ed3afd418cb6e0d711814a1c52afb302460751ba) Thanks [@IDubuque](https://github.com/IDubuque)! - The `@thirdweb-dev/payments` package is a continuation of the `@paperxyz/js-client-sdk`, aligning with the new branding and expanded capabilities under thirdweb. It inherits the core functionality of Paper's developer-first NFT checkout solution.
|
||||
|
||||
### Changed
|
||||
|
||||
- Rebranded package from `@paperxyz/js-client-sdk` to `@thirdweb-dev/payments`. Users of the old package should migrate to this new package name.
|
||||
- Changed PaperSdkError typename to PaymentsSdkError
|
||||
@@ -0,0 +1,14 @@
|
||||
<p align="center">
|
||||
<br />
|
||||
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
|
||||
<br />
|
||||
</p>
|
||||
<h1 align="center">thirdweb Checkouts SDK</h1>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/@thirdweb-dev/payments"><img src="https://img.shields.io/npm/v/@thirdweb-dev/payments?color=red&label=npm&logo=npm" alt="npm version"/></a>
|
||||
<a href="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml/badge.svg"/></a>
|
||||
<a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a>
|
||||
|
||||
</p>
|
||||
<p align="center"><strong>web3 Checkouts library to integrate any NFT checkout into your applications.</strong></p>
|
||||
<br />
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/payments",
|
||||
"version": "1.0.1",
|
||||
"main": "dist/thirdweb-dev-payments.cjs.js",
|
||||
"module": "dist/thirdweb-dev-payments.esm.js",
|
||||
"types": "dist/thirdweb-dev-payments.cjs.d.ts",
|
||||
"browser": {
|
||||
"./dist/thirdweb-dev-payments.esm.js": "./dist/thirdweb-dev-payments.browser.esm.js"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"module": {
|
||||
"browser": "./dist/thirdweb-dev-payments.browser.esm.js",
|
||||
"default": "./dist/thirdweb-dev-payments.esm.js"
|
||||
},
|
||||
"default": "./dist/thirdweb-dev-payments.cjs.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/payments",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/thirdweb-dev/js/issues"
|
||||
},
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
"index.ts"
|
||||
],
|
||||
"exports": {
|
||||
"envConditions": [
|
||||
"browser"
|
||||
]
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"ethers": "^5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@thirdweb-dev/tsconfig": "workspace:*",
|
||||
"eslint-config-thirdweb": "workspace:*",
|
||||
"typescript": "^5.1.6",
|
||||
"@preconstruct/cli": "2.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write 'src/**/*'",
|
||||
"lint": "eslint src/",
|
||||
"fix": "eslint src/ --fix",
|
||||
"clean": "rm -rf dist/",
|
||||
"build": "tsc && preconstruct build",
|
||||
"preconstruct:fix": "preconstruct fix",
|
||||
"push": "yalc push"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// UNCHANGED: MERGED FROM sdk-common-utilities/blockchain
|
||||
export const ChainToPublicRpc: Record<Chain, string> = {
|
||||
Ethereum: "https://ethereum.rpc.thirdweb.com",
|
||||
Goerli: "https://goerli.rpc.thirdweb.com",
|
||||
Mumbai: "https://mumbai.rpc.thirdweb.com",
|
||||
Polygon: "https://polygon.rpc.thirdweb.com",
|
||||
Avalanche: "https://avalanche.rpc.thirdweb.com",
|
||||
Optimism: "https://optimism.rpc.thirdweb.com",
|
||||
OptimismGoerli: "https://optimism-goerli.rpc.thirdweb.com",
|
||||
BSC: "https://binance.rpc.thirdweb.com",
|
||||
BSCTestnet: "https://binance-testnet.rpc.thirdweb.com",
|
||||
ArbitrumOne: "https://arbitrum.rpc.thirdweb.com",
|
||||
ArbitrumGoerli: "https://arbitrum-goerli.rpc.thirdweb.com",
|
||||
Fantom: "https://fantom.rpc.thirdweb.com",
|
||||
FantomTestnet: "https://fantom-testnet.rpc.thirdweb.com",
|
||||
Sepolia: "https://sepolia.rpc.thirdweb.com",
|
||||
AvalancheFuji: "https://avalanche-fuji.rpc.thirdweb.com",
|
||||
};
|
||||
|
||||
export const ChainIdToChain: Record<number, Chain> = {
|
||||
1: "Ethereum",
|
||||
5: "Goerli",
|
||||
80001: "Mumbai",
|
||||
137: "Polygon",
|
||||
43114: "Avalanche",
|
||||
10: "Optimism",
|
||||
420: "OptimismGoerli",
|
||||
56: "BSC",
|
||||
97: "BSCTestnet",
|
||||
42161: "ArbitrumOne",
|
||||
421613: "ArbitrumGoerli",
|
||||
250: "Fantom",
|
||||
4002: "FantomTestnet",
|
||||
11155111: "Sepolia",
|
||||
43113: "AvalancheFuji",
|
||||
};
|
||||
|
||||
// General Embedded wallet types
|
||||
export type Chain =
|
||||
| "Polygon"
|
||||
| "Mumbai"
|
||||
| "Goerli"
|
||||
| "Ethereum"
|
||||
| "Avalanche"
|
||||
| "Optimism"
|
||||
| "OptimismGoerli"
|
||||
| "BSC"
|
||||
| "BSCTestnet"
|
||||
| "ArbitrumOne"
|
||||
| "ArbitrumGoerli"
|
||||
| "Fantom"
|
||||
| "FantomTestnet"
|
||||
| "Sepolia"
|
||||
| "AvalancheFuji";
|
||||
|
||||
export type SupportedChainName = Chain | "Rinkeby";
|
||||
@@ -0,0 +1,12 @@
|
||||
// UNCHANGED: MERGED FROM sdk-common-utilities/iframeMessages
|
||||
export type MessageType<T> =
|
||||
| {
|
||||
eventType: string;
|
||||
success: true;
|
||||
data: T;
|
||||
}
|
||||
| {
|
||||
eventType: string;
|
||||
success: false;
|
||||
error: string;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
// CHANGED: export merged sdk-common-utilities/constants & js-client-sdk/constants
|
||||
export * from "./settings";
|
||||
export * from "./blockchain";
|
||||
export * from "./style";
|
||||
@@ -0,0 +1,43 @@
|
||||
// UNCHANGED: MERGE BETWEEN sdk-common-utilities/settings and js-client-sdk/settings
|
||||
const isDev = (): boolean => {
|
||||
return !!(
|
||||
typeof window !== "undefined" &&
|
||||
window.localStorage.getItem("IS_PAPER_DEV") === "true"
|
||||
);
|
||||
};
|
||||
|
||||
const isOldPaperDomain = (): boolean =>
|
||||
typeof window !== "undefined" &&
|
||||
(window.location.hostname === "paper.xyz" ||
|
||||
window.location.hostname.endsWith(".paper.xyz"));
|
||||
|
||||
const isThirdwebDomain = (): boolean =>
|
||||
typeof window !== "undefined" &&
|
||||
(window.location.hostname === "thirdweb.com" ||
|
||||
window.location.hostname.endsWith(".thirdweb.com"));
|
||||
|
||||
export const getPaperOriginUrl = (): string => {
|
||||
if (isDev()) {
|
||||
return (
|
||||
window.localStorage.getItem("PAPER_DEV_URL") ?? "http://localhost:3000"
|
||||
);
|
||||
}
|
||||
|
||||
if (isOldPaperDomain()) {
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
if (isThirdwebDomain()) {
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
return "https://payments.thirdweb.com";
|
||||
};
|
||||
|
||||
// eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
|
||||
export const PAPER_APP_URL = getPaperOriginUrl();
|
||||
|
||||
export const CHECKOUT_WITH_ETH_IFRAME_URL = "/sdk/2022-08-12/checkout-with-eth";
|
||||
export const CHECKOUT_WITH_CARD_IFRAME_URL =
|
||||
"/sdk/2022-08-12/checkout-with-card";
|
||||
export const CREATE_WALLET_IFRAME_URL = "/sdk/v2/verify-email";
|
||||
@@ -0,0 +1,25 @@
|
||||
// // UNCHANGED: MERGED FROM sdk-common-utilities/style
|
||||
export type StyleObject = Partial<CSSStyleDeclaration>;
|
||||
|
||||
export interface ICustomizationOptions {
|
||||
colorPrimary?: string;
|
||||
colorBackground?: string;
|
||||
colorText?: string;
|
||||
borderRadius?: number;
|
||||
fontFamily?: string;
|
||||
|
||||
inputBackgroundColor?: string;
|
||||
inputBorderColor?: string;
|
||||
}
|
||||
export type CustomizationOptionsType = {
|
||||
[key in keyof ICustomizationOptions]: string;
|
||||
};
|
||||
export type Locale = "en" | "fr" | "es" | "it" | "de" | "ja" | "ko" | "zh";
|
||||
|
||||
export const DEFAULT_BRAND_OPTIONS = {
|
||||
colorPrimary: "#cf3781",
|
||||
colorBackground: "#ffffff",
|
||||
colorText: "#1a202c",
|
||||
borderRadius: 12,
|
||||
fontFamily: "Open Sans",
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// CHANGED: exports
|
||||
|
||||
// Customization options and base urls
|
||||
export * from "./constants";
|
||||
// Types
|
||||
export * from "./interfaces";
|
||||
// Components
|
||||
export * from "./lib";
|
||||
@@ -0,0 +1,67 @@
|
||||
// UNCHANGED
|
||||
|
||||
export interface ReviewResult {
|
||||
/**
|
||||
* A unique ID for this purchase.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The cardholder's full name provided by the buyer.
|
||||
*/
|
||||
cardholderName: string;
|
||||
}
|
||||
|
||||
export interface KycModal {
|
||||
iframeLink: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ICheckoutWithCardConfigs interface defines the configuration options for the checkoutWithCard component provided by Paper.
|
||||
* This component allows users to generate a card payment component for NFTs without needing any backend calls.
|
||||
*
|
||||
* The configuration options include:
|
||||
*
|
||||
* - `contractId`: The id of your contract (not the address) found in you seller dashboard.
|
||||
* - `walletAddress`: The wallet address to which the payment will be sent (AKA the receiving wallet).
|
||||
* - `email`: (Optional) Email address of the buyer.
|
||||
* - `quantity`: (Optional) The number of items to be purchased.
|
||||
* - `metadata`: (Optional) A record of additional information about the transaction.
|
||||
* - `mintMethod`: (Optional, Only required for Custom Contract Types) The contract function name, function args, and payment information which will be sent to your contract.
|
||||
* - `contractArgs`: (Optional) Depending on the contract type, this can defined additional args to call your contract with. See [Thirdweb/Reservoir docs](https://docs.withpaper.com/reference/thirdweb-contracts) for more details.
|
||||
* - `feeBearer`: (Optional) Who will bear the transaction fee, either 'BUYER' or 'SELLER'.
|
||||
* - `capturePaymentLater`: (Optional) If set to true, the payment is authorized but not captured immediately.
|
||||
* - `fiatCurrency`: (Optional) The type of fiat currency for the payment.
|
||||
* - `title`: (Optional) The title to be displayed on the Stripe receipt.
|
||||
* - `sendEmailOnTransferSucceeded`: (Optional) If set to true, an email is sent when the transfer succeeds.
|
||||
* - `postPurchaseMessageMarkdown`: (Optional) A post-purchase message in Markdown format.
|
||||
* - `postPurchaseButtonText`: (Optional) The text for the post-purchase button.
|
||||
* - `successCallbackUrl`: (Optional) The URL to be called back when the transaction is successful.
|
||||
*
|
||||
* Note: This interface is part of an open-source npm package. Please ensure you understand the implications of each field before using this interface.
|
||||
*/
|
||||
export interface ICheckoutWithCardConfigs {
|
||||
contractId: string;
|
||||
walletAddress: string;
|
||||
email?: string;
|
||||
quantity?: number;
|
||||
mintMethod?: {
|
||||
name: string;
|
||||
args: Record<string, any>;
|
||||
payment: { value: string; currency: string };
|
||||
};
|
||||
contractArgs?: Record<string, any>;
|
||||
|
||||
// payment customizations
|
||||
capturePaymentLater?: boolean;
|
||||
fiatCurrency?: string;
|
||||
|
||||
// stripe receipt
|
||||
title?: string;
|
||||
|
||||
// email
|
||||
sendEmailOnTransferSucceeded?: boolean;
|
||||
postPurchaseMessageMarkdown?: string;
|
||||
postPurchaseButtonText?: string;
|
||||
successCallbackUrl?: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// UNCHANGED
|
||||
|
||||
import type { StyleObject } from "../constants/style";
|
||||
|
||||
export interface ModalStyles {
|
||||
main: StyleObject;
|
||||
overlay: StyleObject;
|
||||
body: StyleObject;
|
||||
iframe: StyleObject;
|
||||
closeButton?: StyleObject;
|
||||
spinner: StyleObject;
|
||||
}
|
||||
|
||||
export interface ModalInterface {
|
||||
modalContainer?: HTMLElement;
|
||||
modalStyles?: Partial<ModalStyles>;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// UNCHANGED
|
||||
|
||||
export type PaperUser = {
|
||||
/**
|
||||
* The user's email address.
|
||||
* This address is case-insensitive (i.e. different capitalizations map to the same wallet).
|
||||
*/
|
||||
emailAddress: string;
|
||||
|
||||
/**
|
||||
* The Paper Wallet address associated with this user's email address.
|
||||
*/
|
||||
walletAddress: string;
|
||||
|
||||
/**
|
||||
* The user's access code. Will only be set if [clientId] was provided. This can be used to query about the user details.
|
||||
*/
|
||||
accessCode?: string;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// CHANGED: Updated name to Payments
|
||||
|
||||
export type PaymentsSDKError = {
|
||||
/**
|
||||
* An enum representing the error encountered.
|
||||
* The value is a human-readable, English message describing the error.
|
||||
*/
|
||||
code: PaymentsSDKErrorCode | PayWithCryptoErrorCode;
|
||||
error: Error;
|
||||
};
|
||||
|
||||
export enum PaymentsSDKErrorCode {
|
||||
UserAbandonedCheckout = "User abandoned the checkout",
|
||||
UserLoginFailed = "User login failed",
|
||||
InvalidProps = "The props you passed in to this component are not valid.",
|
||||
InvalidCard = "The card information is invalid. Please double check that the Card, CVC, and Zip code are all correct.",
|
||||
EmailNotVerified = "The email was unable to be verified.",
|
||||
NotEnoughSupply = "There is not enough supply to claim.",
|
||||
AddressNotAllowed = "This address is not on the allowlist.",
|
||||
NoActiveClaimPhase = "There is no active claim phase at the moment.",
|
||||
}
|
||||
|
||||
export enum PayWithCryptoErrorCode {
|
||||
ErrorConnectingToWallet = "Error connecting to wallet",
|
||||
ErrorSendingTransaction = "Something went wrong sending transaction",
|
||||
InsufficientBalance = `Insufficient ETH`,
|
||||
TransactionCancelled = "Transaction Cancelled",
|
||||
WrongChain = `Wrong Chain Detected`,
|
||||
ChainSwitchUnderway = "There is a network switch already underway",
|
||||
PendingSignature = "Pending Signature",
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// UNCHANGED
|
||||
|
||||
export interface PriceSummary {
|
||||
quantity: number;
|
||||
unitPrice: PriceDetail;
|
||||
networkFees: PriceDetail;
|
||||
serviceFees: PriceDetail;
|
||||
total: PriceDetail;
|
||||
cryptoToFiatConversionRate?: number;
|
||||
}
|
||||
|
||||
export interface PriceDetail {
|
||||
/**
|
||||
* A human-readable string to display.
|
||||
* Example: $123.45 CAD
|
||||
*/
|
||||
display: string;
|
||||
/**
|
||||
* This is the value in smallest units (cents, yen, wei).
|
||||
* Example: 12345
|
||||
*/
|
||||
valueInSubunits: number;
|
||||
/**
|
||||
* The currency associated with the value.
|
||||
* Example: USD, ETH
|
||||
*/
|
||||
currency: string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// CHANGED: Added exports
|
||||
export * from "./CheckoutWithCard";
|
||||
export * from "./PaymentsSdkError";
|
||||
export * from "./PaperUser";
|
||||
export * from "./PriceSummary";
|
||||
@@ -0,0 +1,256 @@
|
||||
// CHANGED: Added clientId to interface
|
||||
import type { ICustomizationOptions, Locale } from "../constants/style";
|
||||
import { DEFAULT_BRAND_OPTIONS } from "../constants/style";
|
||||
import { getPaperOriginUrl } from "../constants/settings";
|
||||
import {
|
||||
CHECKOUT_WITH_CARD_IFRAME_URL,
|
||||
PAPER_APP_URL,
|
||||
} from "../constants/settings";
|
||||
import type {
|
||||
ICheckoutWithCardConfigs,
|
||||
KycModal,
|
||||
ReviewResult,
|
||||
} from "../interfaces/CheckoutWithCard";
|
||||
import type {
|
||||
PaymentsSDKError,
|
||||
PaymentsSDKErrorCode,
|
||||
} from "../interfaces/PaymentsSdkError";
|
||||
import type { PriceSummary } from "../interfaces/PriceSummary";
|
||||
import { LinksManager } from "../utils/LinksManager";
|
||||
import { openCenteredPopup } from "../utils/device";
|
||||
import { postMessageToIframe } from "../utils/postMessageToIframe";
|
||||
import type { PaperPaymentElementConstructorArgs } from "./CreatePaymentElement";
|
||||
import { PaperPaymentElement } from "./CreatePaymentElement";
|
||||
import { Modal } from "./Modal";
|
||||
|
||||
export interface CheckoutWithCardLinkArgs {
|
||||
clientId?: string;
|
||||
sdkClientSecret?: string;
|
||||
appName?: string;
|
||||
options?: ICustomizationOptions;
|
||||
locale?: Locale;
|
||||
configs?: ICheckoutWithCardConfigs;
|
||||
|
||||
/**
|
||||
* @deprecated: No longer used. Domain is set to "withpaper.com".
|
||||
*/
|
||||
useAltDomain?: boolean;
|
||||
}
|
||||
|
||||
export function createCheckoutWithCardLink({
|
||||
clientId,
|
||||
sdkClientSecret,
|
||||
appName,
|
||||
options = { ...DEFAULT_BRAND_OPTIONS },
|
||||
locale,
|
||||
configs,
|
||||
}: CheckoutWithCardLinkArgs): URL {
|
||||
const CheckoutWithCardUrlBase = new URL(
|
||||
CHECKOUT_WITH_CARD_IFRAME_URL,
|
||||
PAPER_APP_URL,
|
||||
);
|
||||
let clientSecret = sdkClientSecret;
|
||||
if (!clientSecret && configs) {
|
||||
clientSecret = btoa(JSON.stringify(configs));
|
||||
}
|
||||
if (!clientSecret) {
|
||||
const error = `Must have either sdkClientSecret or configs field set. Received neither`;
|
||||
const destination = `/error?errorMessage=${error}`;
|
||||
const domain = getPaperOriginUrl();
|
||||
return new URL(destination, domain);
|
||||
}
|
||||
|
||||
if (!clientId) {
|
||||
const error = `Must have clientId field set. Please add clientId`;
|
||||
const destination = `/error?errorMessage=${error}`;
|
||||
const domain = getPaperOriginUrl();
|
||||
return new URL(destination, domain);
|
||||
}
|
||||
|
||||
const checkoutWithCardLink = new LinksManager(CheckoutWithCardUrlBase);
|
||||
checkoutWithCardLink.addClientId(clientId ?? "");
|
||||
checkoutWithCardLink.addClientSecret(clientSecret ?? "");
|
||||
checkoutWithCardLink.addStylingOptions(options);
|
||||
checkoutWithCardLink.addLocale(locale);
|
||||
checkoutWithCardLink.addAppName(appName);
|
||||
|
||||
return checkoutWithCardLink.getLink();
|
||||
}
|
||||
|
||||
export interface CheckoutWithCardMessageHandlerArgs {
|
||||
iframe: HTMLIFrameElement;
|
||||
onPaymentSuccess?: (props: {
|
||||
transactionId: string;
|
||||
/** @deprecated */
|
||||
id: string;
|
||||
}) => void;
|
||||
onReview?: (result: ReviewResult) => void;
|
||||
onError?: (error: PaymentsSDKError) => void;
|
||||
onOpenKycModal?: (props: KycModal) => void;
|
||||
onCloseKycModal?: () => void;
|
||||
onBeforeModalOpen?: (props: { url: string }) => void;
|
||||
onPriceUpdate?: (props: PriceSummary) => void;
|
||||
useAltDomain?: boolean;
|
||||
}
|
||||
|
||||
export function createCheckoutWithCardMessageHandler({
|
||||
iframe,
|
||||
onError,
|
||||
onReview,
|
||||
onPaymentSuccess,
|
||||
onBeforeModalOpen,
|
||||
onPriceUpdate,
|
||||
}: CheckoutWithCardMessageHandlerArgs) {
|
||||
let modal: Modal;
|
||||
|
||||
return (event: MessageEvent) => {
|
||||
if (!event.origin.startsWith(PAPER_APP_URL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = event;
|
||||
switch (data.eventType) {
|
||||
case "checkoutWithCardError":
|
||||
if (onError) {
|
||||
onError({
|
||||
code: data.code as PaymentsSDKErrorCode,
|
||||
error: data.error,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "paymentSuccess":
|
||||
if (onPaymentSuccess) {
|
||||
onPaymentSuccess({ transactionId: data.id, id: data.id });
|
||||
}
|
||||
|
||||
if (data.postToIframe) {
|
||||
postMessageToIframe(iframe, data.eventType, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case "reviewComplete":
|
||||
if (onReview) {
|
||||
onReview({
|
||||
id: data.id,
|
||||
cardholderName: data.cardholderName,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "openModalWithUrl":
|
||||
if (
|
||||
onBeforeModalOpen &&
|
||||
data.url &&
|
||||
data.url.includes("promptKYCModal")
|
||||
) {
|
||||
onBeforeModalOpen({
|
||||
url: data.url,
|
||||
});
|
||||
} else {
|
||||
modal = new Modal(undefined, {
|
||||
body: {
|
||||
colorScheme: "light",
|
||||
},
|
||||
});
|
||||
modal.open({ iframeUrl: data.url });
|
||||
}
|
||||
break;
|
||||
|
||||
case "completedSDKModal":
|
||||
modal.close();
|
||||
if (data.postToIframe) {
|
||||
postMessageToIframe(iframe, data.eventType, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case "requestedPopup": {
|
||||
// The iframe requested a popup.
|
||||
// The reference to this window is not stored so the popup cannot
|
||||
// be programmatically closed.
|
||||
const popupRef = openCenteredPopup({
|
||||
url: data.url,
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
});
|
||||
if (!popupRef) {
|
||||
console.error("CheckoutWithCard: Unable to open popup.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "sizing":
|
||||
iframe.style.height = data.height + "px";
|
||||
iframe.style.maxHeight = data.height + "px";
|
||||
break;
|
||||
|
||||
case "onPriceUpdate": {
|
||||
onPriceUpdate?.(data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Ignore unrecognized event
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export type CheckoutWithCardElementArgs = Omit<
|
||||
CheckoutWithCardMessageHandlerArgs,
|
||||
"iframe"
|
||||
> &
|
||||
CheckoutWithCardLinkArgs &
|
||||
PaperPaymentElementConstructorArgs;
|
||||
|
||||
export function createCheckoutWithCardElement({
|
||||
clientId,
|
||||
onCloseKycModal,
|
||||
onOpenKycModal,
|
||||
sdkClientSecret,
|
||||
appName,
|
||||
elementOrId,
|
||||
onLoad,
|
||||
onError,
|
||||
locale,
|
||||
options,
|
||||
onPaymentSuccess,
|
||||
onReview,
|
||||
onBeforeModalOpen,
|
||||
onPriceUpdate,
|
||||
useAltDomain = true,
|
||||
configs,
|
||||
}: CheckoutWithCardElementArgs) {
|
||||
const checkoutWithCardId = "checkout-with-card-iframe";
|
||||
const checkoutWithCardMessageHandler = (iframe: HTMLIFrameElement) =>
|
||||
createCheckoutWithCardMessageHandler({
|
||||
iframe,
|
||||
onCloseKycModal,
|
||||
onOpenKycModal,
|
||||
onError,
|
||||
onPaymentSuccess,
|
||||
onReview,
|
||||
onBeforeModalOpen,
|
||||
onPriceUpdate,
|
||||
useAltDomain,
|
||||
});
|
||||
|
||||
const checkoutWithCardUrl = createCheckoutWithCardLink({
|
||||
clientId,
|
||||
sdkClientSecret,
|
||||
appName,
|
||||
locale,
|
||||
options,
|
||||
useAltDomain,
|
||||
configs,
|
||||
});
|
||||
|
||||
const paymentElement = new PaperPaymentElement({
|
||||
onLoad,
|
||||
elementOrId,
|
||||
});
|
||||
return paymentElement.createPaymentElement({
|
||||
handler: checkoutWithCardMessageHandler,
|
||||
iframeId: checkoutWithCardId,
|
||||
link: checkoutWithCardUrl,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
// CHANGED: Imports only
|
||||
import type { ethers } from "ethers";
|
||||
import {
|
||||
CHECKOUT_WITH_ETH_IFRAME_URL,
|
||||
PAPER_APP_URL,
|
||||
getPaperOriginUrl,
|
||||
} from "../constants/settings";
|
||||
import type { ICustomizationOptions, Locale } from "../constants/style";
|
||||
import { DEFAULT_BRAND_OPTIONS } from "../constants/style";
|
||||
import type { PaymentsSDKError } from "../interfaces/PaymentsSdkError";
|
||||
import { PayWithCryptoErrorCode } from "../interfaces/PaymentsSdkError";
|
||||
import type { PriceSummary } from "../interfaces/PriceSummary";
|
||||
import { LinksManager } from "../utils/LinksManager";
|
||||
import { handlePayWithCryptoError } from "../utils/handleCheckoutWithEthError";
|
||||
import { postMessageToIframe } from "../utils/postMessageToIframe";
|
||||
import type { PaperPaymentElementConstructorArgs } from "./CreatePaymentElement";
|
||||
import { PaperPaymentElement } from "./CreatePaymentElement";
|
||||
|
||||
export const PAY_WITH_ETH_ERROR = "payWithEthError";
|
||||
|
||||
export async function checkAndSendEth({
|
||||
data,
|
||||
iframe,
|
||||
payingWalletSigner,
|
||||
suppressErrorToast,
|
||||
onError,
|
||||
onPaymentSuccess,
|
||||
}: {
|
||||
payingWalletSigner: ethers.Signer;
|
||||
data: {
|
||||
chainId: number;
|
||||
chainName: string;
|
||||
blob: string;
|
||||
paymentAddress: string;
|
||||
value: string;
|
||||
transactionId: string;
|
||||
};
|
||||
suppressErrorToast: boolean;
|
||||
iframe: HTMLIFrameElement;
|
||||
onPaymentSuccess?:
|
||||
| CheckoutWithEthMessageHandlerArgs["onPaymentSuccess"]
|
||||
| CheckoutWithEthMessageHandlerArgs["onSuccess"];
|
||||
onError?: (error: PaymentsSDKError) => Promise<void> | void;
|
||||
}) {
|
||||
try {
|
||||
const chainId = await payingWalletSigner.getChainId();
|
||||
if (chainId !== data.chainId) {
|
||||
throw {
|
||||
isErrorObject: true,
|
||||
title: PayWithCryptoErrorCode.WrongChain,
|
||||
description: `Please change to ${data.chainName} to proceed.`,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
await handlePayWithCryptoError(e as Error, onError, (errorObject) => {
|
||||
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
|
||||
error: errorObject,
|
||||
suppressErrorToast,
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// send the transaction
|
||||
try {
|
||||
console.log("sending funds...", data);
|
||||
const result = await payingWalletSigner.sendTransaction({
|
||||
chainId: data.chainId,
|
||||
data: data.blob,
|
||||
to: data.paymentAddress,
|
||||
value: data.value,
|
||||
});
|
||||
const receipt = await result.wait();
|
||||
if (onPaymentSuccess && result) {
|
||||
await onPaymentSuccess({
|
||||
onChainTxResponse: result,
|
||||
onChainTxReceipt: receipt,
|
||||
transactionId: data.transactionId,
|
||||
});
|
||||
}
|
||||
if (result) {
|
||||
postMessageToIframe(iframe, "paymentSuccess", {
|
||||
suppressErrorToast,
|
||||
transactionHash: result.hash,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error sending funds", error);
|
||||
await handlePayWithCryptoError(error as Error, onError, (errorObject) => {
|
||||
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
|
||||
error: errorObject,
|
||||
suppressErrorToast,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface CheckoutWithEthMessageHandlerArgs {
|
||||
iframe: HTMLIFrameElement;
|
||||
onPaymentSuccess?: (props: {
|
||||
onChainTxReceipt: ethers.providers.TransactionReceipt;
|
||||
transactionId: string;
|
||||
}) => Promise<void> | void;
|
||||
/** @deprecated */
|
||||
onSuccess?: (props: {
|
||||
onChainTxResponse: ethers.providers.TransactionResponse;
|
||||
transactionId: string;
|
||||
}) => Promise<void> | void;
|
||||
onPriceUpdate?: (props: PriceSummary) => void;
|
||||
onError?: (error: PaymentsSDKError) => void;
|
||||
suppressErrorToast?: boolean;
|
||||
setUpUserPayingWalletSigner?: (args: {
|
||||
chainId: number;
|
||||
chainName?: string;
|
||||
}) => void | Promise<void>;
|
||||
payingWalletSigner: ethers.Signer;
|
||||
}
|
||||
|
||||
export function createCheckoutWithEthMessageHandler({
|
||||
iframe,
|
||||
onError,
|
||||
onPaymentSuccess,
|
||||
onPriceUpdate,
|
||||
payingWalletSigner,
|
||||
suppressErrorToast = false,
|
||||
setUpUserPayingWalletSigner,
|
||||
}: CheckoutWithEthMessageHandlerArgs) {
|
||||
return async (event: MessageEvent) => {
|
||||
if (!event.origin.startsWith(PAPER_APP_URL)) {
|
||||
return;
|
||||
}
|
||||
const data = event.data;
|
||||
switch (data.eventType) {
|
||||
case "payWithEth": {
|
||||
if (data.error) {
|
||||
await handlePayWithCryptoError(
|
||||
new Error(data.error),
|
||||
onError,
|
||||
(errorObject) => {
|
||||
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
|
||||
error: errorObject,
|
||||
suppressErrorToast,
|
||||
});
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Allows Dev's to inject any chain switching for their custom signer here.
|
||||
if (setUpUserPayingWalletSigner) {
|
||||
try {
|
||||
console.log("setting up signer");
|
||||
await setUpUserPayingWalletSigner({
|
||||
chainId: data.chainId,
|
||||
chainName: data.chainName,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error setting up signer", error);
|
||||
await handlePayWithCryptoError(
|
||||
error as Error,
|
||||
onError,
|
||||
(errorObject) => {
|
||||
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
|
||||
error: errorObject,
|
||||
suppressErrorToast,
|
||||
});
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await checkAndSendEth({
|
||||
data,
|
||||
iframe,
|
||||
payingWalletSigner,
|
||||
suppressErrorToast,
|
||||
onError,
|
||||
onPaymentSuccess,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "checkout-with-eth-sizing": {
|
||||
iframe.style.height = data.height + "px";
|
||||
iframe.style.maxHeight = data.height + "px";
|
||||
break;
|
||||
}
|
||||
case "onPriceUpdate": {
|
||||
onPriceUpdate?.(data as PriceSummary);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface ICheckoutWithEthConfigs {
|
||||
contractId: string;
|
||||
walletAddress: string;
|
||||
email?: string;
|
||||
quantity?: number;
|
||||
mintMethod?: {
|
||||
name: string;
|
||||
args: Record<string, any>[];
|
||||
payment: { value: string; currency: string };
|
||||
};
|
||||
contractArgs?: Record<string, any>;
|
||||
|
||||
// payment customizations
|
||||
capturePaymentLater?: boolean;
|
||||
fiatCurrency?: string;
|
||||
|
||||
// stripe receipt
|
||||
title?: string;
|
||||
|
||||
// email
|
||||
sendEmailOnTransferSucceeded?: boolean;
|
||||
postPurchaseMessageMarkdown?: string;
|
||||
postPurchaseButtonText?: string;
|
||||
successCallbackUrl?: string;
|
||||
}
|
||||
|
||||
export interface CheckoutWithEthLinkArgs {
|
||||
sdkClientSecret?: string;
|
||||
appName?: string;
|
||||
payingWalletSigner: ethers.Signer;
|
||||
receivingWalletType?:
|
||||
| "WalletConnect"
|
||||
| "MetaMask"
|
||||
| "Coinbase Wallet"
|
||||
| string;
|
||||
showConnectWalletOptions?: boolean;
|
||||
|
||||
locale?: Locale;
|
||||
options?: ICustomizationOptions;
|
||||
configs?: ICheckoutWithEthConfigs;
|
||||
}
|
||||
|
||||
export async function createCheckoutWithEthLink({
|
||||
sdkClientSecret,
|
||||
payingWalletSigner,
|
||||
receivingWalletType,
|
||||
showConnectWalletOptions = false,
|
||||
appName,
|
||||
locale,
|
||||
options = {
|
||||
...DEFAULT_BRAND_OPTIONS,
|
||||
},
|
||||
configs,
|
||||
}: CheckoutWithEthLinkArgs) {
|
||||
const checkoutWithEthUrlBase = new URL(
|
||||
CHECKOUT_WITH_ETH_IFRAME_URL,
|
||||
PAPER_APP_URL,
|
||||
);
|
||||
const address = await payingWalletSigner.getAddress();
|
||||
let clientSecret = sdkClientSecret;
|
||||
if (!clientSecret && configs) {
|
||||
clientSecret = btoa(JSON.stringify(configs));
|
||||
}
|
||||
if (!clientSecret) {
|
||||
const error = `Must have either sdkClientSecret or configs field set. Received neither`;
|
||||
const destination = `/error?errorMessage=${error}`;
|
||||
const domain = getPaperOriginUrl();
|
||||
return new URL(destination, domain);
|
||||
}
|
||||
|
||||
const checkoutWithEthLink = new LinksManager(checkoutWithEthUrlBase);
|
||||
checkoutWithEthLink.addClientSecret(clientSecret ?? "");
|
||||
checkoutWithEthLink.addRecipientWalletAddress(address);
|
||||
checkoutWithEthLink.addPayerWalletAddress(address);
|
||||
checkoutWithEthLink.addReceivingWalletType(receivingWalletType);
|
||||
checkoutWithEthLink.addAppName(appName);
|
||||
checkoutWithEthLink.addShowConnectWalletOptions(showConnectWalletOptions);
|
||||
checkoutWithEthLink.addStylingOptions(options);
|
||||
checkoutWithEthLink.addLocale(locale);
|
||||
|
||||
return checkoutWithEthLink.getLink();
|
||||
}
|
||||
|
||||
export type CheckoutWithEthElementArgs = Omit<
|
||||
Omit<CheckoutWithEthMessageHandlerArgs, "iframe">,
|
||||
"setUpUserPayingWalletSigner"
|
||||
> &
|
||||
CheckoutWithEthLinkArgs &
|
||||
PaperPaymentElementConstructorArgs;
|
||||
|
||||
export async function createCheckoutWithEthElement({
|
||||
sdkClientSecret,
|
||||
suppressErrorToast,
|
||||
onError,
|
||||
onLoad,
|
||||
payingWalletSigner,
|
||||
receivingWalletType,
|
||||
appName,
|
||||
showConnectWalletOptions,
|
||||
locale,
|
||||
options,
|
||||
elementOrId,
|
||||
onPaymentSuccess: _onPaymentSuccess,
|
||||
onSuccess,
|
||||
}: CheckoutWithEthElementArgs): Promise<HTMLIFrameElement> {
|
||||
const onPaymentSuccess =
|
||||
_onPaymentSuccess ??
|
||||
(onSuccess as CheckoutWithEthMessageHandlerArgs["onPaymentSuccess"]);
|
||||
const checkoutWithEthId = "checkout-with-eth-iframe";
|
||||
|
||||
const checkoutWithEthMessageHandler = (iframe: HTMLIFrameElement) =>
|
||||
createCheckoutWithEthMessageHandler({
|
||||
iframe,
|
||||
onError,
|
||||
payingWalletSigner,
|
||||
suppressErrorToast,
|
||||
onPaymentSuccess,
|
||||
});
|
||||
|
||||
const checkoutWithEthUrl = await createCheckoutWithEthLink({
|
||||
payingWalletSigner,
|
||||
sdkClientSecret,
|
||||
appName,
|
||||
locale,
|
||||
options,
|
||||
receivingWalletType,
|
||||
showConnectWalletOptions,
|
||||
});
|
||||
const paymentElement = new PaperPaymentElement({
|
||||
onLoad,
|
||||
elementOrId,
|
||||
});
|
||||
return paymentElement.createPaymentElement({
|
||||
handler: checkoutWithEthMessageHandler,
|
||||
iframeId: checkoutWithEthId,
|
||||
link: checkoutWithEthUrl,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// CHANGED: packageJson import + version string
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, better-tree-shaking/no-top-level-side-effects
|
||||
const packageJson = require("../package.json");
|
||||
|
||||
export interface PaperPaymentElementConstructorArgs {
|
||||
onLoad?: (event?: Event) => void;
|
||||
elementOrId?: string | HTMLElement;
|
||||
}
|
||||
|
||||
export class PaperPaymentElement {
|
||||
private elementOrId?: PaperPaymentElementConstructorArgs["elementOrId"];
|
||||
private onLoad?: PaperPaymentElementConstructorArgs["onLoad"];
|
||||
|
||||
constructor({ elementOrId, onLoad }: PaperPaymentElementConstructorArgs) {
|
||||
this.elementOrId = elementOrId;
|
||||
this.onLoad = onLoad;
|
||||
}
|
||||
createPaymentElement({
|
||||
handler,
|
||||
link,
|
||||
iframeId,
|
||||
}: {
|
||||
handler: (
|
||||
iframe: HTMLIFrameElement,
|
||||
) => (event: MessageEvent) => void | Promise<void>;
|
||||
link: URL;
|
||||
iframeId: string;
|
||||
}) {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.src = link.href;
|
||||
iframe.id = iframeId;
|
||||
iframe.allow = "payment";
|
||||
iframe.setAttribute(
|
||||
"style",
|
||||
"margin-left:auto; margin-right:auto; width:100%; height: 100%; min-height:375px; transition-property:all; transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1); transition-duration:150ms; color-scheme: light;",
|
||||
);
|
||||
iframe.onload = (event: Event) => {
|
||||
if (this.onLoad) {
|
||||
this.onLoad(event);
|
||||
}
|
||||
};
|
||||
iframe.setAttribute(
|
||||
"data-thirdweb-sdk-version",
|
||||
`${packageJson.name}@${packageJson.version}`,
|
||||
);
|
||||
|
||||
if (!this.elementOrId) {
|
||||
window.addEventListener("message", handler(iframe));
|
||||
return iframe;
|
||||
}
|
||||
|
||||
let container: HTMLElement | string = this.elementOrId;
|
||||
if (typeof container === "string") {
|
||||
const domElement = document.getElementById(container);
|
||||
if (!domElement) {
|
||||
throw new Error("Invalid id given");
|
||||
}
|
||||
container = domElement;
|
||||
}
|
||||
|
||||
const existing: HTMLIFrameElement | null = container.querySelector(
|
||||
"#" + iframeId,
|
||||
);
|
||||
// if we already created an iframe, consider updating the iframe link if it's new
|
||||
if (existing) {
|
||||
if (existing.src === link.href) {
|
||||
return existing;
|
||||
}
|
||||
existing.src = link.href;
|
||||
return existing;
|
||||
}
|
||||
|
||||
window.addEventListener("message", handler(iframe));
|
||||
return container.appendChild(iframe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import type { StyleObject } from "../../constants/style";
|
||||
import type { ModalStyles } from "../../interfaces/Modal";
|
||||
import { getDefaultModalStyles } from "./styles";
|
||||
|
||||
import packageJson from "../../../package.json";
|
||||
|
||||
const MAIN_CLASSNAME = "paper--drawer-main";
|
||||
const OVERLAY_CLASSNAME = "paper--drawer-overlay";
|
||||
const BODY_CLASSNAME = "paper--drawer-body";
|
||||
const CLOSE_CLASSNAME = "paper--drawer-close";
|
||||
|
||||
export class Drawer {
|
||||
protected container: HTMLElement;
|
||||
protected main: HTMLDivElement;
|
||||
protected overlay: HTMLDivElement;
|
||||
protected closeButton: HTMLButtonElement;
|
||||
protected iframe: HTMLIFrameElement;
|
||||
protected onCloseCallback: (() => void) | undefined;
|
||||
|
||||
protected closeTimeout: number | undefined;
|
||||
// eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
|
||||
styles = getDefaultModalStyles();
|
||||
body: HTMLDivElement;
|
||||
|
||||
constructor(container?: HTMLElement, styles?: Partial<ModalStyles>) {
|
||||
this.container = container || document.body;
|
||||
|
||||
if (styles) {
|
||||
this.mergeStyles(styles);
|
||||
}
|
||||
|
||||
this.main = document.createElement("div");
|
||||
this.main.className = MAIN_CLASSNAME;
|
||||
|
||||
this.overlay = document.createElement("div");
|
||||
this.overlay.className = OVERLAY_CLASSNAME;
|
||||
|
||||
this.body = document.createElement("div");
|
||||
this.body.className = BODY_CLASSNAME;
|
||||
|
||||
this.closeButton = document.createElement("button");
|
||||
this.closeButton.className = CLOSE_CLASSNAME;
|
||||
this.closeButton.innerHTML = "✕";
|
||||
this.closeButton.onclick = () => {
|
||||
this.close();
|
||||
};
|
||||
|
||||
this.iframe = document.createElement("iframe");
|
||||
this.iframe.allow = "camera; microphone; payment";
|
||||
this.iframe.setAttribute(
|
||||
"data-thirdweb-sdk-version",
|
||||
`${packageJson.name}@${packageJson.version}`,
|
||||
);
|
||||
|
||||
this.assignStyles(this.main, this.styles.main);
|
||||
this.assignStyles(this.overlay, this.styles.overlay);
|
||||
this.assignStyles(this.body, this.styles.body);
|
||||
this.assignStyles(this.iframe, this.styles.iframe);
|
||||
if (this.styles.closeButton) {
|
||||
this.assignStyles(this.closeButton, this.styles.closeButton);
|
||||
}
|
||||
}
|
||||
|
||||
open({ iframeUrl }: { iframeUrl?: string } = {}) {
|
||||
if (iframeUrl) {
|
||||
this.iframe.src = iframeUrl;
|
||||
this.body.appendChild(this.iframe);
|
||||
}
|
||||
|
||||
this.addAccessibility();
|
||||
|
||||
this.main.appendChild(this.overlay);
|
||||
this.main.appendChild(this.body);
|
||||
this.main.appendChild(this.closeButton);
|
||||
|
||||
this.container.appendChild(this.main);
|
||||
document.body.style.overflow = "hidden";
|
||||
|
||||
// Animate in.
|
||||
this.overlay.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
|
||||
this.body.style.right = "0px";
|
||||
this.body.style.opacity = "1";
|
||||
|
||||
return this.iframe;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.closeButton.remove();
|
||||
|
||||
// Animate out.
|
||||
this.overlay.style.backgroundColor = "rgba(0, 0, 0, 0)";
|
||||
this.body.style.right = "-100px";
|
||||
this.body.style.opacity = "0";
|
||||
|
||||
// Remove drawer from DOM.
|
||||
this.closeTimeout = window.setTimeout(() => {
|
||||
document.body.style.overflow = "visible";
|
||||
this.main.remove();
|
||||
|
||||
window.clearTimeout(this.closeTimeout);
|
||||
this.onCloseCallback?.();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
setOnCloseCallback(callback: () => void) {
|
||||
this.onCloseCallback = callback;
|
||||
}
|
||||
|
||||
protected mergeStyles(styles: Partial<ModalStyles>) {
|
||||
this.styles.body = {
|
||||
...this.styles.body,
|
||||
...(styles.body || {}),
|
||||
};
|
||||
|
||||
this.styles.overlay = {
|
||||
...this.styles.overlay,
|
||||
...(styles.overlay || {}),
|
||||
};
|
||||
|
||||
this.styles.main = {
|
||||
...this.styles.main,
|
||||
...(styles.main || {}),
|
||||
};
|
||||
|
||||
this.styles.iframe = {
|
||||
...this.styles.iframe,
|
||||
...(styles.iframe || {}),
|
||||
};
|
||||
|
||||
this.styles.closeButton = {
|
||||
...this.styles.closeButton,
|
||||
...(styles.closeButton || {}),
|
||||
};
|
||||
}
|
||||
|
||||
protected addAccessibility() {
|
||||
this.main.setAttribute("aria-hidden", "true");
|
||||
this.overlay.setAttribute("aria-hidden", "true");
|
||||
this.body.setAttribute("aria-modal", "true");
|
||||
this.body.setAttribute("role", "dialog");
|
||||
}
|
||||
|
||||
protected assignStyles(el: HTMLElement, styles: StyleObject) {
|
||||
Object.assign(el.style, styles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { ModalStyles } from "../../interfaces/Modal";
|
||||
|
||||
const fullScreen = {
|
||||
position: "fixed",
|
||||
top: "0px",
|
||||
left: "0px",
|
||||
right: "0px",
|
||||
bottom: "0px",
|
||||
};
|
||||
|
||||
export const getDefaultModalStyles = (): ModalStyles => ({
|
||||
main: {
|
||||
...fullScreen,
|
||||
zIndex: "2147483646",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "end",
|
||||
},
|
||||
overlay: {
|
||||
...fullScreen,
|
||||
backgroundColor: "rgba(0, 0, 0, 0)",
|
||||
transition: "background-color ease-out 0.2s",
|
||||
},
|
||||
body: {
|
||||
position: "fixed",
|
||||
width: "100%",
|
||||
maxWidth: "420px",
|
||||
top: "0px",
|
||||
bottom: "0px",
|
||||
right: "-100px",
|
||||
opacity: "0",
|
||||
boxShadow: "0px 10px 20px rgba(0, 0, 0, 0.25)",
|
||||
backgroundColor: window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "#2F2F2F"
|
||||
: "white",
|
||||
overflow: "hidden",
|
||||
transition: "all ease-out 0.2s",
|
||||
},
|
||||
spinner: {
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
bottom: "0",
|
||||
left: "0",
|
||||
right: "0",
|
||||
margin: "auto",
|
||||
borderWidth: "3px",
|
||||
borderColor: "#2D3748 #2D3748 transparent transparent",
|
||||
borderRadius: "50%",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
animation: "spin 1s linear infinite",
|
||||
},
|
||||
iframe: {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
border: "none",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
closeButton: {
|
||||
position: "fixed",
|
||||
cursor: "pointer",
|
||||
top: "0.75rem",
|
||||
right: "1rem",
|
||||
color: "#888",
|
||||
padding: "2px 8px",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
import type { StyleObject } from "../../constants/style";
|
||||
import type { ModalStyles } from "../../interfaces/Modal";
|
||||
import { getDefaultModalStyles, modalKeyframeAnimations } from "./styles";
|
||||
|
||||
import packageJson from "../../../package.json";
|
||||
|
||||
export const MODAL_ID = "paper-js-sdk-modal";
|
||||
|
||||
export class Modal {
|
||||
protected container: HTMLElement;
|
||||
protected main: HTMLDivElement;
|
||||
protected overlay: HTMLDivElement;
|
||||
protected iframe: HTMLIFrameElement;
|
||||
protected spinner: HTMLDivElement;
|
||||
|
||||
protected style: HTMLStyleElement;
|
||||
// eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
|
||||
styles = getDefaultModalStyles();
|
||||
body: HTMLDivElement;
|
||||
|
||||
constructor(container?: HTMLElement, styles?: Partial<ModalStyles>) {
|
||||
this.container = container || document.body;
|
||||
|
||||
if (styles) {
|
||||
this.mergeStyles(styles);
|
||||
}
|
||||
|
||||
this.main = document.createElement("div");
|
||||
this.main.id = MODAL_ID;
|
||||
|
||||
this.overlay = document.createElement("div");
|
||||
this.overlay.id = `${MODAL_ID}-overlay`;
|
||||
this.body = document.createElement("div");
|
||||
this.body.id = `${MODAL_ID}-body`;
|
||||
this.spinner = document.createElement("div");
|
||||
this.spinner.id = `${MODAL_ID}-spinner`;
|
||||
this.iframe = document.createElement("iframe");
|
||||
this.iframe.id = `${MODAL_ID}-iframe`;
|
||||
this.iframe.allow = "camera; microphone; payment";
|
||||
this.iframe.setAttribute(
|
||||
"data-thirdweb-sdk-version",
|
||||
`${packageJson.name}@${packageJson.version}`,
|
||||
);
|
||||
|
||||
this.style = document.createElement("style");
|
||||
this.style.innerHTML = modalKeyframeAnimations;
|
||||
|
||||
this.assignStyles(this.main, this.styles.main);
|
||||
this.assignStyles(this.overlay, this.styles.overlay);
|
||||
this.assignStyles(this.body, this.styles.body);
|
||||
this.assignStyles(this.spinner, this.styles.spinner);
|
||||
this.assignStyles(this.iframe, this.styles.iframe);
|
||||
}
|
||||
|
||||
open({ iframeUrl }: { iframeUrl?: string } = {}) {
|
||||
if (iframeUrl) {
|
||||
this.body.appendChild(this.spinner);
|
||||
this.iframe.src = iframeUrl;
|
||||
// Remove the spinner when the iframe loads.
|
||||
this.iframe.onload = () => this.body.removeChild(this.spinner);
|
||||
this.body.appendChild(this.iframe);
|
||||
}
|
||||
|
||||
this.addAccessibility();
|
||||
|
||||
this.main.appendChild(this.overlay);
|
||||
this.main.appendChild(this.style);
|
||||
this.main.appendChild(this.body);
|
||||
|
||||
this.container.appendChild(this.main);
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
|
||||
close() {
|
||||
this.body.style.animation = "pew-modal-slideOut 0.2s forwards";
|
||||
window.setTimeout(() => this.main.remove(), 250);
|
||||
}
|
||||
|
||||
protected mergeStyles(styles: Partial<ModalStyles>) {
|
||||
this.styles.body = {
|
||||
...this.styles.body,
|
||||
...(styles.body || {}),
|
||||
};
|
||||
|
||||
this.styles.spinner = {
|
||||
...this.styles.spinner,
|
||||
...(styles.spinner || {}),
|
||||
};
|
||||
|
||||
this.styles.overlay = {
|
||||
...this.styles.overlay,
|
||||
...(styles.overlay || {}),
|
||||
};
|
||||
|
||||
this.styles.main = {
|
||||
...this.styles.main,
|
||||
...(styles.main || {}),
|
||||
};
|
||||
|
||||
this.styles.iframe = {
|
||||
...this.styles.iframe,
|
||||
...(styles.iframe || {}),
|
||||
};
|
||||
}
|
||||
|
||||
protected addAccessibility() {
|
||||
this.main.setAttribute("aria-hidden", "true");
|
||||
this.overlay.setAttribute("aria-hidden", "true");
|
||||
this.body.setAttribute("aria-modal", "true");
|
||||
this.body.setAttribute("role", "dialog");
|
||||
}
|
||||
|
||||
protected assignStyles(el: HTMLElement, styles: StyleObject) {
|
||||
Object.assign(el.style, styles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import type { ModalStyles } from "../../interfaces/Modal";
|
||||
|
||||
const fullScreen = {
|
||||
top: "0px",
|
||||
left: "0px",
|
||||
right: "0px",
|
||||
bottom: "0px",
|
||||
};
|
||||
|
||||
export const getDefaultModalStyles = (): ModalStyles => ({
|
||||
main: {
|
||||
...fullScreen,
|
||||
position: "fixed",
|
||||
zIndex: "10000",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
pointerEvents: "auto",
|
||||
},
|
||||
overlay: {
|
||||
...fullScreen,
|
||||
position: "absolute",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.33)",
|
||||
pointerEvents: "auto",
|
||||
},
|
||||
body: {
|
||||
background: "transparent",
|
||||
borderRadius: "12px",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
maxWidth: "500px",
|
||||
height: "700px",
|
||||
maxHeight: "80%",
|
||||
animation: "pew-modal-slideIn 0.2s forwards",
|
||||
pointerEvents: "auto",
|
||||
},
|
||||
spinner: {
|
||||
position: "absolute",
|
||||
top: "0",
|
||||
bottom: "0",
|
||||
left: "0",
|
||||
right: "0",
|
||||
margin: "auto",
|
||||
borderWidth: "3px",
|
||||
borderColor: "#2D3748 #2D3748 transparent transparent",
|
||||
borderRadius: "50%",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
animation: "spin 1s linear infinite",
|
||||
},
|
||||
iframe: {
|
||||
position: "relative",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
border: "none",
|
||||
background: "transparent",
|
||||
},
|
||||
});
|
||||
|
||||
export const modalKeyframeAnimations = `
|
||||
@keyframes pew-modal-slideIn {
|
||||
from {opacity: 0; transform: translate3d(0, 20px, 0);}
|
||||
to {opacity: 1; transform: translate3d(0, 0, 0);}
|
||||
}
|
||||
|
||||
@keyframes pew-modal-slideOut {
|
||||
from {opacity: 1; transform: translate3d(0, 0, 0);}
|
||||
to {opacity: 0; transform: translate3d(0, 20px, 0);}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,4 @@
|
||||
// CHANGED: ADDED
|
||||
export * from "./CheckoutWithCard";
|
||||
export * from "./CheckoutWithEth";
|
||||
export * from "./renderPaperCheckoutLink";
|
||||
@@ -0,0 +1,82 @@
|
||||
// UNCHANGED
|
||||
import { PAPER_APP_URL } from "../constants/settings";
|
||||
import { Drawer } from "./Drawer";
|
||||
|
||||
async function sleepForSeconds(seconds: number) {
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res(0);
|
||||
}, seconds * 1000);
|
||||
});
|
||||
}
|
||||
|
||||
export function renderPaperCheckoutLink({
|
||||
checkoutLinkUrl,
|
||||
onPaymentSucceeded,
|
||||
onPaymentFailed,
|
||||
onTransferSucceeded,
|
||||
onModalClosed,
|
||||
}: {
|
||||
checkoutLinkUrl: string;
|
||||
onPaymentSucceeded?: ({ transactionId }: { transactionId: string }) => void;
|
||||
onPaymentFailed?: ({ transactionId }: { transactionId: string }) => void;
|
||||
onTransferSucceeded?: ({
|
||||
transactionId,
|
||||
claimedTokens,
|
||||
}: {
|
||||
transactionId: string;
|
||||
claimedTokens: any;
|
||||
}) => void;
|
||||
onModalClosed?: () => void;
|
||||
}) {
|
||||
const drawer = new Drawer();
|
||||
|
||||
const formattedCheckoutLinkUrl = new URL(checkoutLinkUrl);
|
||||
formattedCheckoutLinkUrl.searchParams.set("display", "DRAWER");
|
||||
drawer.open({ iframeUrl: formattedCheckoutLinkUrl.href });
|
||||
if (onModalClosed) {
|
||||
drawer.setOnCloseCallback(onModalClosed);
|
||||
}
|
||||
|
||||
const messageHandler = async (e: MessageEvent) => {
|
||||
if (e.origin !== PAPER_APP_URL) {
|
||||
return;
|
||||
}
|
||||
const result = e.data;
|
||||
if (!result.eventType) {
|
||||
return;
|
||||
}
|
||||
switch (result.eventType) {
|
||||
case "paymentSuccess": {
|
||||
const transactionId = e.data.id;
|
||||
onPaymentSucceeded?.({ transactionId });
|
||||
break;
|
||||
}
|
||||
case "claimSuccessful": {
|
||||
const { id: transactionId, claimedTokens } = e.data;
|
||||
onTransferSucceeded?.({ transactionId, claimedTokens });
|
||||
await sleepForSeconds(3.5);
|
||||
drawer.close();
|
||||
break;
|
||||
}
|
||||
case "redirectAfterSuccess": {
|
||||
const redirectUrl = e.data.redirectUrl;
|
||||
window.location.assign(redirectUrl);
|
||||
break;
|
||||
}
|
||||
case "paymentFailed": {
|
||||
const transactionId = e.data.id;
|
||||
onPaymentFailed?.({ transactionId });
|
||||
break;
|
||||
}
|
||||
case "modalClosed": {
|
||||
onModalClosed?.();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported eventType ${result.eventType}`);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", messageHandler);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// CHANGED: added clientId to link
|
||||
|
||||
import type { ICustomizationOptions, Locale } from "../constants/style";
|
||||
|
||||
export class LinksManager {
|
||||
private link: URL;
|
||||
|
||||
constructor(baseLink: URL) {
|
||||
this.link = baseLink;
|
||||
}
|
||||
|
||||
addStylingOptions(options: ICustomizationOptions) {
|
||||
if (options.colorPrimary) {
|
||||
this.link.searchParams.set("colorPrimary", options.colorPrimary);
|
||||
}
|
||||
if (options.colorBackground) {
|
||||
this.link.searchParams.set("colorBackground", options.colorBackground);
|
||||
}
|
||||
if (options.colorText) {
|
||||
this.link.searchParams.set("colorText", options.colorText);
|
||||
}
|
||||
if (options.borderRadius !== undefined) {
|
||||
this.link.searchParams.set(
|
||||
"borderRadius",
|
||||
options.borderRadius.toString(),
|
||||
);
|
||||
}
|
||||
if (options.fontFamily) {
|
||||
this.link.searchParams.set("fontFamily", options.fontFamily);
|
||||
}
|
||||
if (options.inputBackgroundColor) {
|
||||
this.link.searchParams.set(
|
||||
"inputBackgroundColor",
|
||||
options.inputBackgroundColor,
|
||||
);
|
||||
}
|
||||
if (options.inputBorderColor) {
|
||||
this.link.searchParams.set("inputBorderColor", options.inputBorderColor);
|
||||
}
|
||||
}
|
||||
|
||||
addClientSecret(sdkClientSecret: string) {
|
||||
this.link.searchParams.set("sdkClientSecret", sdkClientSecret);
|
||||
}
|
||||
|
||||
addClientId(clientId: string) {
|
||||
this.link.searchParams.set("clientId", clientId);
|
||||
}
|
||||
|
||||
addLocale(locale?: Locale) {
|
||||
if (locale) {
|
||||
this.link.searchParams.set("locale", locale.toString());
|
||||
}
|
||||
}
|
||||
|
||||
addOTP() {
|
||||
this.link.searchParams.set("withOTP", "true");
|
||||
}
|
||||
|
||||
addAppName(appName?: string) {
|
||||
if (appName) {
|
||||
this.link.searchParams.set("appName", appName);
|
||||
}
|
||||
}
|
||||
|
||||
addShowConnectWalletOptions(showConnectWalletOptions: boolean) {
|
||||
this.link.searchParams.append(
|
||||
"showConnectWalletOptions",
|
||||
showConnectWalletOptions.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
addReceivingWalletType(walletType?: string) {
|
||||
this.link.searchParams.append("walletType", walletType || "Preset");
|
||||
}
|
||||
|
||||
addRecipientWalletAddress(address: string) {
|
||||
this.link.searchParams.set("recipientWalletAddress", address);
|
||||
}
|
||||
|
||||
addPayerWalletAddress(address: string) {
|
||||
this.link.searchParams.append("payerWalletAddress", address);
|
||||
}
|
||||
|
||||
addDate(date?: Date) {
|
||||
this.link.searchParams.set(
|
||||
"date",
|
||||
date ? date.toString() : Date.now().toString(),
|
||||
);
|
||||
}
|
||||
|
||||
getLink(): URL {
|
||||
return this.link;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// UNCHANGED
|
||||
/**
|
||||
* Opens a popup centered on the current window.
|
||||
* Note that modern browsers may prevent popups from opening automatically,
|
||||
* so try to handle the case where the window returned is null.
|
||||
*
|
||||
* @returns Window | null - The Window object that was opened. If null is returned, the popup failed to open.
|
||||
*/
|
||||
export const openCenteredPopup = ({
|
||||
url,
|
||||
width = 440,
|
||||
height = 700,
|
||||
}: {
|
||||
url: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}): Window | null => {
|
||||
if (!window?.top) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const y = window.top.outerHeight / 2 + window.top.screenY - height / 2;
|
||||
const x = window.top.outerWidth / 2 + window.top.screenX - width / 2;
|
||||
return window.open(
|
||||
url,
|
||||
"_blank",
|
||||
`popup=true,width=${width},height=${height},top=${y},left=${x}`,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
// Changed: Updated PaperSDKError
|
||||
import type { PaymentsSDKError } from "../interfaces/PaymentsSdkError";
|
||||
import { PayWithCryptoErrorCode } from "../interfaces/PaymentsSdkError";
|
||||
|
||||
export interface IErrorObject {
|
||||
isErrorObject: boolean;
|
||||
title: PayWithCryptoErrorCode;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const handlePayWithCryptoError = async (
|
||||
error: Error | IErrorObject,
|
||||
onError?: (code: PaymentsSDKError) => Promise<void> | void,
|
||||
postToParent?: (errorObject: Omit<IErrorObject, "isErrorObject">) => void,
|
||||
) => {
|
||||
if ("isErrorObject" in error) {
|
||||
if (onError) {
|
||||
await onError({ code: error.title, error: new Error(error.title) });
|
||||
}
|
||||
if (postToParent) {
|
||||
postToParent({ ...error });
|
||||
}
|
||||
} else if (!("message" in error)) {
|
||||
if (onError) {
|
||||
await onError({
|
||||
code: PayWithCryptoErrorCode.ErrorSendingTransaction,
|
||||
error: new Error(JSON.stringify(error)),
|
||||
});
|
||||
}
|
||||
if (postToParent) {
|
||||
postToParent({
|
||||
description: `${error}`,
|
||||
title: PayWithCryptoErrorCode.ErrorSendingTransaction,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
error.message.includes("rejected") ||
|
||||
error.message.includes("denied transaction")
|
||||
) {
|
||||
if (onError) {
|
||||
await onError({
|
||||
code: PayWithCryptoErrorCode.TransactionCancelled,
|
||||
error,
|
||||
});
|
||||
}
|
||||
if (postToParent) {
|
||||
postToParent({
|
||||
description: "",
|
||||
title: PayWithCryptoErrorCode.TransactionCancelled,
|
||||
});
|
||||
}
|
||||
} else if (error.message.includes("insufficient funds")) {
|
||||
if (onError) {
|
||||
await onError({
|
||||
code: PayWithCryptoErrorCode.InsufficientBalance,
|
||||
error,
|
||||
});
|
||||
}
|
||||
if (postToParent) {
|
||||
postToParent({
|
||||
description:
|
||||
"Check your wallet's ETH balance to make sure you have enough!",
|
||||
title: PayWithCryptoErrorCode.InsufficientBalance,
|
||||
});
|
||||
}
|
||||
} else if (error.message.includes("Error switching chain")) {
|
||||
if (onError) {
|
||||
await onError({
|
||||
code: PayWithCryptoErrorCode.ChainSwitchUnderway,
|
||||
error,
|
||||
});
|
||||
}
|
||||
if (postToParent) {
|
||||
postToParent({
|
||||
description: "Check your wallet app",
|
||||
title: PayWithCryptoErrorCode.ChainSwitchUnderway,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (onError) {
|
||||
await onError({
|
||||
code: PayWithCryptoErrorCode.ErrorSendingTransaction,
|
||||
error,
|
||||
});
|
||||
}
|
||||
if (postToParent) {
|
||||
postToParent({
|
||||
description: `${error.message}`,
|
||||
title: PayWithCryptoErrorCode.ErrorSendingTransaction,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
// UNCHANGED
|
||||
export function postMessageToIframe<T>(
|
||||
frame: HTMLIFrameElement,
|
||||
eventType: string,
|
||||
data: T,
|
||||
) {
|
||||
frame.contentWindow?.postMessage({ eventType, ...data }, "*");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@thirdweb-dev/tsconfig/sdk.json",
|
||||
"include": ["src", "types"],
|
||||
"exclude": ["dist", "build", "node_modules"],
|
||||
"compilerOptions": {
|
||||
"noUncheckedIndexedAccess": true
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,27 @@
|
||||
# @thirdweb-dev/react-core
|
||||
|
||||
## 4.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2119](https://github.com/thirdweb-dev/js/pull/2119) [`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc) Thanks [@MananTank](https://github.com/MananTank)! - Update Links to Docs website
|
||||
|
||||
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
|
||||
- @thirdweb-dev/wallets@2.3.4
|
||||
- @thirdweb-dev/sdk@4.0.25
|
||||
- @thirdweb-dev/auth@4.1.21
|
||||
|
||||
## 4.2.1
|
||||
|
||||
## 4.2.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
|
||||
- @thirdweb-dev/sdk@4.0.24
|
||||
- @thirdweb-dev/wallets@2.3.3
|
||||
- @thirdweb-dev/auth@4.1.20
|
||||
|
||||
## 4.1.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-core",
|
||||
"version": "4.1.17",
|
||||
"version": "4.2.2",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -204,7 +204,7 @@ export function useWallets() {
|
||||
* #### walletConfig
|
||||
* The wallet to connect. Must be of type `WalletConfig`.
|
||||
*
|
||||
* [Learn more about the available wallet options](https://portal.thirdweb.com/react/connecting-wallets).
|
||||
* [Learn more about the available wallet options](https://portal.thirdweb.com/react/v4/connecting-wallets).
|
||||
*
|
||||
* #### connectOptions
|
||||
* The typeof `connectOptions` object depends on the wallet you are connecting. For some wallets, it may be optional
|
||||
@@ -288,7 +288,7 @@ export function useConnectionStatus() {
|
||||
/**
|
||||
* Hook for setting the `connectionStatus` of the wallet which is returned by the `useConnectionStatus` hook
|
||||
*
|
||||
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet/build-a-wallet)
|
||||
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet-sdk/v2/build)
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@@ -418,7 +418,7 @@ export function useSwitchChain() {
|
||||
/**
|
||||
* Hook for setting a wallet instance as "connected" - once done, the wallet connection hooks like `useWallet`, `useAddress`, `useSigner`, `useConnectionStatus` etc will return the data for that wallet instance
|
||||
*
|
||||
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet/build-a-wallet)
|
||||
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet-sdk/v2/build)
|
||||
*
|
||||
* @example
|
||||
*
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface ISecureStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration to use by the React and React Native SDKs with an [auth](https://portal.thirdweb.com/auth) server.
|
||||
* The configuration to use by the React and React Native SDKs with an [auth](https://portal.thirdweb.com/wallets/auth) server.
|
||||
* @auth
|
||||
*/
|
||||
export interface ThirdwebAuthConfig {
|
||||
|
||||
@@ -172,7 +172,7 @@ export function useActiveClaimCondition(
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the claimer proofs for an address for [ERC20](https://portal.thirdweb.com/solidity/base-contracts/erc20drop), [ERC721](https://portal.thirdweb.com/solidity/base-contracts/erc721drop), and [ERC1155](https://portal.thirdweb.com/solidity/base-contracts/erc1155drop) drop contracts.
|
||||
* Hook to get the claimer proofs for an address for [ERC20](https://portal.thirdweb.com/contracts/build/base-contracts/erc-20/drop), [ERC721](https://portal.thirdweb.com/contracts/build/base-contracts/erc-721/drop), and [ERC1155](https://portal.thirdweb.com/contracts/build/base-contracts/erc-1155/drop) drop contracts.
|
||||
*
|
||||
* A claimer proof is a set of data about a claimer's claim condition, including the claimer's address, the claimer's proof, and the claimer's max claimable amount, price, and currency address.
|
||||
*
|
||||
@@ -875,9 +875,9 @@ export function useSetClaimConditions(
|
||||
* Hook for resetting the claim conditions on a drop contract.
|
||||
*
|
||||
* Available to use on contracts that implement
|
||||
* [`ERC721ClaimableWithConditions`](https://portal.thirdweb.com/solidity/extensions/erc721claimconditions),
|
||||
* [`ERC1155ClaimableWithConditions`](https://portal.thirdweb.com/solidity/extensions/erc1155claimconditions) or
|
||||
* [`ERC20ClaimableWithConditions`](https://portal.thirdweb.com/solidity/extensions/erc20claimconditions).
|
||||
* [`ERC721ClaimableWithConditions`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721ClaimConditions),
|
||||
* [`ERC1155ClaimableWithConditions`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155ClaimConditions) or
|
||||
* [`ERC20ClaimableWithConditions`](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20ClaimConditions).
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
|
||||
@@ -27,7 +27,7 @@ import invariant from "tiny-invariant";
|
||||
/**
|
||||
* Hook for getting the primary sales recipient of a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/solidity/extensions/primarysale) interface.
|
||||
* Available to use on contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/contracts/build/extensions/general/PrimarySale) interface.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
@@ -67,7 +67,7 @@ export function usePrimarySaleRecipient(
|
||||
/**
|
||||
* Hook for updating the primary sale recipient on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/solidity/extensions/primarysale) interface.
|
||||
* Available to use on smart contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/contracts/build/extensions/general/PrimarySale) interface.
|
||||
*
|
||||
* The wallet that initiates this transaction must have the required permissions to change the primary sale recipient (defaults to `"admin"` level).
|
||||
*
|
||||
@@ -156,7 +156,7 @@ export function useUpdatePrimarySaleRecipient(
|
||||
/**
|
||||
* Hook for retrieving royalty settings of a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [`Royalty`](https://portal.thirdweb.com/solidity/extensions/royalty) interface.
|
||||
* Available to use on contracts that implement the [`Royalty`](https://portal.thirdweb.com/contracts/build/extensions/general/Royalty) interface.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
@@ -214,7 +214,7 @@ export function useRoyaltySettings(
|
||||
/**
|
||||
* Hook for updating royalty settings on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`Royalty`](https://portal.thirdweb.com/solidity/extensions/royalty) interface.
|
||||
* Available to use on smart contracts that implement the [`Royalty`](https://portal.thirdweb.com/contracts/build/extensions/general/Royalty) interface.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
@@ -310,7 +310,7 @@ export function useUpdateRoyaltySettings(
|
||||
/**
|
||||
* Hook for getting the platform fee settings of a contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/solidity/extensions/platformfee) interface.
|
||||
* Available to use on contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/contracts/build/extensions/general/PlatformFee) interface.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@@ -370,7 +370,7 @@ export function usePlatformFees(
|
||||
/**
|
||||
* Hook for updating platform fees on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/solidity/extensions/platformfee) interface.
|
||||
* Available to use on smart contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/contracts/build/extensions/general/PlatformFee) interface.
|
||||
*
|
||||
* ```jsx
|
||||
* import {
|
||||
@@ -466,7 +466,7 @@ export function useUpdatePlatformFees(
|
||||
/**
|
||||
* Hook for getting the metadata associated with a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [Contract Metadata](https://portal.thirdweb.com/solidity/extensions/contractmetadata) interface.
|
||||
* Available to use on contracts that implement the [Contract Metadata](https://portal.thirdweb.com/contracts/build/extensions/general/ContractMetadata) interface.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
|
||||
@@ -107,7 +107,7 @@ export function useUnclaimedNFTs(
|
||||
/**
|
||||
* Hook for fetching all claimed NFTs from a given NFT Drop contract.
|
||||
*
|
||||
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/solidity/extensions/erc721claimable),
|
||||
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Claimable),
|
||||
* such as the [NFT Drop](https://thirdweb.com/thirdweb.eth/DropERC721).
|
||||
*
|
||||
* @example
|
||||
@@ -174,7 +174,7 @@ export function useClaimedNFTs(
|
||||
*
|
||||
* Unclaimed NFTs are tokens that were lazy-minted but have not yet been claimed by a user.
|
||||
*
|
||||
* Available to use on contracts that implement the [`LazyMint`](https://portal.thirdweb.com/solidity/extensions/lazymint) extension;
|
||||
* Available to use on contracts that implement the [`LazyMint`](https://portal.thirdweb.com/contracts/build/extensions/general/LazyMint) extension;
|
||||
* such as the [NFT Drop](https://thirdweb.com/thirdweb.eth/DropERC721) contract.
|
||||
*
|
||||
* @example
|
||||
@@ -220,7 +220,7 @@ export function useUnclaimedNFTSupply(
|
||||
/**
|
||||
* Hook for retrieving the total supply of NFTs claimed from an NFT Drop contract.
|
||||
*
|
||||
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/solidity/extensions/erc721claimable).
|
||||
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Claimable).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@@ -262,8 +262,8 @@ export function useClaimedNFTSupply(
|
||||
/**
|
||||
* Hook for fetching batches of lazy-minted NFTs that were set to be revealed at a later date, but have not yet been revealed.
|
||||
*
|
||||
* Available to use on contracts that implement the [ERC721Revealable](https://portal.thirdweb.com/solidity/extensions/erc721revealable)
|
||||
* or [ERC1155Revealable](https://portal.thirdweb.com/solidity/extensions/erc1155revealable) interfaces,
|
||||
* Available to use on contracts that implement the [ERC721Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Revealable)
|
||||
* or [ERC1155Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Revealable) interfaces,
|
||||
* such as the [NFT Drop](https://thirdweb.com/thirdweb.eth/DropERC721)
|
||||
* and [Edition Drop](https://thirdweb.com/thirdweb.eth/DropERC1155) smart contracts.
|
||||
*
|
||||
@@ -605,8 +605,8 @@ export function useLazyMint<TContract extends DropContract>(
|
||||
* allowing the owner to set placeholder metadata and reveal the metadata of the NFTs at a later time.
|
||||
*
|
||||
* Available to use on contracts that implement the
|
||||
* [ERC721Revealable](https://portal.thirdweb.com/solidity/extensions/erc721revealable)
|
||||
* or [ERC1155Revealable](https://portal.thirdweb.com/solidity/extensions/erc1155revealable)
|
||||
* [ERC721Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Revealable)
|
||||
* or [ERC1155Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Revealable)
|
||||
* interfaces.
|
||||
*
|
||||
* @example
|
||||
@@ -725,8 +725,8 @@ export function useDelayedRevealLazyMint<TContract extends RevealableContract>(
|
||||
* Hook for revealing a batch of delayed reveal NFTs using [delayed reveal](https://portal.thirdweb.com/glossary/delayed-reveal).
|
||||
*
|
||||
* Available to use on contracts that implement the
|
||||
* [ERC721Revealable](https://portal.thirdweb.com/solidity/extensions/erc721revealable)
|
||||
* or [ERC1155Revealable](https://portal.thirdweb.com/solidity/extensions/erc1155revealable)
|
||||
* [ERC721Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Revealable)
|
||||
* or [ERC1155Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Revealable)
|
||||
* interfaces.
|
||||
*
|
||||
* ```jsx
|
||||
|
||||
@@ -37,8 +37,8 @@ import invariant from "tiny-invariant";
|
||||
/**
|
||||
* Hook for fetching information about an NFT from a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* NFT metadata is automatically fetched from where the `tokenUri` is hosted (e.g. IPFS), and makes the `image`
|
||||
* property available as a URL through our IPFS gateway (if the image is hosted on IPFS).
|
||||
@@ -122,8 +122,8 @@ export function useNFT<TContract extends NFTContract>(
|
||||
/**
|
||||
* Hook to query all NFTs associated with a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* NFT metadata is automatically fetched from where the `tokenUri` is hosted (e.g. IPFS), and makes the `image`
|
||||
* property available as a URL through our IPFS gateway (if the image is hosted on IPFS).
|
||||
@@ -184,8 +184,8 @@ export function useNFTs<TContract extends NFTContract>(
|
||||
/**
|
||||
* Hook to get the total count of **unique** NFTs minted on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* When used for ERC1155 contracts, the total count is the number of unique token IDs minted, _not_ the total supply of all tokens in circulation.
|
||||
*
|
||||
@@ -242,8 +242,8 @@ export function useTotalCount<TContract extends NFTContract>(
|
||||
*
|
||||
* This takes into account the increase in supply due to minting and the decrease in supply due to burning.
|
||||
*
|
||||
* Available to use on contracts that implement either the [ERC721](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* or [ERC1155](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on contracts that implement either the [ERC721](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* or [ERC1155](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@@ -370,9 +370,9 @@ export function useOwnedNFTs<TContract extends NFTContract>(
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the quantity a user owns of a specific [ERC1155](https://portal.thirdweb.com/solidity/extensions/erc1155) NFT.
|
||||
* Hook to get the quantity a user owns of a specific [ERC1155](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) NFT.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on smart contracts that implement the [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
@@ -479,8 +479,8 @@ export function useSharedMetadata(
|
||||
/**
|
||||
* Hook for minting a new NFT on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* By default, the process uploads and pins the NFT metadata to IPFS before minting.
|
||||
*
|
||||
@@ -608,7 +608,7 @@ export function useMintNFT<TContract extends NFTContract>(
|
||||
* Hook for minting additional supply to an _existing_ ERC-1155 token.
|
||||
*
|
||||
* Available to use on contracts that implement the
|
||||
* [ERC1155Mintable](https://portal.thirdweb.com/solidity/extensions/erc1155mintable)
|
||||
* [ERC1155Mintable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Mintable)
|
||||
* interface, such as the [Edition](https://thirdweb.com/thirdweb.eth/TokenERC1155) or [Edition Drop](https://thirdweb.com/thirdweb.eth/DropERC1155).
|
||||
*
|
||||
* The wallet address that initiates this transaction must have minting permissions on the contract.
|
||||
@@ -717,8 +717,8 @@ export function useMintNFTSupply(contract: Erc1155) {
|
||||
* Hook for transferring ERC721 or ERC1155 NFTs to another wallet address.
|
||||
*
|
||||
* Available to use on contracts that implement either the
|
||||
* [ERC721](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* and [ERC1155](https://portal.thirdweb.com/solidity/extensions/erc1155)
|
||||
* [ERC721](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* and [ERC1155](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155)
|
||||
* interfaces, such as the [Edition](https://thirdweb.com/thirdweb.eth/TokenERC1155)
|
||||
* or [NFT Collection](https://thirdweb.com/thirdweb.eth/TokenERC721).
|
||||
*
|
||||
@@ -834,7 +834,7 @@ export function useTransferNFT<TContract extends NFTContract>(
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for airdropping [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) NFT tokens to multiple wallet addresses at once.
|
||||
* Hook for airdropping [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) NFT tokens to multiple wallet addresses at once.
|
||||
*
|
||||
* Available to use on smart contracts that implement the `ERC1155` standard.
|
||||
*
|
||||
@@ -915,8 +915,8 @@ export function useAirdropNFT(contract: Erc1155) {
|
||||
/**
|
||||
* Hook for burning a NFT on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
|
||||
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
|
||||
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
|
||||
@@ -48,7 +48,7 @@ type GetAllReturnType<TContract extends ContractWithRoles> = Promise<
|
||||
/**
|
||||
* Hook for getting all wallet addresses that have a role in a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions).
|
||||
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@@ -116,7 +116,7 @@ export function useAllRoleMembers<TContract extends ContractWithRoles>(
|
||||
/**
|
||||
* Hook for getting all wallet addresses that have a specific role in a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface.
|
||||
* Available to use on contracts that implement the [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
@@ -173,7 +173,7 @@ export function useRoleMembers<TContract extends ContractWithRoles>(
|
||||
/**
|
||||
* Hook to check if an address is a member of a role on a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface
|
||||
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface
|
||||
*
|
||||
* Provide the following arguments to the hook:
|
||||
*
|
||||
@@ -305,7 +305,7 @@ export function useSetAllRoleMembers<TContract extends ContractWithRoles>(
|
||||
/**
|
||||
* Hook for granting a role on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface.
|
||||
* Available to use on smart contracts that implement the [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
@@ -403,7 +403,7 @@ export function useGrantRole<TContract extends ContractWithRoles>(
|
||||
/**
|
||||
* Hook for revoking a wallet address from a role on a smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface
|
||||
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface
|
||||
*
|
||||
* The wallet address that initiates this transaction must have the relevant permissions on the contract to remove the role from the wallet address (typically `"admin"` level required).
|
||||
*
|
||||
|
||||
@@ -138,7 +138,7 @@ export function useTokenBalance(
|
||||
|
||||
/**
|
||||
* Hook for fetching the [decimals](https://docs.openzeppelin.com/contracts/3.x/erc20#a-note-on-decimals)
|
||||
* of an [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) token.
|
||||
* of an [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) token.
|
||||
*
|
||||
* Tokens usually opt for a value of `18`, imitating the relationship between Ether and Wei.
|
||||
* Therefore, `18` is the default value returned by this function, unless your ERC20 contract explicitly overrides it.
|
||||
@@ -187,7 +187,7 @@ export function useTokenDecimals(contract: RequiredParam<TokenContract>) {
|
||||
/**
|
||||
* Hook for minting new tokens in an ERC20 smart contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [ERC20Mintable](https://portal.thirdweb.com/solidity/extensions/erc20mintable)
|
||||
* Available to use on contracts that implement the [ERC20Mintable](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20BatchMintable)
|
||||
* interface, such as the [Token](https://thirdweb.com/thirdweb.eth/TokenERC20) contract.
|
||||
*
|
||||
* The wallet address that initiates this transaction must have minting permissions on the contract.
|
||||
@@ -286,8 +286,8 @@ export function useMintToken(
|
||||
/**
|
||||
* Hook for claiming a ERC20 tokens from a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement both the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) interface
|
||||
* and the [`claim`](https://portal.thirdweb.com/solidity/extensions/erc721claimable) function,
|
||||
* Available to use on smart contracts that implement both the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) interface
|
||||
* and the [`claim`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Claimable) function,
|
||||
* such as the [Token Drop](https://thirdweb.com/thirdweb.eth/DropERC20).
|
||||
*
|
||||
* @example
|
||||
@@ -374,7 +374,7 @@ export function useClaimToken(contract: RequiredParam<TokenContract>) {
|
||||
/**
|
||||
* Hook for transferring tokens on an ERC20 contract.
|
||||
*
|
||||
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20)
|
||||
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20)
|
||||
* interface, such as the [Token](https://thirdweb.com/thirdweb.eth/TokenERC20) contract.
|
||||
*
|
||||
* The wallet address that initiates this transaction must have a balance of tokens
|
||||
@@ -518,7 +518,7 @@ export function useTransferNativeToken() {
|
||||
/**
|
||||
* Hook for transferring ERC20 tokens to multiple recipients in a single transaction (i.e. airdrop).
|
||||
*
|
||||
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) interface.
|
||||
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) interface.
|
||||
*
|
||||
* The wallet that initiates this transaction must have sufficient balance to cover the total amount of tokens being transferred
|
||||
* and must have transfer permissions on the contract, i.e. tokens are not soulbound.
|
||||
@@ -620,7 +620,7 @@ export function useTransferBatchToken(contract: RequiredParam<TokenContract>) {
|
||||
/**
|
||||
* Hook for burning ERC20 tokens on a smart contract.
|
||||
*
|
||||
* Available to use on smart contracts that implement the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) standard.
|
||||
* Available to use on smart contracts that implement the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) standard.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
|
||||
@@ -7,7 +7,7 @@ import invariant from "tiny-invariant";
|
||||
import { AUTH_TOKEN_STORAGE_KEY } from "../../../core/constants/auth";
|
||||
|
||||
/**
|
||||
* Hook to prompt the user to sign in with their wallet using [auth](https://portal.thirdweb.com/auth)
|
||||
* Hook to prompt the user to sign in with their wallet using [auth](https://portal.thirdweb.com/wallets/auth)
|
||||
*
|
||||
* Requires the `authConfig` prop to be configured on the `ThirdwebProvider`
|
||||
*
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface UserWithData<
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for retrieving information about the currently signed-in user using [auth](https://portal.thirdweb.com/auth).
|
||||
* Hook for retrieving information about the currently signed-in user using [auth](https://portal.thirdweb.com/wallets/auth).
|
||||
*
|
||||
* Useful to get the user's address and session data, or `undefined` if no user is signed in.
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useSDK } from "../useSDK";
|
||||
/**
|
||||
* Get the instance of the `ThirdwebStorage` class being used by the `ThirdwebProvider`
|
||||
*
|
||||
* Allows you to use the TypeScript SDK functionality of [Storage](https://portal.thirdweb.com/storage) in your React app.
|
||||
* Allows you to use the TypeScript SDK functionality of [Storage](https://portal.thirdweb.com/infrastructure/storage/overview) in your React app.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
|
||||
@@ -226,7 +226,7 @@ const WrappedThirdwebSDKProvider = <TChains extends Chain[]>({
|
||||
* This Overrides any of the default values for the SDK. If not provided, it uses sensible defaults.
|
||||
*
|
||||
* ### storageInterface (optional)
|
||||
* Override the default [Storage](https://portal.thirdweb.com/storage) interface used by the SDK.
|
||||
* Override the default [Storage](https://portal.thirdweb.com/infrastructure/storage/overview) interface used by the SDK.
|
||||
*
|
||||
* It allows you to create an instance of `ThirdwebStorage` with your own customized config, and pass it to the SDK.
|
||||
*
|
||||
@@ -265,7 +265,7 @@ const WrappedThirdwebSDKProvider = <TChains extends Chain[]>({
|
||||
* ```
|
||||
*
|
||||
* ### authConfig (optional)
|
||||
* The configuration object for setting up [Auth](https://portal.thirdweb.com/auth); allowing users to sign in with their wallet.
|
||||
* The configuration object for setting up [Auth](https://portal.thirdweb.com/wallets/auth); allowing users to sign in with their wallet.
|
||||
*
|
||||
* ### secretKey (optional)
|
||||
* secretKey for thirdweb services. This is only required if server side rendering is being used.
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface ThirdwebSDKProviderProps<TChains extends Chain[]>
|
||||
sdkOptions?: Omit<SDKOptions, "chains">;
|
||||
|
||||
/**
|
||||
* Override the default [Storage](https://portal.thirdweb.com/storage) interface used by the SDK.
|
||||
* Override the default [Storage](https://portal.thirdweb.com/infrastructure/storage/overview) interface used by the SDK.
|
||||
*
|
||||
* It allows you to create an instance of `ThirdwebStorage` with your own customized config, and pass it to the SDK.
|
||||
*
|
||||
@@ -89,7 +89,7 @@ export interface ThirdwebSDKProviderProps<TChains extends Chain[]>
|
||||
storageInterface?: IThirdwebStorage;
|
||||
|
||||
/**
|
||||
* The configuration object for setting up [Auth](https://portal.thirdweb.com/auth); allowing users to sign in with their wallet.
|
||||
* The configuration object for setting up [Auth](https://portal.thirdweb.com/wallets/auth); allowing users to sign in with their wallet.
|
||||
*/
|
||||
authConfig?: ThirdwebAuthConfig;
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/react-native-compat
|
||||
|
||||
## 0.5.5
|
||||
|
||||
## 0.5.4
|
||||
|
||||
## 0.5.3
|
||||
|
||||
## 0.5.2
|
||||
|
||||
## 0.5.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native-compat",
|
||||
"description": "Shims for Thirdweb in React Native Projects",
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.5",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,44 @@
|
||||
# @thirdweb-dev/react-native
|
||||
|
||||
## 0.5.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2119](https://github.com/thirdweb-dev/js/pull/2119) [`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc) Thanks [@MananTank](https://github.com/MananTank)! - Update Links to Docs website
|
||||
|
||||
- [#2092](https://github.com/thirdweb-dev/js/pull/2092) [`4c3c840d`](https://github.com/thirdweb-dev/js/commit/4c3c840d8815915840d0819932aa0a858c5d3184) Thanks [@iketw](https://github.com/iketw)! - Export WalletConnect modal component
|
||||
|
||||
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
|
||||
- @thirdweb-dev/react-core@4.2.2
|
||||
- @thirdweb-dev/wallets@2.3.4
|
||||
- @thirdweb-dev/sdk@4.0.25
|
||||
|
||||
## 0.5.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/react-core@4.2.1
|
||||
|
||||
## 0.5.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2101](https://github.com/thirdweb-dev/js/pull/2101) [`7fdb1715`](https://github.com/thirdweb-dev/js/commit/7fdb1715e4263e9b9c90370a1e70c7fa1fa0e95e) Thanks [@iketw](https://github.com/iketw)! - Export types for better reference docs
|
||||
|
||||
- [#2108](https://github.com/thirdweb-dev/js/pull/2108) [`b90fa2cc`](https://github.com/thirdweb-dev/js/commit/b90fa2cc298bf1a6deb0f4c0f927ab77f2716448) Thanks [@iketw](https://github.com/iketw)! - Adds `hideSendButton` and `hideReceiveButton` to the ConnectWallet component.
|
||||
|
||||
Both props default to `false`.
|
||||
|
||||
```js
|
||||
<ConnectWallet hideReceiveButton={true} hideSendButton={true} />
|
||||
```
|
||||
|
||||
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native",
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.5",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -24,15 +24,29 @@ import { SupportedTokens, defaultTokens } from "./SendFunds/defaultTokens";
|
||||
|
||||
export type ConnectWalletProps = {
|
||||
/**
|
||||
* render a custom button to display the connected wallet details instead of the default button
|
||||
* Renders a custom button to display the connected wallet details instead of the default button
|
||||
*/
|
||||
detailsButton?: ConnectWalletDetailsProps["detailsButton"];
|
||||
|
||||
/**
|
||||
* render custom rows in the Connect Wallet Details modal
|
||||
* Renders custom rows in the Connect Wallet Details modal
|
||||
*/
|
||||
extraRows?: ConnectWalletDetailsProps["extraRows"];
|
||||
|
||||
/**
|
||||
* Option to hide the Send button in the wallet details modal.
|
||||
*
|
||||
* The default is `false`
|
||||
*/
|
||||
hideSendButton?: boolean;
|
||||
|
||||
/**
|
||||
* Option to hide the Receive button in the wallet details modal.
|
||||
*
|
||||
* The default is `false`
|
||||
*/
|
||||
hideReceiveButton?: boolean;
|
||||
|
||||
/**
|
||||
* Hide option to request testnet funds for testnets in dropdown
|
||||
*
|
||||
@@ -42,7 +56,7 @@ export type ConnectWalletProps = {
|
||||
|
||||
/**
|
||||
* Whether to show "Switch Network" button if the wallet is connected,
|
||||
* but it is not connected to the `activeChain` provided in `ThirdwebProvider`
|
||||
* but it is not connected to the `activeChain` provided in [`ThirdwebProvider`](https://portal.thirdweb.com/react-native/v0/ThirdwebProvider)
|
||||
*
|
||||
* Please, note that if you support multiple networks in your app this prop should
|
||||
* be set to `false` to allow users to switch between networks.
|
||||
@@ -84,6 +98,8 @@ export const ConnectWallet = ({
|
||||
buttonTitle,
|
||||
modalTitle,
|
||||
modalTitleIconUrl,
|
||||
hideReceiveButton,
|
||||
hideSendButton,
|
||||
extraRows,
|
||||
hideTestnetFaucet,
|
||||
displayBalanceToken,
|
||||
@@ -169,6 +185,8 @@ export const ConnectWallet = ({
|
||||
detailsButton={detailsButton}
|
||||
extraRows={extraRows}
|
||||
hideTestnetFaucet={hideTestnetFaucet}
|
||||
hideReceiveButton={hideReceiveButton}
|
||||
hideSendButton={hideSendButton}
|
||||
supportedTokens={supportedTokensMemo}
|
||||
displayBalanceToken={displayBalanceToken}
|
||||
hideSwitchToPersonalWallet={hideSwitchToPersonalWallet}
|
||||
|
||||
+27
-9
@@ -45,6 +45,8 @@ export const ConnectWalletDetailsModal = ({
|
||||
supportedTokens,
|
||||
displayBalanceToken,
|
||||
hideSwitchToPersonalWallet,
|
||||
hideReceiveButton,
|
||||
hideSendButton,
|
||||
}: {
|
||||
isVisible: boolean;
|
||||
onClosePress: () => void;
|
||||
@@ -54,6 +56,8 @@ export const ConnectWalletDetailsModal = ({
|
||||
supportedTokens: SupportedTokens;
|
||||
displayBalanceToken?: Record<number, string>;
|
||||
hideSwitchToPersonalWallet?: boolean;
|
||||
hideReceiveButton?: boolean;
|
||||
hideSendButton?: boolean;
|
||||
}) => {
|
||||
const l = useLocale();
|
||||
const [isExportModalVisible, setIsExportModalVisible] = useState(false);
|
||||
@@ -143,7 +147,7 @@ export const ConnectWalletDetailsModal = ({
|
||||
if (activeWallet?.walletId === walletIds.localWallet) {
|
||||
return (
|
||||
<>
|
||||
<View style={styles.currentNetwork}>
|
||||
<View style={styles.additionalActions}>
|
||||
<Text variant="bodySmallSecondary">
|
||||
{l.connect_wallet_details.additional_actions}
|
||||
</Text>
|
||||
@@ -267,14 +271,19 @@ export const ConnectWalletDetailsModal = ({
|
||||
</Text>
|
||||
</Box>
|
||||
) : null}
|
||||
<Box
|
||||
flexDirection="row"
|
||||
justifyContent="space-evenly"
|
||||
marginVertical="md"
|
||||
>
|
||||
<SendButton supportedTokens={supportedTokens} />
|
||||
<ReceiveButton />
|
||||
</Box>
|
||||
{hideReceiveButton && hideSendButton ? null : (
|
||||
<Box
|
||||
flexDirection="row"
|
||||
justifyContent="space-evenly"
|
||||
gap="xs"
|
||||
marginTop="md"
|
||||
>
|
||||
{hideSendButton ? null : (
|
||||
<SendButton supportedTokens={supportedTokens} />
|
||||
)}
|
||||
{hideReceiveButton ? null : <ReceiveButton />}
|
||||
</Box>
|
||||
)}
|
||||
<View style={styles.currentNetwork}>
|
||||
<Text variant="bodySmallSecondary">
|
||||
{l.connect_wallet_details.current_network}
|
||||
@@ -353,6 +362,15 @@ const styles = StyleSheet.create({
|
||||
minWidth: 200,
|
||||
},
|
||||
currentNetwork: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "flex-start",
|
||||
marginBottom: 8,
|
||||
marginTop: 28,
|
||||
},
|
||||
additionalActions: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignContent: "flex-start",
|
||||
|
||||
@@ -42,6 +42,20 @@ export type ConnectWalletDetailsProps = {
|
||||
* The default is `false`
|
||||
*/
|
||||
hideSwitchToPersonalWallet?: boolean;
|
||||
|
||||
/**
|
||||
* Option to hide the Send button in the wallet details modal.
|
||||
*
|
||||
* The default is `false`
|
||||
*/
|
||||
hideSendButton?: boolean;
|
||||
|
||||
/**
|
||||
* Option to hide the Receive button in the wallet details modal.
|
||||
*
|
||||
* The default is `false`
|
||||
*/
|
||||
hideReceiveButton?: boolean;
|
||||
};
|
||||
|
||||
export const WalletDetailsButton = ({
|
||||
@@ -49,6 +63,8 @@ export const WalletDetailsButton = ({
|
||||
detailsButton,
|
||||
extraRows,
|
||||
hideTestnetFaucet,
|
||||
hideReceiveButton,
|
||||
hideSendButton,
|
||||
supportedTokens,
|
||||
displayBalanceToken,
|
||||
hideSwitchToPersonalWallet,
|
||||
@@ -84,6 +100,8 @@ export const WalletDetailsButton = ({
|
||||
supportedTokens={supportedTokens}
|
||||
displayBalanceToken={displayBalanceToken}
|
||||
hideSwitchToPersonalWallet={hideSwitchToPersonalWallet}
|
||||
hideReceiveButton={hideReceiveButton}
|
||||
hideSendButton={hideSendButton}
|
||||
/>
|
||||
{detailsButton ? (
|
||||
detailsButton({ onPress })
|
||||
|
||||
@@ -120,7 +120,7 @@ export const ConnectWalletFlow = () => {
|
||||
connected={handleClose}
|
||||
isOpen={modalVisible}
|
||||
show={onOpenModal}
|
||||
hide={() => {}}
|
||||
hide={handleClose}
|
||||
walletConfig={activeWallet}
|
||||
supportedWallets={supportedWallets}
|
||||
selectionData={selectionData}
|
||||
|
||||
+2
@@ -25,6 +25,7 @@ export const SmartWalletFlow = ({
|
||||
goBack,
|
||||
walletConfig,
|
||||
personalWalletConfig,
|
||||
hide,
|
||||
...props
|
||||
}: ConnectUIProps<SmartWallet> & { personalWalletConfig: WalletConfig }) => {
|
||||
const l = useLocale();
|
||||
@@ -124,6 +125,7 @@ export const SmartWalletFlow = ({
|
||||
<PersonalWalletConfigUI
|
||||
{...props}
|
||||
connected={connected}
|
||||
hide={hide}
|
||||
goBack={goBack}
|
||||
walletConfig={personalWalletConfig}
|
||||
onLocallyConnected={onPersonalWalletConnected}
|
||||
|
||||
@@ -33,7 +33,6 @@ export const ReceiveButton = () => {
|
||||
<>
|
||||
<IconTextButton
|
||||
flex={1}
|
||||
ml="xs"
|
||||
text={l.common.receive}
|
||||
justifyContent="center"
|
||||
icon={
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useGlobalTheme, useUIContext } from "../providers/ui-context-provider";
|
||||
|
||||
export type ActionFn = (contract: SmartContract) => Promise<any>;
|
||||
|
||||
interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
export interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
contractAddress: `0x${string}` | `${string}.eth` | string;
|
||||
contractAbi?: ContractInterface;
|
||||
|
||||
@@ -43,7 +43,7 @@ interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
/**
|
||||
* A component that allows the user to call an on-chain function on a contract.
|
||||
*
|
||||
* The button has to be wrapped in a `ThirdwebProvider` in order to function.
|
||||
* The button has to be wrapped in a [`ThirdwebProvider`](https://portal.thirdweb.com/react-native/v0/ThirdwebProvider) in order to function.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
|
||||
@@ -14,6 +14,8 @@ export function TWModal(props: Partial<ModalProps>) {
|
||||
useNativeDriver
|
||||
hideModalContentWhileAnimating={true}
|
||||
style={{ maxWidth: MAX_WIDTH }}
|
||||
animationIn="slideInUp" // Slide in from bottom
|
||||
animationOut="slideOutDown" // Slide out to bottom
|
||||
>
|
||||
{props.children}
|
||||
</Modal>
|
||||
|
||||
@@ -38,7 +38,13 @@ export {
|
||||
ConnectWallet,
|
||||
type ConnectWalletProps,
|
||||
} from "./components/ConnectWallet";
|
||||
export { Web3Button, type ActionFn } from "./components/Web3Button";
|
||||
export {
|
||||
Web3Button,
|
||||
type Web3ButtonProps,
|
||||
type ActionFn,
|
||||
} from "./components/Web3Button";
|
||||
|
||||
export { WalletConnectUI } from "./wallets/wallets/wallet-connect/WalletConnectUI";
|
||||
|
||||
// utilities
|
||||
export * from "./utils/uri";
|
||||
@@ -61,11 +67,19 @@ export { ConnectWalletHeader } from "./components/ConnectWalletFlow/ConnectingWa
|
||||
export * from "./assets";
|
||||
|
||||
// providers
|
||||
// export * from "./providers/full";
|
||||
export * from "./providers/thirdweb-provider";
|
||||
export {
|
||||
ThirdwebProvider,
|
||||
type DefaultChains,
|
||||
type ThirdwebProviderProps,
|
||||
} from "./providers/thirdweb-provider";
|
||||
|
||||
// re-export everything from react-core
|
||||
export * from "@thirdweb-dev/react-core";
|
||||
|
||||
// ThirdwebSDK RN
|
||||
export { ThirdwebSDK } from "./sdk/ThirdwebSDK";
|
||||
|
||||
export {
|
||||
type SupportedTokens,
|
||||
defaultTokens,
|
||||
} from "./components/SendFunds/defaultTokens";
|
||||
|
||||
@@ -18,7 +18,7 @@ import { walletIds } from "@thirdweb-dev/wallets";
|
||||
import { ThirdwebStorage } from "../../core/storage/storage";
|
||||
import type { Locale } from "../i18n/types";
|
||||
|
||||
interface ThirdwebProviderProps<TChains extends Chain[]>
|
||||
export interface ThirdwebProviderProps<TChains extends Chain[]>
|
||||
extends Omit<
|
||||
ThirdwebProviderCoreProps<TChains>,
|
||||
"supportedWallets" | "secretKey" | "signer" | "theme"
|
||||
|
||||
@@ -10,9 +10,7 @@ import { MagicConnector } from "../connectors/magic/magic-connector";
|
||||
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
|
||||
* To learn more, please see:
|
||||
*
|
||||
* Our release notes: https://portal.thirdweb.com/embedded-wallet
|
||||
*
|
||||
* Our documentation: https://portal.thirdweb.com/react-native/react-native.embeddedwallet
|
||||
* Checkout the [Release Notes](https://portal.thirdweb.com/wallets/embedded-wallet/overview) and [Documentation](https://portal.thirdweb.com/react-native/v0/wallets/embedded-wallet) for more information.
|
||||
*/
|
||||
export class MagicLink extends AbstractClientWallet<
|
||||
MagicLinkOptions,
|
||||
|
||||
@@ -29,9 +29,7 @@ import { useGlobalTheme, useLocale } from "../../providers/ui-context-provider";
|
||||
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
|
||||
* To learn more, please see:
|
||||
*
|
||||
* Our release notes: https://portal.thirdweb.com/embedded-wallet
|
||||
*
|
||||
* Our documentation: https://portal.thirdweb.com/react-native/react-native.embeddedwallet
|
||||
* Check out [Release Note](https://portal.thirdweb.com/wallets/embedded-wallet/overview) and [Documentation](https://portal.thirdweb.com/react-native/v0/wallets/embedded-wallet) for more details.
|
||||
*/
|
||||
export const magicLink = (
|
||||
magicLinkOptions: MagicLinkOptions & { recommended?: boolean },
|
||||
|
||||
@@ -43,8 +43,21 @@ export function WalletConnectUI({
|
||||
walletConfig,
|
||||
projectId,
|
||||
goBack,
|
||||
hide,
|
||||
supportedWallets,
|
||||
}: ConnectUIProps<WalletConnect> & { projectId: string }) {
|
||||
isVisible,
|
||||
}: Omit<
|
||||
ConnectUIProps<WalletConnect>,
|
||||
| "isOpen"
|
||||
| "show"
|
||||
| "theme"
|
||||
| "selectionData"
|
||||
| "setSelectionData"
|
||||
| "modalSize"
|
||||
> & {
|
||||
projectId: string;
|
||||
isVisible?: boolean;
|
||||
}) {
|
||||
const l = useLocale();
|
||||
const theme = useGlobalTheme();
|
||||
const [wallets, setWallets] = useState<WCWallet[]>([]);
|
||||
@@ -55,6 +68,9 @@ export function WalletConnectUI({
|
||||
const createWalletInstance = useCreateWalletInstance();
|
||||
const setConnectedWallet = useSetConnectedWallet();
|
||||
const setConnectionStatus = useSetConnectionStatus();
|
||||
const [isVisibleInternal, setIsVisibleInternal] = useState<
|
||||
boolean | undefined
|
||||
>(isVisible);
|
||||
|
||||
const onChangeText = useDebounceCallback({ callback: setSearch });
|
||||
|
||||
@@ -118,6 +134,10 @@ export function WalletConnectUI({
|
||||
};
|
||||
}, [projectId]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisibleInternal(isVisible);
|
||||
}, [isVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wallets && search) {
|
||||
setSearchWallets(
|
||||
@@ -145,18 +165,24 @@ export function WalletConnectUI({
|
||||
console.error(`Error connecting with WalletConnect: ${e}`);
|
||||
})
|
||||
.finally(() => {
|
||||
connected();
|
||||
setIsVisibleInternal(false);
|
||||
setTimeout(() => {
|
||||
connected();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onClosePress = () => {
|
||||
setConnectionStatus("disconnected");
|
||||
connected();
|
||||
setIsVisibleInternal(false);
|
||||
setTimeout(() => {
|
||||
hide();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<TWModal
|
||||
isVisible={true}
|
||||
isVisible={isVisibleInternal ?? true}
|
||||
onBackdropPress={onClosePress}
|
||||
backdropOpacity={0.7}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,43 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 4.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#2119](https://github.com/thirdweb-dev/js/pull/2119) [`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc) Thanks [@MananTank](https://github.com/MananTank)! - Update Links to Docs website
|
||||
|
||||
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
|
||||
- @thirdweb-dev/react-core@4.2.2
|
||||
- @thirdweb-dev/wallets@2.3.4
|
||||
- @thirdweb-dev/sdk@4.0.25
|
||||
|
||||
## 4.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`d00ca7fd`](https://github.com/thirdweb-dev/js/commit/d00ca7fd2a0fa3e694bb37d1f486d3d4cf1ad79e)]:
|
||||
- @thirdweb-dev/payments@1.0.1
|
||||
- @thirdweb-dev/react-core@4.2.1
|
||||
|
||||
## 4.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1913](https://github.com/thirdweb-dev/js/pull/1913) [`ed3afd41`](https://github.com/thirdweb-dev/js/commit/ed3afd418cb6e0d711814a1c52afb302460751ba) Thanks [@IDubuque](https://github.com/IDubuque)! - Migrated `@paperxyz/react-client-sdk` into `@thirdweb-dev/react` to provide checkout components under the thirdweb react sdk. The `@paperxyz/react-client-sdk` package will be continued within the `@thirdweb-dev/react` package, aligning with the new branding and expanded capabilities under thirdweb. It inherits the core functionality and components of Paper's developer-first NFT checkout solution.
|
||||
|
||||
### Added
|
||||
|
||||
- Integrated the `@paper-xyz/react-client-sdk` components into `@thirdweb-dev/react`. This update brings all the Paper.xyz React components into the thirdweb ecosystem, providing a seamless developer experience for building NFT-powered applications.
|
||||
- New components from Paper.xyz for NFT checkouts, enhancing the existing thirdweb SDK capabilities.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8), [`ed3afd41`](https://github.com/thirdweb-dev/js/commit/ed3afd418cb6e0d711814a1c52afb302460751ba)]:
|
||||
- @thirdweb-dev/sdk@4.0.24
|
||||
- @thirdweb-dev/wallets@2.3.3
|
||||
- @thirdweb-dev/payments@1.0.0
|
||||
- @thirdweb-dev/react-core@4.2.0
|
||||
|
||||
## 4.1.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "4.1.17",
|
||||
"version": "4.2.2",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -110,6 +110,8 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@emotion/css": "11.10.5",
|
||||
"@headlessui/react": "1.7.6",
|
||||
"@google/model-viewer": "^2.1.1",
|
||||
"@radix-ui/colors": "^0.1.9",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
@@ -122,8 +124,10 @@
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@tanstack/react-query": "^4.33.0",
|
||||
"@thirdweb-dev/chains": "workspace:*",
|
||||
"@thirdweb-dev/sdk": "workspace:*",
|
||||
"@thirdweb-dev/react-core": "workspace:*",
|
||||
"@thirdweb-dev/wallets": "workspace:*",
|
||||
"@thirdweb-dev/payments": "workspace:*",
|
||||
"buffer": "^6.0.3",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"detect-browser": "^5.3.0",
|
||||
|
||||
@@ -110,7 +110,7 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
style?: React.CSSProperties;
|
||||
|
||||
/**
|
||||
* Web3Button renders a `ConnectWallet` if no wallet is connected. You can pass props for that component by passing a `connectWallet` prop to Web3Button
|
||||
* Web3Button renders a [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) if no wallet is connected. You can pass props for that component by passing a `connectWallet` prop to Web3Button
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
@@ -136,7 +136,7 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
btnTitle?: string;
|
||||
|
||||
/**
|
||||
* title of the `ConnectWallet` Modal.
|
||||
* title of the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) Modal.
|
||||
*
|
||||
* The default is `"Connect"`.
|
||||
*/
|
||||
@@ -193,9 +193,9 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
*
|
||||
* It ensures the following criteria before attempting to call the contract function:
|
||||
*
|
||||
* 1. There is a connected wallet (if there is not, it renders a `ConnectWallet` component instead.
|
||||
* 1. There is a connected wallet (if there is not, it renders a [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component instead.
|
||||
*
|
||||
* 2. The connected wallet is on the correct network as specified in the `ThirdwebProvider`'s `activeChain` prop. if it is not, it renders a "switch network" button instead.
|
||||
* 2. The connected wallet is on the correct network as specified in the [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `activeChain` prop. if it is not, it renders a "switch network" button instead.
|
||||
*
|
||||
* If the action you are performing is async, make sure to return a `Promise` from the action function so that the SDK knows when the action is complete. This can be done by either using async/await or by returning a `Promise`.
|
||||
*
|
||||
@@ -220,7 +220,7 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
|
||||
* @param props -
|
||||
* The props for the component.
|
||||
*
|
||||
* Refer to [Web3ButtonProps](https://portal.thirdweb.com/references/react/Web3ButtonProps) for more details.
|
||||
* Refer to [Web3ButtonProps](https://portal.thirdweb.com/references/react/v4/Web3ButtonProps) for more details.
|
||||
*
|
||||
*/
|
||||
export const Web3Button = <TAction extends ActionFn>(
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { SafeConnectionArgs } from "@thirdweb-dev/wallets";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated use `ConnectWallet` or `useConnect` instead
|
||||
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) instead
|
||||
* @walletConnection
|
||||
* @internal
|
||||
*/
|
||||
@@ -23,7 +23,7 @@ export function useSafe() {
|
||||
// for backwards compatibility
|
||||
|
||||
/**
|
||||
* @deprecated use `ConnectWallet` or `useConnect` instead
|
||||
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) instead
|
||||
* @walletConnection
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { MagicLinkAdditionalOptions } from "@thirdweb-dev/wallets/evm/wallets/ma
|
||||
import { MagicLinkConnectOptions } from "@thirdweb-dev/wallets";
|
||||
|
||||
/**
|
||||
* @deprecated use `ConnectWallet` or `useConnect` instead
|
||||
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) instead
|
||||
* @walletConnection
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { BloctoWalletConfigOptions } from "../../../wallet/wallets/blocto/blocto
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
* @walletConnection
|
||||
*/
|
||||
export function useBloctoWallet() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* @walletConnection
|
||||
* @deprecated use `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
* @internal
|
||||
*/
|
||||
export function useCoinbaseWallet() {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
/**
|
||||
* Hook to connect `EmbeddedWallet` which allows users to login via Email or social logins
|
||||
*
|
||||
* The `embeddedWallet()` should be added to `ThirdwebProvider`'s `supportedWallets` prop to enable auto-connection on page load
|
||||
* The `embeddedWallet()` should be added to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `supportedWallets` prop to enable auto-connection on page load
|
||||
*
|
||||
* @example
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
* @internal
|
||||
*/
|
||||
export function useFrameWallet() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated Use `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated Use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
* @internal
|
||||
*/
|
||||
export function useMetamask() {
|
||||
|
||||
@@ -15,7 +15,7 @@ type PaperConfig = Omit<PaperWalletAdditionalOptions, "chain">;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the `useEmbeddedWallet` hook instead
|
||||
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the [`useEmbeddedWallet`](https://portal.thirdweb.com/references/react/v4/useEmbeddedWallet) hook instead
|
||||
*/
|
||||
export function usePaperWallet() {
|
||||
const connect = useConnect();
|
||||
@@ -35,7 +35,7 @@ export function usePaperWallet() {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the `useEmbeddedWallet` hook instead
|
||||
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the [`useEmbeddedWallet`](https://portal.thirdweb.com/references/react/v4/useEmbeddedWallet) hook instead
|
||||
*/
|
||||
export function usePaperWalletUserEmail(): UseQueryResult<
|
||||
string | undefined,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
* @internal
|
||||
* @walletConnection
|
||||
*/
|
||||
|
||||
@@ -16,11 +16,11 @@ import { useCallback } from "react";
|
||||
import type { BytesLike } from "ethers";
|
||||
|
||||
/**
|
||||
* Hook to connect [Smart wallet](https://portal.thirdweb.com/wallet/smart-wallet)
|
||||
* Hook to connect [Smart wallet](https://portal.thirdweb.com/references/wallets/v2/SmartWallet)
|
||||
*
|
||||
* `useSmartWallet()` handles connecting both the personal wallet and the Smart Wallet.
|
||||
*
|
||||
* The `smartWallet()` also need to be added in `ThirdwebProvider`'s `supportedWallets` prop to enable auto-connection on page load
|
||||
* The `smartWallet()` also need to be added in [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `supportedWallets` prop to enable auto-connection on page load
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
|
||||
import { useCallback } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
* @internal
|
||||
* @walletConnection
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,7 @@ export function useWalletConnectV1() {
|
||||
/**
|
||||
* @internal
|
||||
* @walletConnection
|
||||
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
|
||||
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
|
||||
*/
|
||||
export function useWalletConnect() {
|
||||
const connect = useConnect();
|
||||
|
||||
@@ -320,7 +320,7 @@ export function enDefault() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function will return the default English locale object to be set on `ThirdwebProvider` to localize the thirdweb components.
|
||||
* Calling this function will return the default English locale object to be set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) to localize the thirdweb components.
|
||||
*
|
||||
* You can also overrides parts of the default locale object by passing an object with the same structure as the default locale object and only those parts will be overridden.
|
||||
*
|
||||
@@ -339,7 +339,7 @@ export function enDefault() {
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Pass it to `ThirdwebProvider`'s `locale` prop to localize the thirdweb components.
|
||||
* Pass it to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `locale` prop to localize the thirdweb components.
|
||||
*
|
||||
* ```tsx
|
||||
* function Example() {
|
||||
|
||||
@@ -328,7 +328,7 @@ export function esDefault(): ThirdwebLocale {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function will return the default Spanish locale object to be set on `ThirdwebProvider` to localize the thirdweb components.
|
||||
* Calling this function will return the default Spanish locale object to be set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) to localize the thirdweb components.
|
||||
*
|
||||
* You can also overrides parts of the default locale object by passing an object with the same structure as the default locale object and only those parts will be overridden.
|
||||
*
|
||||
@@ -347,7 +347,7 @@ export function esDefault(): ThirdwebLocale {
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Pass it to `ThirdwebProvider`'s `locale` prop to localize the thirdweb components.
|
||||
* Pass it to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `locale` prop to localize the thirdweb components.
|
||||
*
|
||||
* ```tsx
|
||||
* function Example() {
|
||||
|
||||
@@ -322,7 +322,7 @@ export function jaDefault(): ThirdwebLocale {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function will return the default Japanese locale object to be set on `ThirdwebProvider` to localize the thirdweb components.
|
||||
* Calling this function will return the default Japanese locale object to be set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) to localize the thirdweb components.
|
||||
*
|
||||
* You can also overrides parts of the default locale object by passing an object with the same structure as the default locale object and only those parts will be overridden.
|
||||
*
|
||||
@@ -341,7 +341,7 @@ export function jaDefault(): ThirdwebLocale {
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Pass it to `ThirdwebProvider`'s `locale` prop to localize the thirdweb components.
|
||||
* Pass it to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `locale` prop to localize the thirdweb components.
|
||||
*
|
||||
* ```tsx
|
||||
* function Example() {
|
||||
|
||||
@@ -381,7 +381,7 @@ export type DefaultChains = typeof defaultChains;
|
||||
* ```
|
||||
*
|
||||
* ### authConfig (optional)
|
||||
* The configuration object for setting up [Auth](https://portal.thirdweb.com/auth); allowing users to sign in with their wallet.
|
||||
* The configuration object for setting up [Auth](https://portal.thirdweb.com/wallets/auth); allowing users to sign in with their wallet.
|
||||
* It takes an object with the following properties:
|
||||
* - `authUrl` - The backend URL of the authentication endpoints. For example, if your endpoints are at `/api/auth/login`, `/api/auth/logout`, etc. then this should be set to `/api/auth`
|
||||
* - `domain` - The frontend domain used to generate the login payload. This domain should match the domain used on your auth backend.
|
||||
@@ -412,7 +412,7 @@ export type DefaultChains = typeof defaultChains;
|
||||
*
|
||||
* Allows you to create an instance of `ThirdwebStorage` with your own customized config, and pass it to the SDK. This requires the `@thirdweb-dev/storage` package to be installed.
|
||||
*
|
||||
* [Learn more aboout storage](https://portal.thirdweb.com/storage)
|
||||
* [Learn more about storage](https://portal.thirdweb.com/infrastructure/storage/overview)
|
||||
*
|
||||
* ```jsx
|
||||
* import { ThirdwebProvider } from "@thirdweb-dev/react";
|
||||
|
||||
@@ -78,14 +78,14 @@ export const WalletUIStatesProvider = (
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the `ConnectWallet` Modal is open or not
|
||||
* Check if the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) Modal is open or not
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const isOpen = useIsWalletModalOpen();
|
||||
* ```
|
||||
*
|
||||
* @returns `true` if the `ConnectWallet` modal is open, `false` otherwise
|
||||
* @returns `true` if the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) modal is open, `false` otherwise
|
||||
* @connectWallet
|
||||
*/
|
||||
export const useIsWalletModalOpen = () => {
|
||||
@@ -93,7 +93,7 @@ export const useIsWalletModalOpen = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Open or close the `ConnectWallet` Modal
|
||||
* Open or close the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) Modal
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
|
||||
@@ -113,3 +113,4 @@ export type {
|
||||
|
||||
// at the moment we'll re-export everything from the evm package
|
||||
export * from "./evm";
|
||||
export * from "./payments";
|
||||
@@ -0,0 +1,54 @@
|
||||
// UNCHANGED
|
||||
import type { SupportedChainName } from "@thirdweb-dev/payments";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import React, { createContext, useContext, useMemo, useState } from "react";
|
||||
|
||||
interface SDKContext {
|
||||
chainName: SupportedChainName;
|
||||
setChainName: Dispatch<SetStateAction<SupportedChainName>>;
|
||||
clientId: string;
|
||||
appName: string;
|
||||
}
|
||||
|
||||
const defaultContextValue: SDKContext = {
|
||||
chainName: "Polygon",
|
||||
setChainName: () => {},
|
||||
clientId: "",
|
||||
appName: "",
|
||||
};
|
||||
|
||||
const PaymentsSDKContext = /* @__PURE__ */ createContext(defaultContextValue);
|
||||
|
||||
export interface PaymentsProviderProps {
|
||||
chainName?: SupportedChainName;
|
||||
appName?: string;
|
||||
clientId?: string;
|
||||
}
|
||||
|
||||
export const PaymentsSDKProvider = ({
|
||||
appName = "",
|
||||
chainName = "Polygon",
|
||||
clientId = "",
|
||||
children,
|
||||
}: React.PropsWithChildren<PaymentsProviderProps>) => {
|
||||
const [chainName_, setChainName] = useState<SupportedChainName>(chainName);
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
chainName: chainName_,
|
||||
setChainName,
|
||||
appName: appName,
|
||||
clientId: clientId,
|
||||
}),
|
||||
[chainName_, appName, clientId],
|
||||
);
|
||||
|
||||
return (
|
||||
<PaymentsSDKContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</PaymentsSDKContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePaymentsSDKContext = (): SDKContext => {
|
||||
return useContext(PaymentsSDKContext);
|
||||
};
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// UNCHANGED
|
||||
declare module '*.png';
|
||||
declare module '*.webp';
|
||||
declare module '*.jpg';
|
||||
declare module '*.jpeg';
|
||||
declare module '*.svg';
|
||||
declare module '*.gif';
|
||||
declare module '*.css';
|
||||
@@ -0,0 +1,114 @@
|
||||
// CHANGED: Import packageJSON + sdk-version only
|
||||
import type {
|
||||
ICheckoutWithCardConfigs,
|
||||
PaymentsSDKError,
|
||||
PriceSummary,
|
||||
ReviewResult,
|
||||
ICustomizationOptions,
|
||||
Locale,
|
||||
} from "@thirdweb-dev/payments";
|
||||
import {
|
||||
createCheckoutWithCardElement,
|
||||
DEFAULT_BRAND_OPTIONS,
|
||||
} from "@thirdweb-dev/payments";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { usePaymentsSDKContext } from "../Provider";
|
||||
import { iframeContainer } from "../lib/utils/styles";
|
||||
import { SpinnerWrapper } from "./common/SpinnerWrapper";
|
||||
|
||||
import packageJson from "../../../package.json";
|
||||
|
||||
interface CheckoutWithCardProps {
|
||||
clientId?: string;
|
||||
sdkClientSecret?: string;
|
||||
onPaymentSuccess: (result: { transactionId: string }) => void;
|
||||
appName?: string;
|
||||
options?: ICustomizationOptions;
|
||||
onReview?: (result: ReviewResult) => void;
|
||||
onError?: (error: PaymentsSDKError) => void;
|
||||
onBeforeModalOpen?: (props: { url: string }) => void;
|
||||
onPriceUpdate?: (priceSummary: PriceSummary) => void;
|
||||
configs?: ICheckoutWithCardConfigs;
|
||||
|
||||
/**
|
||||
* @deprecated No longer used.
|
||||
*/
|
||||
experimentalUseAltDomain?: boolean;
|
||||
|
||||
/**
|
||||
* Sets the locale to a supported language.
|
||||
* NOTE: Localization is in early alpha and many languages are not yet supported.
|
||||
*
|
||||
* Defaults to English.
|
||||
*/
|
||||
locale?: Locale;
|
||||
}
|
||||
|
||||
export const CheckoutWithCard = ({
|
||||
clientId,
|
||||
sdkClientSecret,
|
||||
appName,
|
||||
options = {
|
||||
...DEFAULT_BRAND_OPTIONS,
|
||||
},
|
||||
onPaymentSuccess,
|
||||
onReview,
|
||||
onError,
|
||||
onBeforeModalOpen,
|
||||
onPriceUpdate,
|
||||
locale,
|
||||
configs,
|
||||
}: CheckoutWithCardProps): React.ReactElement => {
|
||||
const { appName: appNameContext } = usePaymentsSDKContext();
|
||||
const [isCardDetailIframeLoading, setIsCardDetailIframeLoading] =
|
||||
useState<boolean>(true);
|
||||
const onCardDetailLoad = useCallback(() => {
|
||||
setIsCardDetailIframeLoading(false);
|
||||
}, []);
|
||||
const CheckoutWithCardIframeContainerRef = useRef<HTMLDivElement>(null);
|
||||
const appNameToUse = appName || appNameContext;
|
||||
|
||||
// force hide spinner if iframe taking too long
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setIsCardDetailIframeLoading(false);
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearTimeout(timer); // Clear the timer if the component unmounts before the delay
|
||||
};
|
||||
}, []);
|
||||
// Handle message events from the popup. Pass along the message to the iframe as well
|
||||
useEffect(() => {
|
||||
if (!CheckoutWithCardIframeContainerRef.current) {
|
||||
return;
|
||||
}
|
||||
createCheckoutWithCardElement({
|
||||
clientId,
|
||||
sdkClientSecret,
|
||||
appName: appNameToUse,
|
||||
elementOrId: CheckoutWithCardIframeContainerRef.current,
|
||||
locale,
|
||||
onError,
|
||||
onLoad: onCardDetailLoad,
|
||||
onPaymentSuccess,
|
||||
onReview,
|
||||
onBeforeModalOpen,
|
||||
onPriceUpdate,
|
||||
options,
|
||||
configs,
|
||||
});
|
||||
}, [CheckoutWithCardIframeContainerRef.current]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={iframeContainer}
|
||||
ref={CheckoutWithCardIframeContainerRef}
|
||||
// Label the package version.
|
||||
data-thirdweb-sdk-version={`${packageJson.name}@${packageJson.version}`}
|
||||
>
|
||||
{isCardDetailIframeLoading && <SpinnerWrapper />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
// UNCHANGED
|
||||
import { VerifyOwnershipWithPaper } from "./VerifyOwnershipWithPaper";
|
||||
|
||||
export { VerifyOwnershipWithPaper as LoginWithPaper };
|
||||
@@ -0,0 +1,490 @@
|
||||
// CHANGED: imports only
|
||||
import { PAPER_APP_URL, DEFAULT_BRAND_OPTIONS } from "@thirdweb-dev/payments";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import type {
|
||||
ContractType,
|
||||
CustomContractArgWrapper,
|
||||
ReadMethodCallType,
|
||||
WriteMethodCallType,
|
||||
} from "../interfaces/CustomContract";
|
||||
import { fetchCustomContractArgsFromProps } from "../interfaces/CustomContract";
|
||||
import type { PaymentSuccessResult } from "../interfaces/PaymentSuccessResult";
|
||||
import type { TransferSuccessResult } from "../interfaces/TransferSuccessResult";
|
||||
import { openCenteredPopup } from "../lib/utils/popup";
|
||||
|
||||
export enum PaperCheckoutDisplay {
|
||||
/**
|
||||
* Open the checkout in a new popup centered over the parent window.
|
||||
*/
|
||||
POPUP = "POPUP",
|
||||
|
||||
/**
|
||||
* Open the checkout in a new browser tab.
|
||||
*/
|
||||
NEW_TAB = "NEW_TAB",
|
||||
|
||||
/**
|
||||
* Open the checkout in a modal on the parent page with a darkened background.
|
||||
*
|
||||
* NOTE: Pay with Crypto is disabled in this display mode.
|
||||
*/
|
||||
MODAL = "MODAL",
|
||||
|
||||
/**
|
||||
* Open the checkout in a drawer on the right side of the parent page with a darkened background.
|
||||
*
|
||||
* NOTE: Pay with Crypto is disabled in this display mode.
|
||||
*/
|
||||
DRAWER = "DRAWER",
|
||||
|
||||
/**
|
||||
* Embed the checkout directly on the parent page.
|
||||
*
|
||||
* NOTE: Pay with Crypto is disabled in this display mode.
|
||||
*/
|
||||
EMBED = "EMBED",
|
||||
}
|
||||
|
||||
export type PaperCheckoutProps<T extends ContractType> =
|
||||
CustomContractArgWrapper<
|
||||
{
|
||||
checkoutId: string;
|
||||
display?: PaperCheckoutDisplay;
|
||||
recipientWalletAddress?: string;
|
||||
emailAddress?: string;
|
||||
quantity?: number;
|
||||
mintMethod?: WriteMethodCallType;
|
||||
eligibilityMethod?: ReadMethodCallType;
|
||||
metadata?: Record<string, any>;
|
||||
appName?: string;
|
||||
onOpenCheckout?: () => void;
|
||||
onCloseCheckout?: () => void;
|
||||
onPaymentSuccess?: (result: PaymentSuccessResult) => void;
|
||||
onTransferSuccess?: (result: TransferSuccessResult) => void;
|
||||
options?: {
|
||||
width: number;
|
||||
height: number;
|
||||
colorPrimary: string;
|
||||
colorBackground: string;
|
||||
colorText: string;
|
||||
borderRadius: number;
|
||||
fontFamily: string;
|
||||
};
|
||||
children?: React.ReactNode;
|
||||
},
|
||||
T
|
||||
>;
|
||||
|
||||
export const PaperCheckout = <T extends ContractType>({
|
||||
checkoutId,
|
||||
display = PaperCheckoutDisplay.POPUP,
|
||||
recipientWalletAddress,
|
||||
emailAddress,
|
||||
quantity,
|
||||
eligibilityMethod,
|
||||
mintMethod,
|
||||
metadata,
|
||||
appName,
|
||||
options = {
|
||||
width: 400,
|
||||
height: 800,
|
||||
...DEFAULT_BRAND_OPTIONS,
|
||||
},
|
||||
onOpenCheckout,
|
||||
onCloseCheckout,
|
||||
onPaymentSuccess,
|
||||
onTransferSuccess,
|
||||
children,
|
||||
...props
|
||||
}: PaperCheckoutProps<T>) => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
const { contractType, contractArgs } =
|
||||
fetchCustomContractArgsFromProps(props);
|
||||
|
||||
// Handle message events from iframe.
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const data = event.data;
|
||||
|
||||
switch (data.eventType) {
|
||||
case "paymentSuccess":
|
||||
if (onPaymentSuccess) {
|
||||
onPaymentSuccess({ id: data.id });
|
||||
}
|
||||
break;
|
||||
|
||||
case "transferSuccess":
|
||||
if (onTransferSuccess) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
onTransferSuccess({
|
||||
id: data.id,
|
||||
// ...
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "modalClosed":
|
||||
// Emitted when the iframe decides to close, such as when the purchase is completed.
|
||||
setIsOpen(false);
|
||||
if (onCloseCheckout) {
|
||||
onCloseCheckout();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore unrecognized event
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handleMessage);
|
||||
return () => {
|
||||
window.removeEventListener("message", handleMessage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Build iframe URL with query params.
|
||||
const checkoutUrl = new URL(`/checkout/${checkoutId}`, PAPER_APP_URL);
|
||||
|
||||
checkoutUrl.searchParams.append("display", display);
|
||||
const mintMethodStringified = JSON.stringify(mintMethod);
|
||||
const eligibilityMethodStringified = JSON.stringify(eligibilityMethod);
|
||||
const metadataStringified = JSON.stringify(metadata);
|
||||
const contractArgsStringified = JSON.stringify(contractArgs);
|
||||
|
||||
if (options.colorPrimary) {
|
||||
checkoutUrl.searchParams.append("colorPrimary", options.colorPrimary);
|
||||
}
|
||||
if (options.colorBackground) {
|
||||
checkoutUrl.searchParams.append("colorBackground", options.colorBackground);
|
||||
}
|
||||
if (options.colorText) {
|
||||
checkoutUrl.searchParams.append("colorText", options.colorText);
|
||||
}
|
||||
if (options.borderRadius !== undefined) {
|
||||
checkoutUrl.searchParams.append(
|
||||
"borderRadius",
|
||||
options.borderRadius.toString(),
|
||||
);
|
||||
}
|
||||
if (options.fontFamily) {
|
||||
checkoutUrl.searchParams.append("fontFamily", options.fontFamily);
|
||||
}
|
||||
if (mintMethod) {
|
||||
checkoutUrl.searchParams.append(
|
||||
"mintMethod",
|
||||
Buffer.from(mintMethodStringified, "utf-8").toString("base64"),
|
||||
);
|
||||
}
|
||||
if (eligibilityMethod) {
|
||||
checkoutUrl.searchParams.append(
|
||||
"eligibilityMethod",
|
||||
Buffer.from(eligibilityMethodStringified, "utf-8").toString("base64"),
|
||||
);
|
||||
}
|
||||
if (contractType) {
|
||||
checkoutUrl.searchParams.append("contractType", contractType);
|
||||
}
|
||||
if (contractArgs) {
|
||||
checkoutUrl.searchParams.append(
|
||||
"contractArgs",
|
||||
// Base 64 encode
|
||||
Buffer.from(contractArgsStringified, "utf-8").toString("base64"),
|
||||
);
|
||||
}
|
||||
if (appName) {
|
||||
checkoutUrl.searchParams.append("appName", appName);
|
||||
}
|
||||
if (recipientWalletAddress) {
|
||||
checkoutUrl.searchParams.append("wallet", recipientWalletAddress);
|
||||
}
|
||||
if (emailAddress) {
|
||||
checkoutUrl.searchParams.append("username", emailAddress);
|
||||
}
|
||||
if (quantity) {
|
||||
checkoutUrl.searchParams.append("quantity", quantity.toString());
|
||||
}
|
||||
if (metadata) {
|
||||
checkoutUrl.searchParams.append(
|
||||
"metadata",
|
||||
encodeURIComponent(metadataStringified),
|
||||
);
|
||||
}
|
||||
|
||||
// Default button if the app doesn't pass one in.
|
||||
const clickableElement = children || (
|
||||
<button
|
||||
style={{
|
||||
backgroundColor: "#cf3781",
|
||||
padding: "8px 20px 8px 20px",
|
||||
borderRadius: "8px",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{"Buy Now"}
|
||||
</button>
|
||||
);
|
||||
|
||||
switch (display) {
|
||||
case PaperCheckoutDisplay.POPUP: {
|
||||
const onClick = () => {
|
||||
openCenteredPopup({
|
||||
url: checkoutUrl.href,
|
||||
h: options.height,
|
||||
w: options.width,
|
||||
win: window,
|
||||
target: "_blank",
|
||||
});
|
||||
|
||||
if (onOpenCheckout) {
|
||||
onOpenCheckout();
|
||||
}
|
||||
};
|
||||
return <a onClick={onClick}>{clickableElement}</a>;
|
||||
}
|
||||
|
||||
case PaperCheckoutDisplay.NEW_TAB: {
|
||||
const onClick = () => {
|
||||
window.open(checkoutUrl, "_blank");
|
||||
if (onOpenCheckout) {
|
||||
onOpenCheckout();
|
||||
}
|
||||
};
|
||||
return <a onClick={onClick}>{clickableElement}</a>;
|
||||
}
|
||||
|
||||
case PaperCheckoutDisplay.MODAL: {
|
||||
const onOpen = () => {
|
||||
setIsOpen(true);
|
||||
if (onOpenCheckout) {
|
||||
onOpenCheckout();
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
setIsOpen(false);
|
||||
if (onCloseCheckout) {
|
||||
onCloseCheckout();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PaperCheckoutModal
|
||||
clickableElement={clickableElement}
|
||||
checkoutUrl={checkoutUrl.href}
|
||||
width={options.width}
|
||||
height={options.height}
|
||||
isOpen={isOpen}
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case PaperCheckoutDisplay.DRAWER: {
|
||||
const onOpen = () => {
|
||||
setIsOpen(true);
|
||||
if (onOpenCheckout) {
|
||||
onOpenCheckout();
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
setIsOpen(false);
|
||||
if (onCloseCheckout) {
|
||||
onCloseCheckout();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PaperCheckoutDrawer
|
||||
clickableElement={clickableElement}
|
||||
checkoutUrl={checkoutUrl.href}
|
||||
width={options.width}
|
||||
isOpen={isOpen}
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case PaperCheckoutDisplay.EMBED: {
|
||||
return (
|
||||
<iframe
|
||||
src={checkoutUrl.href}
|
||||
width={options.width}
|
||||
height={options.height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
console.error(`Invalid or unimplemented display type: ${display}`);
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
const inlineStyles: { [name: string]: any } = {
|
||||
overlay: {
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
margin: 0,
|
||||
zIndex: 1000,
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
visibility: "hidden",
|
||||
opacity: 0,
|
||||
transition: "all 0.2s ease",
|
||||
},
|
||||
overlayIsVisible: {
|
||||
visibility: "visible",
|
||||
opacity: 1,
|
||||
background: "#0008",
|
||||
},
|
||||
modalOverlay: {
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
drawerOverlay: {
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
modalDialog: {
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
borderRadius: "8px",
|
||||
visibility: "hidden",
|
||||
opacity: 0,
|
||||
top: "5%",
|
||||
transition: "all 0.2s ease",
|
||||
maxWidth: "100vw",
|
||||
},
|
||||
modalDialogIsVisible: {
|
||||
visibility: "visible",
|
||||
opacity: 1,
|
||||
top: 0,
|
||||
},
|
||||
drawerDialog: {
|
||||
position: "relative",
|
||||
visibility: "hidden",
|
||||
opacity: 0,
|
||||
right: "-10%",
|
||||
transition: "all 0.2s ease",
|
||||
},
|
||||
drawerDialogIsVisible: {
|
||||
visibility: "visible",
|
||||
opacity: 1,
|
||||
right: 0,
|
||||
},
|
||||
modalCloseButton: {
|
||||
position: "absolute",
|
||||
top: "0.1em",
|
||||
right: "0.2em",
|
||||
borderRadius: "8px",
|
||||
fontSize: "x-large",
|
||||
padding: "0 0.4em",
|
||||
color: "#888",
|
||||
},
|
||||
};
|
||||
|
||||
const PaperCheckoutDrawer = ({
|
||||
clickableElement,
|
||||
checkoutUrl,
|
||||
width,
|
||||
isOpen,
|
||||
onOpen,
|
||||
onClose,
|
||||
}: {
|
||||
clickableElement: React.ReactNode;
|
||||
checkoutUrl: string;
|
||||
width: number;
|
||||
isOpen: boolean;
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<a onClick={onOpen}>{clickableElement}</a>
|
||||
|
||||
<div
|
||||
className="paper-overlay"
|
||||
style={{
|
||||
...inlineStyles.overlay,
|
||||
...(isOpen ? inlineStyles.overlayIsVisible : {}),
|
||||
...inlineStyles.drawerOverlay,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="paper-drawer"
|
||||
style={{
|
||||
...inlineStyles.drawerDialog,
|
||||
...(isOpen ? inlineStyles.drawerDialogIsVisible : {}),
|
||||
width,
|
||||
}}
|
||||
>
|
||||
<button onClick={onClose} style={inlineStyles.modalCloseButton}>
|
||||
×
|
||||
</button>
|
||||
{isOpen && <iframe src={checkoutUrl} width="100%" height="100%" />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const PaperCheckoutModal = ({
|
||||
clickableElement,
|
||||
checkoutUrl,
|
||||
width,
|
||||
height,
|
||||
isOpen,
|
||||
onOpen,
|
||||
onClose,
|
||||
}: {
|
||||
clickableElement: React.ReactNode;
|
||||
checkoutUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
isOpen: boolean;
|
||||
onOpen: () => void;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
// Set the max height to the window's inner height.
|
||||
// This method handles mobile browser heights more reliably than 100vh.
|
||||
const [innerHeight, setInnerHeight] = useState<number>(height);
|
||||
useEffect(() => setInnerHeight(window.innerHeight), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<a onClick={onOpen}>{clickableElement}</a>
|
||||
|
||||
<div
|
||||
className="paper-overlay"
|
||||
style={{
|
||||
...inlineStyles.overlay,
|
||||
...(isOpen ? inlineStyles.overlayIsVisible : {}),
|
||||
...inlineStyles.modalOverlay,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="paper-modal"
|
||||
style={{
|
||||
...inlineStyles.modalDialog,
|
||||
...(isOpen ? inlineStyles.modalDialogIsVisible : {}),
|
||||
width,
|
||||
height,
|
||||
maxHeight: innerHeight,
|
||||
}}
|
||||
>
|
||||
<button onClick={onClose} style={inlineStyles.modalCloseButton}>
|
||||
×
|
||||
</button>
|
||||
{isOpen && <iframe src={checkoutUrl} width="100%" height="100%" />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
// CHANGED: packageJson import + sdk-version + clientId
|
||||
import { css } from "@emotion/css";
|
||||
import type {
|
||||
PaymentsSDKError,
|
||||
SupportedChainName,
|
||||
} from "@thirdweb-dev/payments";
|
||||
import { PAPER_APP_URL, PaymentsSDKErrorCode } from "@thirdweb-dev/payments";
|
||||
import React, { useEffect } from "react";
|
||||
import { usePaymentsSDKContext } from "../Provider";
|
||||
import { openCenteredPopup } from "../lib/utils/popup";
|
||||
import { Button } from "./common/Button";
|
||||
|
||||
import packageJson from "../../../package.json";
|
||||
|
||||
interface VerifyOwnershipWithPaperProps {
|
||||
onSuccess?: (code: string) => void;
|
||||
onError?: (error: PaymentsSDKError) => void;
|
||||
onWindowClose?: () => void;
|
||||
children?: ({
|
||||
onClick,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
}) => React.ReactNode | React.ReactNode;
|
||||
redirectUrl?: string;
|
||||
className?: string;
|
||||
chainName?: SupportedChainName;
|
||||
clientId?: string;
|
||||
}
|
||||
|
||||
const enum VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE {
|
||||
USER_LOGIN_SUCCESS = "userLoginSuccess",
|
||||
USER_LOGIN_FAILED = "userLoginFailed",
|
||||
USER_CLOSE_LOGIN_PAGE = "userCloseLoginPage",
|
||||
}
|
||||
|
||||
export const VerifyOwnershipWithPaper: React.FC<
|
||||
VerifyOwnershipWithPaperProps
|
||||
> = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
onWindowClose,
|
||||
className,
|
||||
children,
|
||||
redirectUrl,
|
||||
chainName,
|
||||
clientId,
|
||||
}) => {
|
||||
const { chainName: chainNameContext, clientId: clientIdContext } =
|
||||
usePaymentsSDKContext();
|
||||
const isChildrenFunction = typeof children === "function";
|
||||
|
||||
const chainNameToUse = chainName || chainNameContext;
|
||||
const clientIdToUse = clientId || clientIdContext;
|
||||
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const data = event.data;
|
||||
switch (data.eventType) {
|
||||
case VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE.USER_LOGIN_SUCCESS:
|
||||
if (onSuccess) {
|
||||
onSuccess(data.values.accessCode);
|
||||
}
|
||||
break;
|
||||
case VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE.USER_CLOSE_LOGIN_PAGE: {
|
||||
if (onWindowClose) {
|
||||
onWindowClose();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE.USER_LOGIN_FAILED:
|
||||
if (onError) {
|
||||
onError({
|
||||
code: PaymentsSDKErrorCode.UserLoginFailed,
|
||||
error: new Error(PaymentsSDKErrorCode.UserLoginFailed),
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Ignore unrecognized event
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handleMessage);
|
||||
return () => {
|
||||
window.removeEventListener("message", handleMessage);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const url = new URL("/sdk/v1/login-with-paper", PAPER_APP_URL);
|
||||
url.searchParams.append("chainName", chainNameToUse);
|
||||
url.searchParams.append("clientId", clientIdToUse);
|
||||
url.searchParams.append("redirectUrl", redirectUrl || "");
|
||||
const onClick = () => {
|
||||
const loginWindow = openCenteredPopup({
|
||||
url: url.href,
|
||||
target: "_blank",
|
||||
win: window,
|
||||
w: 400,
|
||||
h: 600,
|
||||
});
|
||||
loginWindow?.focus();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{children && isChildrenFunction ? (
|
||||
children({ onClick })
|
||||
) : children ? (
|
||||
<a
|
||||
onClick={onClick}
|
||||
data-thirdweb-sdk-version={`${packageJson.name}@${packageJson.version}`}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
) : (
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className={className}
|
||||
data-thirdweb-sdk-version={`${packageJson.name}@${packageJson.version}`}
|
||||
>
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`}
|
||||
>
|
||||
<span style={{ marginRight: "8px" }}>{"Login with"}</span>{" "}
|
||||
<svg
|
||||
width="15"
|
||||
height="30"
|
||||
viewBox="0 0 26 49"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M25.8824 8.91421C25.8824 8.47803 25.5996 8.09218 25.1838 7.96071L0 0V11.7963L14.7899 16.276L2.77311 24.5V34.4815L5.58074 37.3767L3.1009 39.6283C2.89214 39.8178 2.77311 40.0867 2.77311 40.3687V49L11.0924 42.6481V32.6667L8.60325 30.5207L25.8824 19.963V8.91421Z"
|
||||
fill="#19A8D6"
|
||||
/>
|
||||
<path
|
||||
d="M25.8824 8.91421C25.8824 8.47803 25.5996 8.09218 25.1838 7.96071L0 0V11.7963L25.8824 19.963V8.91421Z"
|
||||
fill="#39D0FF"
|
||||
/>
|
||||
<path
|
||||
d="M11.0924 32.6667L2.77311 24.5V34.4815L11.0924 42.6481V32.6667Z"
|
||||
fill="#39D0FF"
|
||||
/>
|
||||
</svg>{" "}
|
||||
<span style={{ marginLeft: "5px" }}>{"Paper"}</span>
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
// Unchanged
|
||||
import { css, cx } from "@emotion/css";
|
||||
import React from "react";
|
||||
import { Spinner } from "./Spinner";
|
||||
|
||||
export const Button = ({
|
||||
isLoading = false,
|
||||
loadingText = "",
|
||||
...props
|
||||
}: {
|
||||
isLoading?: boolean;
|
||||
loadingText?: string;
|
||||
} & React.HTMLProps<HTMLButtonElement>): React.ReactElement => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
type={props.type as "button" | "submit" | "reset" | undefined}
|
||||
disabled={isLoading || props.disabled}
|
||||
className={cx(buttonClass, props.className)}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
<Spinner
|
||||
className={
|
||||
loadingText
|
||||
? css`
|
||||
margin-right: 0.5rem;
|
||||
`
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
{loadingText}
|
||||
</div>
|
||||
) : (
|
||||
props.children
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const buttonClass = css`
|
||||
padding-top: 0.625rem;
|
||||
padding-bottom: 0.625rem;
|
||||
padding-left: 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
background-color: #1f2937;
|
||||
color: #ffffff;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 600;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
`;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Unchanged
|
||||
import React, { forwardRef, memo } from "react";
|
||||
|
||||
const IFrameBase = /* @__PURE__ */ forwardRef(
|
||||
(
|
||||
props: React.IframeHTMLAttributes<HTMLIFrameElement>,
|
||||
ref: React.ForwardedRef<HTMLIFrameElement>,
|
||||
) => {
|
||||
return <iframe ref={ref} {...props} />;
|
||||
},
|
||||
);
|
||||
IFrameBase.displayName = "IFrameBase";
|
||||
export const IFrameWrapper = /* @__PURE__ */ memo(IFrameBase);
|
||||
@@ -0,0 +1,209 @@
|
||||
// UNCHANGED
|
||||
import { css, cx } from "@emotion/css";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import React, { Fragment, useEffect } from "react";
|
||||
import { opacity0, opacity1 } from "../../lib/utils/styles";
|
||||
|
||||
export const Modal: React.FC<{
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
escapeToClose?: boolean;
|
||||
clickOutsideModalToClose?: boolean;
|
||||
bgColor?: string;
|
||||
isFullScreen?: boolean;
|
||||
hasCloseButton?: boolean;
|
||||
children: React.ReactNode;
|
||||
}> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
escapeToClose = true,
|
||||
clickOutsideModalToClose = false,
|
||||
bgColor = "#FAFAFA",
|
||||
isFullScreen,
|
||||
hasCloseButton = true,
|
||||
children,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
const keyDownHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
if (escapeToClose) {
|
||||
document.addEventListener("keydown", keyDownHandler);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", keyDownHandler);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const additionalDialogClasses = isFullScreen ? "" : dialogContainedClasses;
|
||||
const dialogPanelBg = isFullScreen ? "transparent" : bgColor;
|
||||
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className={cx(
|
||||
"paper-modal",
|
||||
css`
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
`,
|
||||
)}
|
||||
onClose={clickOutsideModalToClose ? onClose : () => {}}
|
||||
>
|
||||
{/* Overlay */}
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter={enterClasses}
|
||||
enterFrom={opacity0}
|
||||
enterTo={opacity1}
|
||||
leave={leaveClasses}
|
||||
leaveFrom={opacity1}
|
||||
leaveTo={opacity0}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
"paper-modal-overlay",
|
||||
css`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
`,
|
||||
)}
|
||||
/>
|
||||
</Transition.Child>
|
||||
|
||||
<div
|
||||
className={css`
|
||||
overflow-y: auto;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100%;
|
||||
`}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter={enterClasses}
|
||||
enterFrom={cx(opacity0, scale95)}
|
||||
enterTo={cx(opacity1, scale100)}
|
||||
leave={leaveClasses}
|
||||
leaveFrom={cx(opacity1, scale100)}
|
||||
leaveTo={cx(opacity0, scale95)}
|
||||
>
|
||||
<Dialog.Panel
|
||||
className={cx(
|
||||
"paper-modal-content",
|
||||
dialogClasses,
|
||||
additionalDialogClasses,
|
||||
)}
|
||||
style={{ backgroundColor: dialogPanelBg }}
|
||||
>
|
||||
{hasCloseButton && <CloseButton onClose={onClose} />}
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
|
||||
const CloseButton: React.FC<{ onClose: () => void }> = ({ onClose }) => {
|
||||
return (
|
||||
<button
|
||||
aria-label="close modal"
|
||||
className={css`
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 9999px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: rgba(160, 174, 192, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: rgba(160, 174, 192, 0.2);
|
||||
}
|
||||
`}
|
||||
>
|
||||
<svg
|
||||
className={css`
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
`}
|
||||
onClick={onClose}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const enterClasses = css`
|
||||
transition-duration: 300ms;
|
||||
transition-timing-function: ease-out;
|
||||
`;
|
||||
|
||||
const leaveClasses = css`
|
||||
transition-duration: 200ms;
|
||||
transition-timing-function: ease-in;
|
||||
`;
|
||||
|
||||
const scale95 = css`
|
||||
transform: scale(0.95);
|
||||
`;
|
||||
|
||||
const scale100 = css`
|
||||
transform: scale(1);
|
||||
`;
|
||||
|
||||
const dialogClasses = css`
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
position: relative;
|
||||
transition-property: all;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow:
|
||||
0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
||||
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
`;
|
||||
|
||||
const dialogContainedClasses = css`
|
||||
padding: 1.25rem;
|
||||
|
||||
@media (min-width: 640px) {
|
||||
margin: 1rem;
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,30 @@
|
||||
// UNCHANGED
|
||||
import { css, cx, keyframes } from "@emotion/css";
|
||||
import React from "react";
|
||||
|
||||
export const Spinner = ({
|
||||
className,
|
||||
}: {
|
||||
className?: string;
|
||||
}): React.ReactElement => {
|
||||
return <div id="loader" className={cx(loader, className)}></div>;
|
||||
};
|
||||
|
||||
const Spin = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`;
|
||||
|
||||
const loader = css`
|
||||
border: 0.15rem solid #f3f3f300;
|
||||
border-top: 0.15rem solid #000000;
|
||||
border-left: 0.15rem solid #000000;
|
||||
border-radius: 50%;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
animation: ${Spin} 1s linear infinite;
|
||||
`;
|
||||
@@ -0,0 +1,24 @@
|
||||
// UNCHANGED
|
||||
import { css } from "@emotion/css";
|
||||
import { Spinner } from "./Spinner";
|
||||
|
||||
export const SpinnerWrapper: React.FC = () => {
|
||||
return (
|
||||
<div className={wrapperStyle}>
|
||||
<Spinner className={spinnerStyle} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const wrapperStyle = css`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
`;
|
||||
|
||||
const spinnerStyle = css`
|
||||
color: #000000;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
`;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user