Add a client component for post-auth routing (#37)

This commit is contained in:
Chris Estreich 2025-05-18 23:52:52 -07:00 committed by GitHub
parent dfe41a5da6
commit 486b641381
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 125 additions and 63 deletions

View file

@ -0,0 +1,59 @@
'use client';
import { useState, useRef, useEffect } from 'react';
import { useSessionStorage } from 'react-use';
import { useRouter } from 'next/navigation';
import { useAuth } from '@clerk/nextjs';
import { LoaderCircle, CircleCheck, CircleX } from 'lucide-react';
import { Card, CardContent } from '@/components/ui';
export const Authorized = () => {
const { isSignedIn, orgId } = useAuth();
const [isLoading, setIsLoading] = useState(true);
const isLoadingRef = useRef(true);
const router = useRouter();
const [state, setState] = useSessionStorage<string | undefined>('state');
useEffect(() => {
if (typeof isSignedIn !== 'undefined' && isLoadingRef.current) {
isLoadingRef.current = false;
setTimeout(() => setIsLoading(false), 250);
}
}, [isSignedIn]);
useEffect(() => {
if (!isLoading) {
let path;
if (!isSignedIn) {
path = state ? `/sign-in?state=${state}` : '/sign-in';
} else if (!orgId) {
path = state ? `/select-org/${state}` : '/select-org';
} else {
path = state ? `/extension/sign-in?state=${state}` : '/dashboard';
}
setTimeout(() => router.push(path), 1000);
}
}, [router, isLoading, isSignedIn, orgId, state, setState]);
return (
<Card className="max-w-md mx-auto">
<CardContent className="flex flex-col gap-4">
<div className="flex flex-row items-center gap-2">
<div className="font-medium text-sm text-muted-foreground">
Authenticating
</div>
{isLoading ? (
<LoaderCircle className="animate-spin" />
) : isSignedIn ? (
<CircleCheck className="text-lime-500" />
) : (
<CircleX className="text-rose-500" />
)}
</div>
</CardContent>
</Card>
);
};

View file

@ -0,0 +1,5 @@
import { Authorized } from './Authorized';
export default function Page() {
return <Authorized />;
}

View file

@ -18,14 +18,16 @@ export default async function Page(props: Props) {
}
const { userId, orgId } = await auth();
const code = userId ? await getSignInToken(userId) : undefined;
const code = userId
? await getSignInToken(userId).catch(() => undefined)
: undefined;
if (!code) {
redirect(`/sign-in?state=${state}`);
}
if (!orgId) {
redirect(`/select-org?state=${state}`);
redirect(`/select-org/${state}`);
}
const searchParams = new URLSearchParams({ state, code });

View file

@ -1,22 +0,0 @@
'use client';
import { useMemo } from 'react';
import { useSearchParams } from 'next/navigation';
import { OrganizationList } from '@clerk/nextjs';
export const SelectOrg = () => {
const searchParams = useSearchParams();
const afterSelectOrganizationUrl = useMemo(() => {
const state = searchParams.get('state');
return state ? `/extension/sign-in?state=${state}` : '/dashboard';
}, [searchParams]);
return (
<OrganizationList
afterSelectOrganizationUrl={afterSelectOrganizationUrl}
afterCreateOrganizationUrl={afterSelectOrganizationUrl}
hidePersonal
/>
);
};

View file

@ -0,0 +1,15 @@
import { OrganizationList } from '@clerk/nextjs';
export const SelectOrg = ({ state }: { state?: string }) => {
const redirectUrl = state
? `/extension/sign-in?state=${state}`
: '/dashboard';
return (
<OrganizationList
afterSelectOrganizationUrl={redirectUrl}
afterCreateOrganizationUrl={redirectUrl}
hidePersonal
/>
);
};

View file

@ -0,0 +1,10 @@
import { SelectOrg } from './SelectOrg';
type Props = {
params: Promise<{ state: string }>;
};
export default async function Page({ params }: Props) {
const { state } = await params;
return <SelectOrg state={state} />;
}

View file

@ -1,5 +0,0 @@
import { SelectOrg } from './SelectOrg';
export default async function Page() {
return <SelectOrg />;
}

View file

@ -1,16 +0,0 @@
'use client';
import { useMemo } from 'react';
import { useSearchParams } from 'next/navigation';
import { SignIn as ClerkSignIn } from '@clerk/nextjs';
export const SignIn = () => {
const searchParams = useSearchParams();
const forceRedirectUrl = useMemo(() => {
const state = searchParams.get('state');
return state ? `/extension/sign-in?state=${state}` : undefined;
}, [searchParams]);
return <ClerkSignIn forceRedirectUrl={forceRedirectUrl} />;
};

View file

@ -1,5 +1,20 @@
import { SignIn } from './SignIn';
'use client';
import { useSessionStorage, useMount } from 'react-use';
import { useSearchParams } from 'next/navigation';
import { SignIn } from '@clerk/nextjs';
export default function Page() {
const searchParams = useSearchParams();
const [, setState] = useSessionStorage<string | undefined>('state');
useMount(() => {
const state = searchParams.get('state');
if (state) {
setState(state);
}
});
return <SignIn />;
}

View file

@ -1,16 +0,0 @@
'use client';
import { useMemo } from 'react';
import { useSearchParams } from 'next/navigation';
import { SignUp as ClerkSignUp } from '@clerk/nextjs';
export const SignUp = () => {
const searchParams = useSearchParams();
const forceRedirectUrl = useMemo(() => {
const state = searchParams.get('state');
return state ? `/extension/sign-in?state=${state}` : undefined;
}, [searchParams]);
return <ClerkSignUp forceRedirectUrl={forceRedirectUrl} />;
};

View file

@ -1,5 +1,20 @@
import { SignUp } from './SignUp';
'use client';
import { useSessionStorage, useMount } from 'react-use';
import { useSearchParams } from 'next/navigation';
import { SignUp } from '@clerk/nextjs';
export default function Page() {
const searchParams = useSearchParams();
const [, setState] = useSessionStorage<string | undefined>('state');
useMount(() => {
const state = searchParams.get('state');
if (state) {
setState(state);
}
});
return <SignUp />;
}