Skip to content

Commit cd73364

Browse files
committed
Introduce onPendingSession and remove #componentNavigationContext
1 parent 5902f95 commit cd73364

File tree

18 files changed

+129
-116
lines changed

18 files changed

+129
-116
lines changed

packages/clerk-js/src/core/__tests__/clerk.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ describe('Clerk singleton', () => {
474474
});
475475
});
476476

477+
// TODO -> Refactor tests
477478
describe('with `pending` session status', () => {
478479
const mockSession = {
479480
id: '1',
@@ -525,6 +526,7 @@ describe('Clerk singleton', () => {
525526
});
526527
});
527528

529+
// TODO -> Refactor tests
528530
describe('with force organization selection enabled', () => {
529531
const mockSession = {
530532
id: '1',

packages/clerk-js/src/core/clerk.ts

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import type {
1818
__experimental_CheckoutInstance,
1919
__experimental_CheckoutOptions,
2020
__internal_CheckoutProps,
21-
__internal_ComponentNavigationContext,
2221
__internal_OAuthConsentProps,
2322
__internal_PlanDetailsProps,
2423
__internal_SubscriptionDetailsProps,
@@ -50,6 +49,7 @@ import type {
5049
JoinWaitlistParams,
5150
ListenerCallback,
5251
NavigateOptions,
52+
OnPendingSessionFn,
5353
OrganizationListProps,
5454
OrganizationProfileProps,
5555
OrganizationResource,
@@ -231,7 +231,6 @@ export class Clerk implements ClerkInterface {
231231
#options: ClerkOptions = {};
232232
#pageLifecycle: ReturnType<typeof createPageLifecycle> | null = null;
233233
#touchThrottledUntil = 0;
234-
#componentNavigationContext: __internal_ComponentNavigationContext | null = null;
235234
#publicEventBus = createClerkEventBus();
236235

237236
public __internal_getCachedResources:
@@ -1194,7 +1193,13 @@ export class Clerk implements ClerkInterface {
11941193
/**
11951194
* `setActive` can be used to set the active session and/or organization.
11961195
*/
1197-
public setActive = async ({ session, organization, beforeEmit, redirectUrl }: SetActiveParams): Promise<void> => {
1196+
public setActive = async ({
1197+
session,
1198+
organization,
1199+
beforeEmit,
1200+
redirectUrl,
1201+
onPendingSession,
1202+
}: SetActiveParams): Promise<void> => {
11981203
this.__internal_setActiveInProgress = true;
11991204
try {
12001205
if (!this.client) {
@@ -1253,7 +1258,7 @@ export class Clerk implements ClerkInterface {
12531258
}
12541259

12551260
if (newSession?.status === 'pending') {
1256-
await this.#handlePendingSession(newSession);
1261+
await this.#handlePendingSession(newSession, onPendingSession);
12571262
return;
12581263
}
12591264

@@ -1323,41 +1328,18 @@ export class Clerk implements ClerkInterface {
13231328
}
13241329
};
13251330

1326-
#handlePendingSession = async (session: PendingSessionResource) => {
1327-
/**
1328-
* Do not revalidate server cache when `setActive` is called with a pending
1329-
* session within components, to avoid flash of content and unmount during
1330-
* internal navigation
1331-
*/
1332-
const shouldInvalidateCache = !this.#componentNavigationContext;
1333-
1334-
const onBeforeSetActive: SetActiveHook =
1335-
shouldInvalidateCache &&
1336-
typeof window !== 'undefined' &&
1337-
typeof window.__unstable__onBeforeSetActive === 'function'
1338-
? window.__unstable__onBeforeSetActive
1339-
: noop;
1340-
1341-
const onAfterSetActive: SetActiveHook =
1342-
shouldInvalidateCache &&
1343-
typeof window !== 'undefined' &&
1344-
typeof window.__unstable__onAfterSetActive === 'function'
1345-
? window.__unstable__onAfterSetActive
1346-
: noop;
1347-
1348-
await onBeforeSetActive();
1349-
1331+
#handlePendingSession = async (session: PendingSessionResource, onPendingSession?: OnPendingSessionFn) => {
13501332
if (!this.environment) {
13511333
return;
13521334
}
13531335

1354-
let newSession: SignedInSessionResource | null = session;
1336+
let currentSession: SignedInSessionResource | null = session;
13551337

1356-
// Handles multi-session scenario when switching between `pending` sessions
1357-
// and satisfying task requirements such as organization selection
13581338
if (inActiveBrowserTab() || !this.#options.standardBrowser) {
1339+
// Handles multi-session scenario when switching between `pending` sessions
1340+
// and satisfying task requirements such as organization selection
13591341
await this.#touchCurrentSession(session);
1360-
newSession = this.#getSessionFromClient(session.id) ?? session;
1342+
currentSession = this.#getSessionFromClient(session.id) ?? session;
13611343
}
13621344

13631345
// Syncs __session and __client_uat, in case the `pending` session
@@ -1367,19 +1349,26 @@ export class Clerk implements ClerkInterface {
13671349
eventBus.emit(events.TokenUpdate, { token: null });
13681350
}
13691351

1370-
if (newSession?.currentTask) {
1371-
await navigateToTask(session.currentTask.key, {
1372-
options: this.#options,
1373-
environment: this.environment,
1374-
globalNavigate: this.navigate,
1375-
componentNavigationContext: this.#componentNavigationContext,
1352+
if (currentSession.status === 'pending') {
1353+
const tracker = createBeforeUnloadTracker(this.#options.standardBrowser);
1354+
const onPendingSessionHook = this.__internal_getOption('onPendingSession') ?? onPendingSession;
1355+
const taskUrls = this.__internal_getOption('taskUrls');
1356+
1357+
await tracker.track(async () => {
1358+
if (onPendingSessionHook) {
1359+
await onPendingSessionHook({ session: currentSession });
1360+
} else if (taskUrls) {
1361+
await this.navigate(taskUrls[session.currentTask.key]);
1362+
}
13761363
});
1364+
1365+
if (tracker.isUnloading()) {
1366+
return;
1367+
}
13771368
}
13781369

1379-
this.#setAccessors(session);
1370+
this.#setAccessors(currentSession);
13801371
this.#emit();
1381-
1382-
await onAfterSetActive();
13831372
};
13841373

13851374
public addListener = (listener: ListenerCallback): UnsubscribeCallback => {
@@ -1416,12 +1405,6 @@ export class Clerk implements ClerkInterface {
14161405
return unsubscribe;
14171406
};
14181407

1419-
public __internal_setComponentNavigationContext = (context: __internal_ComponentNavigationContext) => {
1420-
this.#componentNavigationContext = context;
1421-
1422-
return () => (this.#componentNavigationContext = null);
1423-
};
1424-
14251408
public navigate = async (to: string | undefined, options?: NavigateOptions): Promise<unknown> => {
14261409
if (!to || !inBrowser()) {
14271410
return;
@@ -1851,10 +1834,18 @@ export class Clerk implements ClerkInterface {
18511834
});
18521835
};
18531836

1837+
const onPendingSession: OnPendingSessionFn = ({ session }) =>
1838+
navigateToTask(session, {
1839+
baseUrl: displayConfig.signInUrl,
1840+
navigate: this.navigate,
1841+
options: this.#options,
1842+
});
1843+
18541844
if (si.status === 'complete') {
18551845
return this.setActive({
18561846
session: si.sessionId,
18571847
redirectUrl: redirectUrls.getAfterSignInUrl(),
1848+
onPendingSession,
18581849
});
18591850
}
18601851

@@ -1868,6 +1859,7 @@ export class Clerk implements ClerkInterface {
18681859
return this.setActive({
18691860
session: res.createdSessionId,
18701861
redirectUrl: redirectUrls.getAfterSignInUrl(),
1862+
onPendingSession,
18711863
});
18721864
case 'needs_first_factor':
18731865
return navigateToFactorOne();
@@ -1917,6 +1909,7 @@ export class Clerk implements ClerkInterface {
19171909
return this.setActive({
19181910
session: res.createdSessionId,
19191911
redirectUrl: redirectUrls.getAfterSignUpUrl(),
1912+
onPendingSession,
19201913
});
19211914
case 'missing_requirements':
19221915
return navigateToNextStepSignUp({ missingFields: res.missingFields });
@@ -2142,6 +2135,12 @@ export class Clerk implements ClerkInterface {
21422135
await this.setActive({
21432136
session: signInOrSignUp.createdSessionId,
21442137
redirectUrl,
2138+
onPendingSession: ({ session }) =>
2139+
navigateToTask(session, {
2140+
baseUrl: displayConfig.signInUrl,
2141+
navigate: this.navigate,
2142+
options: this.#options,
2143+
}),
21452144
});
21462145
}
21472146
break;

packages/clerk-js/src/core/sessionTasks.ts

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import type {
2-
__internal_ComponentNavigationContext,
3-
ClerkOptions,
4-
EnvironmentResource,
5-
SessionTask,
6-
} from '@clerk/types';
1+
import type { ClerkOptions, PendingSessionResource, SessionTask } from '@clerk/types';
72

83
import { buildURL } from '../utils';
94

@@ -12,38 +7,23 @@ export const INTERNAL_SESSION_TASK_ROUTE_BY_KEY: Record<SessionTask['key'], stri
127
} as const;
138

149
interface NavigateToTaskOptions {
15-
componentNavigationContext: __internal_ComponentNavigationContext | null;
16-
globalNavigate: (to: string) => Promise<unknown>;
17-
options: ClerkOptions;
18-
environment: EnvironmentResource;
10+
navigate: (to: string) => Promise<unknown>;
11+
baseUrl: string;
12+
options?: ClerkOptions;
1913
}
2014

2115
/**
22-
* Handles navigation to the tasks URL based on the application context such
23-
* as internal component routing or custom flows.
2416
* @internal
2517
*/
26-
export function navigateToTask(
27-
routeKey: keyof typeof INTERNAL_SESSION_TASK_ROUTE_BY_KEY,
28-
{ componentNavigationContext, globalNavigate, options, environment }: NavigateToTaskOptions,
29-
) {
30-
const customTaskUrl = options?.taskUrls?.[routeKey];
31-
const internalTaskRoute = `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[routeKey]}`;
18+
export function navigateToTask(session: PendingSessionResource, { options, navigate, baseUrl }: NavigateToTaskOptions) {
19+
const currentTaskKey = session.currentTask.key;
3220

33-
if (componentNavigationContext && !customTaskUrl) {
34-
return componentNavigationContext.navigate(componentNavigationContext.indexPath + internalTaskRoute);
35-
}
36-
37-
const signInUrl = options['signInUrl'] || environment.displayConfig.signInUrl;
38-
const signUpUrl = options['signUpUrl'] || environment.displayConfig.signUpUrl;
39-
const isReferrerSignUpUrl = window.location.href.startsWith(signUpUrl);
40-
41-
return globalNavigate(
42-
customTaskUrl ??
21+
return navigate(
22+
options?.taskUrls?.[currentTaskKey] ??
4323
buildURL(
4424
{
45-
base: isReferrerSignUpUrl ? signUpUrl : signInUrl,
46-
hashPath: internalTaskRoute,
25+
base: baseUrl,
26+
hashPath: `/tasks/${INTERNAL_SESSION_TASK_ROUTE_BY_KEY[currentTaskKey]}`,
4727
},
4828
{ stringify: true },
4929
),

packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/ChooseOrganizationScreen.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export const ChooseOrganizationScreen = withCardStateProvider(
9797

9898
const MembershipPreview = withCardStateProvider((props: { organization: OrganizationResource }) => {
9999
const card = useCardState();
100+
100101
const { redirectUrlComplete } = useSessionTasksContext();
101102
const { isLoaded, setActive } = useOrganizationList();
102103

@@ -108,6 +109,7 @@ const MembershipPreview = withCardStateProvider((props: { organization: Organiza
108109
return card.runAsync(async () => {
109110
await setActive({
110111
organization,
112+
redirectUrl: redirectUrlComplete,
111113
});
112114
});
113115
};

packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const CreateOrganizationScreen = withCardStateProvider((props: CreateOrga
4545
try {
4646
const organization = await createOrganization({ name: nameField.value, slug: slugField.value });
4747

48-
await setActive({ organization });
48+
await setActive({ organization, redirectUrl: redirectUrlComplete });
4949
} catch (err) {
5050
handleError(err, [nameField, slugField], card.setError);
5151
}

packages/clerk-js/src/ui/components/SignIn/SignInFactorOneAlternativeChannelCodeForm.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const SignInFactorOneAlternativeChannelCodeForm = (props: SignInFactorOne
3434
const signIn = useCoreSignIn();
3535
const card = useCardState();
3636
const { navigate } = useRouter();
37-
const { afterSignInUrl } = useSignInContext();
37+
const { afterSignInUrl, onPendingSession } = useSignInContext();
3838
const { setActive } = useClerk();
3939
const supportEmail = useSupportEmail();
4040
const clerk = useClerk();
@@ -65,7 +65,11 @@ export const SignInFactorOneAlternativeChannelCodeForm = (props: SignInFactorOne
6565

6666
switch (res.status) {
6767
case 'complete':
68-
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl });
68+
return setActive({
69+
session: res.createdSessionId,
70+
redirectUrl: afterSignInUrl,
71+
onPendingSession,
72+
});
6973
case 'needs_second_factor':
7074
return navigate('../factor-two');
7175
case 'needs_new_password':

packages/clerk-js/src/ui/components/SignIn/SignInFactorOneCodeForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const SignInFactorOneCodeForm = (props: SignInFactorOneCodeFormProps) =>
3535
const signIn = useCoreSignIn();
3636
const card = useCardState();
3737
const { navigate } = useRouter();
38-
const { afterSignInUrl } = useSignInContext();
38+
const { afterSignInUrl, onPendingSession } = useSignInContext();
3939
const { setActive } = useClerk();
4040
const supportEmail = useSupportEmail();
4141
const clerk = useClerk();
@@ -104,7 +104,7 @@ export const SignInFactorOneCodeForm = (props: SignInFactorOneCodeFormProps) =>
104104

105105
switch (res.status) {
106106
case 'complete':
107-
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl });
107+
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl, onPendingSession });
108108
case 'needs_second_factor':
109109
return navigate('../factor-two');
110110
case 'needs_new_password':

packages/clerk-js/src/ui/components/SignIn/SignInFactorOnePasswordCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps)
5555
const card = useCardState();
5656
const { setActive } = useClerk();
5757
const signIn = useCoreSignIn();
58-
const { afterSignInUrl } = useSignInContext();
58+
const { afterSignInUrl, onPendingSession } = useSignInContext();
5959
const supportEmail = useSupportEmail();
6060
const passwordControl = usePasswordControl(props);
6161
const { navigate } = useRouter();
@@ -74,7 +74,7 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps)
7474
.then(res => {
7575
switch (res.status) {
7676
case 'complete':
77-
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl });
77+
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl, onPendingSession });
7878
case 'needs_second_factor':
7979
return navigate('../factor-two');
8080
default:

packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoBackupCodeCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type SignInFactorTwoBackupCodeCardProps = {
2424
export const SignInFactorTwoBackupCodeCard = (props: SignInFactorTwoBackupCodeCardProps) => {
2525
const { onShowAlternativeMethodsClicked } = props;
2626
const signIn = useCoreSignIn();
27-
const { afterSignInUrl } = useSignInContext();
27+
const { afterSignInUrl, onPendingSession } = useSignInContext();
2828
const { setActive } = useClerk();
2929
const { navigate } = useRouter();
3030
const supportEmail = useSupportEmail();
@@ -52,7 +52,7 @@ export const SignInFactorTwoBackupCodeCard = (props: SignInFactorTwoBackupCodeCa
5252
queryParams.set('createdSessionId', res.createdSessionId);
5353
return navigate(`../reset-password-success?${queryParams.toString()}`);
5454
}
55-
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl });
55+
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl, onPendingSession });
5656
default:
5757
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
5858
}

packages/clerk-js/src/ui/components/SignIn/SignInFactorTwoCodeForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ type SignInFactorTwoCodeFormProps = SignInFactorTwoCodeCard & {
3333
export const SignInFactorTwoCodeForm = (props: SignInFactorTwoCodeFormProps) => {
3434
const signIn = useCoreSignIn();
3535
const card = useCardState();
36-
const { afterSignInUrl } = useSignInContext();
36+
const { afterSignInUrl, onPendingSession } = useSignInContext();
3737
const { setActive } = useClerk();
3838
const { navigate } = useRouter();
3939
const supportEmail = useSupportEmail();
@@ -79,7 +79,7 @@ export const SignInFactorTwoCodeForm = (props: SignInFactorTwoCodeFormProps) =>
7979
queryParams.set('createdSessionId', res.createdSessionId);
8080
return navigate(`../reset-password-success?${queryParams.toString()}`);
8181
}
82-
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl });
82+
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl, onPendingSession });
8383
default:
8484
return console.error(clerkInvalidFAPIResponse(res.status, supportEmail));
8585
}

0 commit comments

Comments
 (0)