1import { useAuth, useClerk } from "@clerk/react";
2import { ServerIcon, SmartphoneIcon } from "lucide-react";
3import { type ReactNode, useCallback, useState } from "react";
4import { createPortal } from "react-dom";
5
6import { MobileClientsUserProfilePage } from "./MobileClientsUserProfilePage";
7import { T3ConnectUserProfilePage } from "./T3ConnectUserProfilePage";
8
9/** Custom pages in the Clerk account modal, in menu order. */
10export const T3_CONNECT_ACCOUNT_PAGES = [
11 {
12 label: "Mobile clients",
13 url: "mobile-clients",
14 icon: <SmartphoneIcon className="size-4" />,
15 content: <MobileClientsUserProfilePage />,
16 },
17 {
18 label: "T3 Connect",
19 url: "t3-connect",
20 icon: <ServerIcon className="size-4" />,
21 content: <T3ConnectUserProfilePage />,
22 },
23] as const;
24
25type PortalTargets = Readonly<Record<string, HTMLDivElement | undefined>>;
26
27/**
28 * Opens the Clerk account modal on the T3 Connect page from outside the
29 * UserButton. Clerk mounts custom pages into DOM nodes it owns, so the caller
30 * must keep `portals` rendered for as long as the modal can be open.
31 */
32export function useT3ConnectAccountPage(): {
33 readonly open: (() => void) | null;
34 readonly portals: ReactNode;
35} {
36 const clerk = useClerk();
37 const { isSignedIn } = useAuth();
38 const [targets, setTargets] = useState<PortalTargets>({});
39
40 const open = useCallback(() => {
41 const setTarget = (key: string, element: HTMLDivElement | undefined) =>
42 setTargets((current) => ({ ...current, [key]: element }));
43 clerk.openUserProfile({
44 __experimental_startPath: "/t3-connect",
45 customPages: T3_CONNECT_ACCOUNT_PAGES.map((page) => ({
46 label: page.label,
47 url: page.url,
48 mount: (element: HTMLDivElement) => setTarget(`content:${page.url}`, element),
49 unmount: () => setTarget(`content:${page.url}`, undefined),
50 mountIcon: (element: HTMLDivElement) => setTarget(`icon:${page.url}`, element),
51 unmountIcon: () => setTarget(`icon:${page.url}`, undefined),
52 })),
53 });
54 }, [clerk]);
55
56 const portals = T3_CONNECT_ACCOUNT_PAGES.flatMap((page) => {
57 const content = targets[`content:${page.url}`];
58 const icon = targets[`icon:${page.url}`];
59 return [
60 content ? createPortal(page.content, content, `content:${page.url}`) : null,
61 icon ? createPortal(page.icon, icon, `icon:${page.url}`) : null,
62 ];
63 });
64
65 return { open: isSignedIn ? open : null, portals };
66}