Files
Jyotisha/frontend/src/hooks/use-home-shell-registration.ts
T
jesse-ux 5a4b87383f
Independent Staging Quality Gate / validate (push) Failing after 6m35s
Independent Staging Quality Gate / publish (push) Skipped
fix(web): retire session archive UI and API, restore archived rows
Remove archive/restore from the session menu and drop archived=1 listing. PATCH still accepts archived_at from old bundles then ignores it. A forward migration clears archived_at without bumping updated_at. Delete stays unchanged.
2026-09-21 18:49:40 +08:00

153 lines
5.9 KiB
TypeScript

"use client";
import { useEffect, type Dispatch, type RefObject, type SetStateAction } from "react";
import type { ChatSession } from "@/lib/home-types";
import type { Account } from "@/lib/home-types";
import type { ChartLibraryRecord, Profile } from "@/lib/home-types";
import type { PublicLanguageModelCatalog } from "@/lib/public-models";
import { toSidebarSessionRow } from "@/lib/session-sidebar-row";
import type { ShellRegistration } from "@/lib/session-list-context";
type AccountDialogName = "profile" | "chart-library" | "general" | "billing" | "logout";
type HomeShellRegistrationParams = {
account: Account | null;
accountMenuOpen: boolean;
accountTrigger: RefObject<HTMLButtonElement | null>;
activeSessionId: string | undefined;
cancellationPending: boolean;
chartLibrary: readonly ChartLibraryRecord[];
creatingSession: boolean;
hydrated: boolean;
loadMoreSessions: () => void;
modalOpen: boolean;
modelCatalog: PublicLanguageModelCatalog | null;
openAccountDialog: (dialog: AccountDialogName, options?: { source?: string }) => void;
pendingSessionId: string | null;
profile: Profile;
rectificationErrorMessage: string;
rectificationErrorSessionId: string | null;
rectificationOpeningSessionId: string | null;
rectificationSurfaceOpen: boolean;
registerShellControls: (registration: ShellRegistration | null) => void;
renameSession: (session: ChatSession) => void | Promise<void>;
selectSession: (sessionId: string) => void;
sessionMenuId: string | null;
sessions: readonly ChatSession[];
sessionsCursor: string | null;
setAccountMenuOpen: Dispatch<SetStateAction<boolean>>;
setPendingSessionDeletion: Dispatch<SetStateAction<ChatSession | null>>;
setSessionMenuId: Dispatch<SetStateAction<string | null>>;
shareSession: (session: ChatSession) => void | Promise<void>;
startNewChat: () => void | Promise<unknown>;
togglePinnedSession: (sessionId: string) => void;
visibleSessions: readonly ChatSession[];
};
export function useHomeShellRegistration(params: HomeShellRegistrationParams) {
const {
account,
accountMenuOpen,
accountTrigger,
activeSessionId,
cancellationPending,
chartLibrary,
creatingSession,
hydrated,
loadMoreSessions,
modalOpen,
modelCatalog,
openAccountDialog,
pendingSessionId,
profile,
rectificationErrorMessage,
rectificationErrorSessionId,
rectificationOpeningSessionId,
rectificationSurfaceOpen,
registerShellControls,
renameSession,
selectSession,
sessionMenuId,
sessions,
sessionsCursor,
setAccountMenuOpen,
setPendingSessionDeletion,
setSessionMenuId,
shareSession,
startNewChat,
togglePinnedSession,
visibleSessions,
} = params;
useEffect(() => {
if (!hydrated || !account) {
registerShellControls(null);
return;
}
const name = profile.name.trim();
const email = account.user.email || "尚未读取邮箱";
registerShellControls({
escapeBlocked: accountMenuOpen || modalOpen,
activeSessionId: activeSessionId ?? null,
openingSessionId: rectificationOpeningSessionId,
openErrorSessionId: rectificationErrorSessionId,
openErrorMessage: rectificationErrorSessionId ? rectificationErrorMessage : "",
insetClassName: `chat-panel${rectificationSurfaceOpen ? " is-rectification" : ""}`,
insetInert: modalOpen,
sidebarAccount: {
name: name || account.user.email || "账户",
email,
credits: account.credits,
avatar: account.avatar,
initial: name.slice(0, 1) || account.user.email?.slice(0, 1).toUpperCase() || "你",
},
sidebarSessions: visibleSessions.map((session) => toSidebarSessionRow(session, chartLibrary)),
controls: {
newChatDisabled: !hydrated || !modelCatalog || creatingSession || Boolean(pendingSessionId) || cancellationPending,
creatingSession,
accountMenuOpen,
accountTriggerRef: accountTrigger,
sessionControls: {
hasMore: Boolean(sessionsCursor),
onLoadMore: loadMoreSessions,
menuSessionId: sessionMenuId,
disabled: Boolean(pendingSessionId) || cancellationPending,
onMenuSessionChange: setSessionMenuId,
onTogglePinned: togglePinnedSession,
onRename: (sessionId) => {
const session = sessions.find((candidate) => candidate.id === sessionId);
if (session) void renameSession(session);
},
onShare: (sessionId) => {
const session = sessions.find((candidate) => candidate.id === sessionId);
if (session) void shareSession(session);
},
onDelete: (sessionId) => {
const session = sessions.find((candidate) => candidate.id === sessionId);
if (session) setPendingSessionDeletion(session);
},
},
onAccountMenuOpenChange: setAccountMenuOpen,
onNewChat: () => { void startNewChat(); },
onSelectSession: selectSession,
onOpenProfile: () => openAccountDialog("profile"),
onOpenChartLibrary: () => openAccountDialog("chart-library"),
onOpenGeneral: () => openAccountDialog("general"),
onOpenBilling: () => openAccountDialog("billing", { source: "account-menu" }),
onOpenLogout: () => openAccountDialog("logout"),
},
});
}, [
account, accountMenuOpen, accountTrigger, activeSessionId, cancellationPending,
chartLibrary, creatingSession, hydrated, loadMoreSessions, modalOpen, modelCatalog,
openAccountDialog, pendingSessionId, profile.name, rectificationErrorMessage,
rectificationErrorSessionId, rectificationOpeningSessionId, rectificationSurfaceOpen,
registerShellControls, renameSession, selectSession, sessionMenuId, sessions,
sessionsCursor, setAccountMenuOpen, setPendingSessionDeletion, setSessionMenuId,
shareSession, startNewChat, togglePinnedSession, visibleSessions,
]);
useEffect(() => () => registerShellControls(null), [registerShellControls]);
}