Settled messages no longer rebuild on every token, Inter is actually requested, and admin routes drop the 33 KB chat stylesheet. Root force-dynamic is gone so public shells can prerender without changing the no-store Cache-Control contract. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
2.2 KiB
TypeScript
78 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { X } from "lucide-react";
|
|
import { memo, useEffect, useState, type MutableRefObject, type ReactNode } from "react";
|
|
|
|
export type AccountOverlayLibraryControls = Readonly<{
|
|
open: boolean;
|
|
toggle: () => void;
|
|
}>;
|
|
|
|
export type AccountOverlayModel = Readonly<{
|
|
title: string;
|
|
dialogClass: string;
|
|
signingOut: boolean;
|
|
close: () => void;
|
|
takeOpenLibraryRequest: () => boolean;
|
|
overlayRef: MutableRefObject<HTMLElement | null>;
|
|
closeButtonRef: MutableRefObject<HTMLButtonElement | null>;
|
|
renderProfile: (library: AccountOverlayLibraryControls) => ReactNode;
|
|
renderLogout: () => ReactNode;
|
|
}>;
|
|
|
|
export const AccountDialogOverlay = memo(function AccountDialogOverlay({
|
|
open,
|
|
dialog,
|
|
openEpoch,
|
|
modelRef,
|
|
}: Readonly<{
|
|
open: boolean;
|
|
dialog: "profile" | "logout" | null;
|
|
openEpoch: number;
|
|
modelRef: MutableRefObject<AccountOverlayModel | null>;
|
|
}>) {
|
|
const [chartLibraryOpen, setChartLibraryOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
if (modelRef.current?.takeOpenLibraryRequest()) setChartLibraryOpen(true);
|
|
}, [dialog, modelRef, open]);
|
|
|
|
if (!open || dialog === null) return null;
|
|
const model = modelRef.current;
|
|
if (!model) return null;
|
|
void openEpoch;
|
|
|
|
return (
|
|
<div className="account-modal-overlay" onMouseDown={model.close}>
|
|
<section
|
|
className={`account-modal ${model.dialogClass}`}
|
|
ref={model.overlayRef}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby="account-dialog-title"
|
|
onMouseDown={(event) => event.stopPropagation()}
|
|
>
|
|
<header className="account-modal-header">
|
|
<h2 id="account-dialog-title">{model.title}</h2>
|
|
<button
|
|
className="dialog-close"
|
|
ref={model.closeButtonRef}
|
|
aria-label="关闭"
|
|
type="button"
|
|
onClick={model.close}
|
|
disabled={model.signingOut}
|
|
>
|
|
<X aria-hidden="true" />
|
|
</button>
|
|
</header>
|
|
{dialog === "profile" && model.renderProfile({
|
|
open: chartLibraryOpen,
|
|
toggle: () => setChartLibraryOpen((current) => !current),
|
|
})}
|
|
{dialog === "logout" && model.renderLogout()}
|
|
</section>
|
|
</div>
|
|
);
|
|
});
|