fix(web): keep rectification sessions listed and persist chats on first send
Empty-consultation filtering is now session_type scoped so birth-time rows stay in the sidebar. Subtitles use updatedAt with sort/group/cursor. New chats stay local until the first send.
This commit is contained in:
@@ -64,9 +64,11 @@ import { completedOnboardingTranscript, isProfileComplete, selectedBirthPlace }
|
||||
import { pendingConsultationStorageKey, timestamp } from "@/lib/home-types";
|
||||
import {
|
||||
consultSendBlockedByRectificationSession,
|
||||
fallbackSessionId,
|
||||
missingActiveSessionSendAction,
|
||||
SESSION_NOT_CONSULTATION_CODE,
|
||||
} from "@/lib/rectification-session-composer-guard";
|
||||
import { isUnsavedEmptyConsultation } from "@/lib/session-list-filter";
|
||||
import type {
|
||||
Account,
|
||||
AccountDialog,
|
||||
@@ -606,6 +608,22 @@ export function useConsultationRun(params: ConsultationRunParams) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isUnsavedEmptyConsultation(currentSession)) {
|
||||
try {
|
||||
await persistSession(currentSession, "create");
|
||||
} catch (caught) {
|
||||
const remaining = sessions.filter((session) => session.id !== currentSession.id);
|
||||
setSessions(remaining);
|
||||
const fallbackId = fallbackSessionId(remaining);
|
||||
setActiveSessionId(fallbackId);
|
||||
setRequestError({
|
||||
sessionId: fallbackId,
|
||||
message: caught instanceof Error ? caught.message : "新对话未能保存到云端。",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const [year, month, day] = profile.date.split("-").map(Number);
|
||||
const [hour, minute] = consultationRoute.time?.split(":").map(Number) ?? [];
|
||||
|
||||
|
||||
@@ -29,7 +29,11 @@ import {
|
||||
patchSessionModel,
|
||||
readSessions,
|
||||
} from "@/lib/home-cloud-sync";
|
||||
import { findReusableEmptyConsultation } from "@/lib/session-list-filter";
|
||||
import {
|
||||
isListedSidebarSession,
|
||||
isUnsavedEmptyConsultation,
|
||||
replaceUnsavedEmptyConsultations,
|
||||
} from "@/lib/session-list-filter";
|
||||
import { chartSnapshotForSession } from "@/lib/home-profile";
|
||||
import { beginSessionPageLoad, mergeSessionPage } from "@/lib/session-groups";
|
||||
import type { ConsultationEntrypoint } from "@/lib/consultation-entrypoint";
|
||||
@@ -110,7 +114,6 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
setActiveChartId,
|
||||
setActiveSessionId,
|
||||
setBirthTimeConsultationConsent,
|
||||
setCreatingSession,
|
||||
setDraft,
|
||||
setDraftEntrypoint,
|
||||
setDraftTheme,
|
||||
@@ -131,12 +134,21 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
const [sessionFullPrompt, setSessionFullPrompt] = useState<{ question: string; theme: Theme } | null>(null);
|
||||
sessionsRef.current = sessions;
|
||||
const loadMoreInFlight = useRef(false);
|
||||
const pendingCreateById = useRef(new Map<string, { continuedFromSessionId?: string }>());
|
||||
const cloudCreatedIds = useRef(new Set<string>());
|
||||
for (const session of sessions) {
|
||||
if (!isUnsavedEmptyConsultation(session)) cloudCreatedIds.current.add(session.id);
|
||||
}
|
||||
|
||||
const visibleSessions = sortSessions(sessions.filter((session) => (showArchivedSessions ? session.archivedAt : !session.archivedAt)
|
||||
&& (session.sessionType === "birth_time_rectification"
|
||||
|| session.messages.length > 0
|
||||
|| !session.messagesHydrated
|
||||
|| session.id === activeSessionId)));
|
||||
const visibleSessions = sortSessions(sessions.filter((session) => {
|
||||
if (showArchivedSessions) {
|
||||
return Boolean(session.archivedAt)
|
||||
&& (session.sessionType === "birth_time_rectification"
|
||||
|| session.messages.length > 0
|
||||
|| !session.messagesHydrated);
|
||||
}
|
||||
return isListedSidebarSession(session);
|
||||
}));
|
||||
|
||||
function updateSession(sessionId: string, change: (session: ChatSession) => ChatSession) {
|
||||
setSessions((current) => current.map((session) => (session.id === sessionId ? change(session) : session)));
|
||||
@@ -149,6 +161,9 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
) {
|
||||
if (!account) throw new Error("账户尚未加载完成");
|
||||
if (process.env.NODE_ENV === "development" && uiPreview.current) return;
|
||||
if (mode === "create" && cloudCreatedIds.current.has(session.id)) return;
|
||||
const continuedFromSessionId = options?.continuedFromSessionId
|
||||
?? pendingCreateById.current.get(session.id)?.continuedFromSessionId;
|
||||
const values = mode === "create"
|
||||
? {
|
||||
title: session.title,
|
||||
@@ -160,8 +175,8 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
chart_profile_id: session.chartProfileId,
|
||||
chart_profile_name: session.chartProfileName,
|
||||
chart_profile_role: session.chartProfileRole,
|
||||
...(options?.continuedFromSessionId
|
||||
? { continued_from_session_id: options.continuedFromSessionId }
|
||||
...(continuedFromSessionId
|
||||
? { continued_from_session_id: continuedFromSessionId }
|
||||
: {}),
|
||||
}
|
||||
: {
|
||||
@@ -173,6 +188,11 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
chart_profile_role: session.chartProfileRole,
|
||||
};
|
||||
await writeChatSession(session.id, values, mode);
|
||||
if (mode === "create") {
|
||||
pendingCreateById.current.delete(session.id);
|
||||
cloudCreatedIds.current.add(session.id);
|
||||
if (!uiPreview.current) writeSessionUrl(session.id, "push");
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureSessionMessages(sessionId: string) {
|
||||
@@ -305,55 +325,24 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
|
||||
async function startNewChat(options?: { continuedFromSessionId?: string }): Promise<ChatSession | null> {
|
||||
if (!account || !modelCatalog || creatingSession) return null;
|
||||
if (!options?.continuedFromSessionId) {
|
||||
const reusable = findReusableEmptyConsultation(sessions);
|
||||
if (reusable) {
|
||||
setActiveSessionId(reusable.id);
|
||||
if (!uiPreview.current) writeSessionUrl(reusable.id, "push");
|
||||
setDraft("");
|
||||
setDraftTheme(null);
|
||||
setDraftEntrypoint(null);
|
||||
setComposerNotice("");
|
||||
setRequestError(null);
|
||||
return reusable;
|
||||
}
|
||||
}
|
||||
const nextSession = {
|
||||
...createSession(modelCatalog.defaultModelId),
|
||||
...chartSnapshotForSession(activeChartId, chartLibrary, profile),
|
||||
};
|
||||
const previousSessionId = activeSession?.id ?? "";
|
||||
const previousHref = `${window.location.pathname}${window.location.search}`;
|
||||
setCreatingSession(true);
|
||||
setSessions((current) => [nextSession, ...current]);
|
||||
for (const session of sessions) {
|
||||
if (isUnsavedEmptyConsultation(session)) pendingCreateById.current.delete(session.id);
|
||||
}
|
||||
pendingCreateById.current.set(nextSession.id, {
|
||||
continuedFromSessionId: options?.continuedFromSessionId,
|
||||
});
|
||||
setSessions((current) => replaceUnsavedEmptyConsultations(current, nextSession));
|
||||
setActiveSessionId(nextSession.id);
|
||||
if (!uiPreview.current) writeSessionUrl(nextSession.id, "push");
|
||||
setDraft("");
|
||||
setDraftTheme(null);
|
||||
setDraftEntrypoint(null);
|
||||
setComposerNotice("");
|
||||
setRequestError(null);
|
||||
try {
|
||||
await persistSession(
|
||||
nextSession,
|
||||
"create",
|
||||
options?.continuedFromSessionId
|
||||
? { continuedFromSessionId: options.continuedFromSessionId }
|
||||
: undefined,
|
||||
);
|
||||
return nextSession;
|
||||
} catch (caught) {
|
||||
setSessions((current) => current.filter((session) => session.id !== nextSession.id));
|
||||
setActiveSessionId(previousSessionId);
|
||||
if (!uiPreview.current) window.history.replaceState(null, "", previousHref);
|
||||
setRequestError({
|
||||
sessionId: previousSessionId,
|
||||
message: caught instanceof Error ? caught.message : "新对话未能保存到云端。",
|
||||
});
|
||||
return null;
|
||||
} finally {
|
||||
setCreatingSession(false);
|
||||
}
|
||||
return nextSession;
|
||||
}
|
||||
|
||||
function selectSession(sessionId: string) {
|
||||
@@ -418,6 +407,7 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
archived: showArchivedSessions,
|
||||
});
|
||||
const incoming = readSessions(page.sessions, modelCatalog).sessions;
|
||||
for (const session of incoming) cloudCreatedIds.current.add(session.id);
|
||||
setSessions((current) => mergeSessionPage(current, incoming));
|
||||
setSessionsCursor(page.nextCursor);
|
||||
} catch (caught) {
|
||||
@@ -434,6 +424,7 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
try {
|
||||
const page = await fetchSessions(undefined, { archived: nextArchived });
|
||||
const parsed = readSessions(page.sessions, modelCatalog).sessions;
|
||||
for (const session of parsed) cloudCreatedIds.current.add(session.id);
|
||||
const active = sessionsRef.current.find((session) => session.id === activeSessionId);
|
||||
setSessions(active && !parsed.some((session) => session.id === active.id)
|
||||
? mergeHydratedSession(parsed, active)
|
||||
@@ -462,6 +453,7 @@ export function useSessionManagement(params: SessionManagementParams) {
|
||||
if (query.present && requestedId && !listed.some((session) => session.id === requestedId)) {
|
||||
void lookupSessionById(requestedId, modelCatalog).then((looked) => {
|
||||
if (looked.status === "found") {
|
||||
cloudCreatedIds.current.add(looked.session.id);
|
||||
setSessions((current) => mergeHydratedSession(current, looked.session));
|
||||
sessionSelectionSource.current = "history";
|
||||
selectSession(looked.session.id);
|
||||
|
||||
Reference in New Issue
Block a user