Skip to content

Commit fafd3b7

Browse files
committed
Trigger internal navigation with event bus
The following changes leverage `eventBus` to trigger a internal component navigation from a `Clerk` method, in this case, `setActive` It detects if there are listeners for the `InternalTaskNavigate` event, and if so execute it, otherwise fallbacks to `Clerk.navigate` for custom flows
1 parent a1c485c commit fafd3b7

File tree

7 files changed

+57
-29
lines changed

7 files changed

+57
-29
lines changed

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

+22-12
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,16 @@ export class Clerk implements ClerkInterface {
927927
}
928928

929929
const hasSessionTaskToResolve = !!newSession?.tasks?.length;
930+
if (hasSessionTaskToResolve && this.#options.standardBrowser) {
931+
const hasEventHandler = eventBus.has(events.InternalComponentNavigate);
932+
if (hasEventHandler) {
933+
await new Promise<void>(resolveNavigation =>
934+
eventBus.dispatch(events.InternalComponentNavigate, resolveNavigation),
935+
);
936+
} else {
937+
await this.navigate(this.internal__buildSessionTaskUrl());
938+
}
939+
}
930940

931941
//2. If there's a beforeEmit, typically we're navigating. Emit the session as
932942
// undefined, then wait for beforeEmit to complete before emitting the new session.
@@ -940,15 +950,14 @@ export class Clerk implements ClerkInterface {
940950
);
941951
beforeUnloadTracker?.startTracking();
942952

943-
// Do not unmount SignIn/SignUp for in-component navigation on new session tasks
944-
if (!hasSessionTaskToResolve) {
945-
this.#setTransitiveState();
946-
}
953+
this.#setTransitiveState();
947954

948955
await beforeEmit(newSession);
949956
beforeUnloadTracker?.stopTracking();
950957
}
951958

