Skip to content

Commit

Permalink
fix(clerk-js,clerk-react,types,elements): Optimize Coinbase SDK loading
Browse files Browse the repository at this point in the history
Instead of lazy loading the Coinbase SDK in getEthereumProvider(), move loading when the SignIn/SignUp components mount. This fix avoid the Safari to block the Coinbase popup
  • Loading branch information
nikospapcom committed Dec 16, 2024
1 parent dfdf23b commit 7217c73
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 3 deletions.
8 changes: 8 additions & 0 deletions .changeset/healthy-pears-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@clerk/clerk-js': patch
'@clerk/elements': patch
'@clerk/clerk-react': patch
'@clerk/types': patch
---

Move Coinbase SDK loading in the SignIn/SignUp components. This fix avoid the Safari to block the Coinbase popup
36 changes: 35 additions & 1 deletion packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -138,6 +140,7 @@ declare global {
__clerk_publishable_key?: string;
__clerk_proxy_url?: ClerkInterface['proxyUrl'];
__clerk_domain?: ClerkInterface['domain'];
__clerk_coinbase_sdk: Promise<typeof CoinbaseWalletSDK>;
}
}

Expand Down Expand Up @@ -180,6 +183,7 @@ export class Clerk implements ClerkInterface {
protected internal_last_error: ClerkAPIError | null = null;
// converted to protected environment to support `updateEnvironment` type assertion
protected environment?: EnvironmentResource | null;
private __promise = createDeferredPromise();

#publishableKey = '';
#domain: DomainOrProxyUrl['domain'];
Expand Down Expand Up @@ -247,6 +251,20 @@ export class Clerk implements ClerkInterface {
return false;
}

get 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');
}

get domain(): string {
if (inBrowser()) {
const strippedDomainString = stripScheme(handleValueOrFn(this.#domain, new URL(window.location.href)));
Expand Down Expand Up @@ -582,6 +600,7 @@ export class Clerk implements ClerkInterface {
this.__experimental_ui.mount('SignIn', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
void this.__internal_warmupDependencies();
void this.#componentControls.ensureMounted({ preloadHint: 'SignIn' }).then(controls =>
controls.mountComponent({
name: 'SignIn',
Expand All @@ -608,6 +627,7 @@ export class Clerk implements ClerkInterface {
this.__experimental_ui.mount('SignUp', node, props);
} else {
this.assertComponentsReady(this.#componentControls);
void this.__internal_warmupDependencies();
void this.#componentControls.ensureMounted({ preloadHint: 'SignUp' }).then(controls =>
controls.mountComponent({
name: 'SignUp',
Expand Down Expand Up @@ -1654,6 +1674,16 @@ export class Clerk implements ClerkInterface {
this.environment = environment;
}

public __internal_warmupDependencies = async (): Promise<void> => {
if (this.isCoinbaseWalletEnabled) {
window.__clerk_coinbase_sdk = this.__promise.promise as Promise<typeof CoinbaseWalletSDK>;

await import('@coinbase/wallet-sdk').then(mod => {
this.__promise.resolve(mod.CoinbaseWalletSDK);
});
}
};

__internal_setCountry = (country: string | null) => {
if (!this.__internal_country) {
this.__internal_country = country;
Expand Down Expand Up @@ -1909,6 +1939,10 @@ export class Clerk implements ClerkInterface {

initComponents();

if (!Clerk.mountComponentRenderer) {
void this.__internal_warmupDependencies();
}

break;
} catch (err) {
if (isError(err, 'dev_browser_unauthenticated')) {
Expand Down
2 changes: 1 addition & 1 deletion packages/clerk-js/src/utils/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ 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;
const sdk = new CoinbaseWalletSDK({});
return sdk.makeWeb3Provider({ options: 'all' });
}
Expand Down
8 changes: 7 additions & 1 deletion packages/elements/src/react/common/connections.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useClerk } from '@clerk/shared/react';
import type { OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import { Slot } from '@radix-ui/react-slot';
import { createContext, useContext } from 'react';
import { createContext, useContext, useEffect } from 'react';

import type { ThirdPartyProvider } from '~/utils/third-party-strategies';

Expand Down Expand Up @@ -50,10 +51,15 @@ export interface ConnectionProps extends React.ButtonHTMLAttributes<HTMLButtonEl
* </SignIn.Root>
*/
export function Connection({ asChild, name, ...rest }: ConnectionProps) {
const clerk = useClerk();
const signInRef = SignInRouterCtx.useActorRef(true);
const signUpRef = SignUpRouterCtx.useActorRef(true);
const provider = useThirdPartyProvider((signInRef || signUpRef)!, name);

useEffect(() => {
void clerk.__internal_warmupDependencies();
}, [clerk]);

if (!provider) {
return null;
}
Expand Down
11 changes: 11 additions & 0 deletions packages/react/src/isomorphicClerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ type IsomorphicLoadedClerk = Without<
| 'client'
| '__internal_getCachedResources'
| '__internal_reloadInitialResources'
| '__internal_warmupDependencies'
> & {
// TODO: Align return type and parms
handleRedirectCallback: (params: HandleOAuthCallbackParams) => void;
Expand Down Expand Up @@ -173,6 +174,7 @@ type IsomorphicLoadedClerk = Without<
mountSignIn: (node: HTMLDivElement, props: SignInProps) => void;
mountUserProfile: (node: HTMLDivElement, props: UserProfileProps) => void;
mountWaitlist: (node: HTMLDivElement, props: WaitlistProps) => void;
__internal_warmupDependencies: () => void;
client: ClientResource | undefined;
};

Expand Down Expand Up @@ -1214,4 +1216,13 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk {
this.premountMethodCalls.set('signOut', callback);
}
};

__internal_warmupDependencies = async (): Promise<void> => {
const callback = () => this.clerkjs?.__internal_warmupDependencies();
if (this.clerkjs && this.#loaded) {
return callback() as Promise<void>;
} else {
this.premountMethodCalls.set('__internal_warmupDependencies', callback);
}
};
}
5 changes: 5 additions & 0 deletions packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ export interface Clerk {

telemetry: TelemetryCollector | undefined;

/** Clerk if coinbase wallet is enabled. */
isCoinbaseWalletEnabled: boolean;

__internal_country?: string | null;

/**
Expand Down Expand Up @@ -606,6 +609,8 @@ export interface Clerk {
* This funtion is used to reload the initial resources (Environment/Client) from the Frontend API.
**/
__internal_reloadInitialResources: () => Promise<void>;

__internal_warmupDependencies: () => Promise<void>;
}

export type HandleOAuthCallbackParams = TransferableOption &
Expand Down

0 comments on commit 7217c73

Please sign in to comment.