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.
153 lines
5.9 KiB
TypeScript
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]);
|
|
}
|