959+
// `hasSessionTaskToResolve` needs to be checked in order to override
960+
// redirects to `afterSignInUrl/afterSignUpUrl` when there are pending tasks
952961
if (redirectUrl && !beforeEmit && !hasSessionTaskToResolve) {
953962
beforeUnloadTracker?.startTracking();
954963
this.#setTransitiveState();
@@ -1148,21 +1157,22 @@ export class Clerk implements ClerkInterface {
11481157
return this.buildUrlWithAuth(this.environment.displayConfig.organizationProfileUrl);
11491158
}
11501159

1151-
public buildSessionTaskUrl(): string {
1160+
// pass this to the session object as internal
1161+
public internal__buildSessionTaskUrl(): string {
11521162
const [currentTask] = this.session?.tasks ?? [];
11531163

11541164
if (!currentTask || !this.environment || !this.environment.displayConfig) {
11551165
return '';
11561166
}
11571167

1158-
// TODO - Make it type safe and abstracted
1159-
const mapTaskRoutePathFromKey = {
1160-
orgs: '/add-organization',
1161-
};
1162-
11631168
const signInUrl = this.#options['signInUrl'] || this.environment.displayConfig.signInUrl;
1164-
// TODO - Make it path dynamic by current session task
1165-
return buildURL({ base: `${signInUrl}`, hashPath: mapTaskRoutePathFromKey[currentTask.key] }, { stringify: true });
1169+
const signUpUrl = this.#options['signUpUrl'] || this.environment.displayConfig.signUpUrl;
1170+
const isReferrerSignUpUrl = window.location.href.startsWith(signUpUrl);
1171+
1172+
return buildURL(
1173+
{ base: isReferrerSignUpUrl ? signUpUrl : signInUrl, hashPath: '/add-organization' },
1174+
{ stringify: true },
1175+
);
11661176
}
11671177

11681178
#redirectToSatellite = async (): Promise<unknown> => {

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { TokenResource } from '@clerk/types';
33
export const events = {
44
TokenUpdate: 'token:update',
55
UserSignOut: 'user:signOut',
6+
InternalComponentNavigate: 'task:internalNavigate',
67
} as const;
78

89
type ClerkEvent = (typeof events)[keyof typeof events];
@@ -13,6 +14,7 @@ type TokenUpdatePayload = { token: TokenResource | null };
1314
type EventPayload = {
1415
[events.TokenUpdate]: TokenUpdatePayload;
1516
[events.UserSignOut]: null;
17+
[events.InternalComponentNavigate]: () => void;
1618
};
1719

1820
const createEventBus = () => {
@@ -45,7 +47,11 @@ const createEventBus = () => {
4547
eventToHandlersMap.set(event, []);
4648
};
4749

48-
return { on, dispatch, off };
50+
const has = <E extends ClerkEvent>(event: E) => {
51+
return !!eventToHandlersMap.has(event);
52+
};
53+
54+
return { on, dispatch, off, has };
4955
};
5056

5157
export const eventBus = createEventBus();

packages/clerk-js/src/ui/common/withRedirect.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ export function withRedirect<P extends AvailableComponentProps>(
2929
const options = useOptions();
3030

3131
const hasTaskAndSingleSessionMode = !!clerk.session?.tasks && environment?.authConfig.singleSessionMode;
32-
const shouldRedirect = hasTaskAndSingleSessionMode || condition(clerk, environment, options);
33-
const redirectUrlWithDefault = hasTaskAndSingleSessionMode ? () => clerk.buildSessionTaskUrl() : redirectUrl;
32+
const shouldRedirect = hasTaskAndSingleSessionMode ? false : condition(clerk, environment, options);
33+
const redirectUrlWithDefault = hasTaskAndSingleSessionMode
34+
? () => clerk.internal__buildSessionTaskUrl()
35+
: redirectUrl;
3436

3537
React.useEffect(() => {
3638
if (shouldRedirect) {

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

+4-7
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ function SignInRoutes(): JSX.Element {
7777
redirectUrl='../factor-two'
7878
/>
7979
</Route>
80+
{/* Make it type safe based on the possible route paths / introduce abstraction */}
81+
<Route path='add-organization'>
82+
<Task />
83+
</Route>
8084
{signInContext.isCombinedFlow && (
8185
<Route path='create'>
8286
<Route
@@ -133,13 +137,6 @@ function SignInRoutes(): JSX.Element {
133137
</Route>
134138
</Route>
135139
)}
136-
{/* Make it type safe based on the possible route paths / introduce abstraction */}
137-
<Route
138-
path='add-organization'
139-
canActivate={clerk => !!clerk.session?.tasks?.length}
140-
>
141-
<Task />
142-
</Route>
143140
<Route index>
144141
<SignInStart />
145142
</Route>

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,7 @@ export const SignInFactorOnePasswordCard = (props: SignInFactorOnePasswordProps)
6868
.then(res => {
6969
switch (res.status) {
7070
case 'complete':
71-
return setActive({
72-
session: res.createdSessionId,
73-
redirectUrl: afterSignInUrl,
74-
beforeEmit: () => navigate('../add-organization'),
75-
});
71+
return setActive({ session: res.createdSessionId, redirectUrl: afterSignInUrl });
7672
case 'needs_second_factor':
7773
return navigate('../factor-two');
7874
default:

packages/clerk-js/src/ui/contexts/components/SignIn.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useClerk } from '@clerk/shared/react';
22
import { isAbsoluteUrl } from '@clerk/shared/url';
3-
import { createContext, useContext, useMemo } from 'react';
3+
import { createContext, useContext, useEffect, useMemo } from 'react';
44

55
import { SIGN_IN_INITIAL_VALUE_KEYS } from '../../../core/constants';
6+
import { eventBus, events } from '../../../core/events';
67
import { buildURL } from '../../../utils';
78
import { RedirectUrls } from '../../../utils/redirectUrls';
89
import { buildRedirectUrl, MAGIC_LINK_VERIFY_PATH_ROUTE, SSO_CALLBACK_PATH_ROUTE } from '../../common/redirects';
@@ -111,6 +112,22 @@ export const useSignInContext = (): SignInContextType => {
111112

112113
const signUpContinueUrl = buildURL({ base: signUpUrl, hashPath: '/continue' }, { stringify: true });
113114

115+
useEffect(() => {
116+
eventBus.on(events.InternalComponentNavigate, async resolveNavigation => {
117+
const tasksUrl = buildRedirectUrl({
118+
routing: ctx.routing,
119+
baseUrl: signInUrl,
120+
path: ctx.path,
121+
endpoint: '/add-organization',
122+
authQueryString: null,
123+
});
124+
125+
await navigate(tasksUrl);
126+
127+
resolveNavigation();
128+
});
129+
}, []);
130+
114131
return {
115132
...(ctx as SignInCtx),
116133
transferable: ctx.transferable ?? true,

packages/types/src/clerk.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ export interface Clerk {
476476
*/
477477
buildWaitlistUrl(opts?: { initialValues?: Record<string, string> }): string;
478478

479-
buildSessionTaskUrl(): string;
479+
internal__buildSessionTaskUrl(): string;
480480

481481
/**
482482
*

0 commit comments

Comments
 (0)