diff --git a/.changeset/healthy-pears-play.md b/.changeset/healthy-pears-play.md new file mode 100644 index 0000000000..50ed044750 --- /dev/null +++ b/.changeset/healthy-pears-play.md @@ -0,0 +1,7 @@ +--- +'@clerk/clerk-js': patch +'@clerk/clerk-react': patch +'@clerk/types': patch +--- + +Introduce new method `prefetchDependencies` to load Coinbase SDK instead of loading the SDK when click the `Coinbase` button in `` / ``. This change avoid the Safari to block the Coinbase popup diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index b1cfd5d586..06af8720cf 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -7,7 +7,7 @@ import { logger } from '@clerk/shared/logger'; import { isHttpOrHttps, isValidProxyUrl, proxyUrlToAbsoluteURL } from '@clerk/shared/proxy'; import { eventPrebuiltComponentMounted, TelemetryCollector } from '@clerk/shared/telemetry'; import { addClerkPrefix, stripScheme } from '@clerk/shared/url'; -import { handleValueOrFn, noop } from '@clerk/shared/utils'; +import { createDeferredPromise, handleValueOrFn, noop } from '@clerk/shared/utils'; import type { __internal_UserVerificationModalProps, ActiveSessionResource, @@ -65,7 +65,9 @@ import type { WaitlistProps, WaitlistResource, Web3Provider, + Web3Strategy, } from '@clerk/types'; +import type CoinbaseWalletSDK from '@coinbase/wallet-sdk'; import type { MountComponentRenderer } from '../ui/Components'; import { UI } from '../ui/new'; @@ -138,6 +140,7 @@ declare global { __clerk_publishable_key?: string; __clerk_proxy_url?: ClerkInterface['proxyUrl']; __clerk_domain?: ClerkInterface['domain']; + __clerk_coinbase_sdk: Promise; } } @@ -197,6 +200,7 @@ export class Clerk implements ClerkInterface { #options: ClerkOptions = {}; #pageLifecycle: ReturnType | null = null; #touchThrottledUntil = 0; + #coinbaseSDKResolvers = createDeferredPromise(); public __internal_getCachedResources: | (() => Promise<{ client: ClientJSONSnapshot | null; environment: EnvironmentJSONSnapshot | null }>) @@ -369,6 +373,20 @@ export class Clerk implements ClerkInterface { return this.#options.experimental?.combinedFlow && this.#options.signInUrl === this.#options.signUpUrl; } + #isCoinbaseWalletEnabled(): boolean { + const attributes = this.environment?.userSettings.attributes; + if (!attributes) { + return false; + } + + const findWeb3Attributes = Object.entries(attributes) + .filter(([name, attr]) => attr.used_for_first_factor && name.startsWith('web3')) + .map(([, desc]) => desc.first_factors) + .flat() as any as Web3Strategy[]; + + return findWeb3Attributes.includes('web3_coinbase_wallet_signature'); + } + public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => { if (!this.client || this.client.sessions.length === 0) { return; @@ -1654,6 +1672,16 @@ export class Clerk implements ClerkInterface { this.environment = environment; } + private prefetchDependencies = async (): Promise => { + if (this.#isCoinbaseWalletEnabled()) { + window.__clerk_coinbase_sdk = this.#coinbaseSDKResolvers.promise as Promise; + + await import('@coinbase/wallet-sdk').then(mod => { + this.#coinbaseSDKResolvers.resolve(mod.CoinbaseWalletSDK); + }); + } + }; + __internal_setCountry = (country: string | null) => { if (!this.__internal_country) { this.__internal_country = country; @@ -1907,6 +1935,8 @@ export class Clerk implements ClerkInterface { return false; } + void this.prefetchDependencies(); + initComponents(); break; diff --git a/packages/clerk-js/src/utils/web3.ts b/packages/clerk-js/src/utils/web3.ts index 2bec8b08f8..aab1e5890c 100644 --- a/packages/clerk-js/src/utils/web3.ts +++ b/packages/clerk-js/src/utils/web3.ts @@ -71,7 +71,11 @@ export async function generateSignatureWithOKXWallet(params: GenerateSignaturePa async function getEthereumProvider(provider: Web3Provider) { if (provider === 'coinbase_wallet') { - const CoinbaseWalletSDK = await import('@coinbase/wallet-sdk').then(mod => mod.CoinbaseWalletSDK); + const CoinbaseWalletSDK = await window.__clerk_coinbase_sdk.catch(() => null); + if (!CoinbaseWalletSDK) { + throw new Error('Coinbase Wallet SDK is not loaded'); + } + const sdk = new CoinbaseWalletSDK({}); return sdk.makeWeb3Provider({ options: 'all' }); }