110 lines
3.6 KiB
TypeScript
110 lines
3.6 KiB
TypeScript
|
|
// ============================================================
|
||
|
|
// useSessionList — 会话历史列表管理
|
||
|
|
// 职责:会话 CRUD、消息持久化、切换会话
|
||
|
|
// ============================================================
|
||
|
|
|
||
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||
|
|
import { v4 as uuidv4 } from "uuid";
|
||
|
|
import {
|
||
|
|
loadSessionSummaries,
|
||
|
|
saveSessionSummaries,
|
||
|
|
loadSessionMessages,
|
||
|
|
saveSessionMessages,
|
||
|
|
deleteSessionMessages,
|
||
|
|
} from "../lib/storage";
|
||
|
|
import type { ChatMessage, SessionSummary } from "../types";
|
||
|
|
|
||
|
|
/** 截取预览文本 */
|
||
|
|
function getPreview(text: string, maxLen = 50): string {
|
||
|
|
const clean = text.replace(/[\n\r]/g, " ").trim();
|
||
|
|
return clean.length > maxLen ? clean.slice(0, maxLen) + "…" : clean;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useSessionList() {
|
||
|
|
const [sessions, setSessions] = useState<SessionSummary[]>(() => loadSessionSummaries());
|
||
|
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
||
|
|
const sessionsRef = useRef(sessions);
|
||
|
|
useEffect(() => { sessionsRef.current = sessions; }, [sessions]);
|
||
|
|
|
||
|
|
/** 创建新会话 */
|
||
|
|
const createSession = useCallback((): string => {
|
||
|
|
const id = uuidv4();
|
||
|
|
const now = Date.now();
|
||
|
|
const summary: SessionSummary = {
|
||
|
|
id,
|
||
|
|
title: "新对话",
|
||
|
|
createdAt: now,
|
||
|
|
lastActiveAt: now,
|
||
|
|
messageCount: 0,
|
||
|
|
preview: "",
|
||
|
|
};
|
||
|
|
setSessions((prev) => [summary, ...prev]);
|
||
|
|
setActiveSessionId(id);
|
||
|
|
// 持久化
|
||
|
|
const all = [summary, ...sessionsRef.current];
|
||
|
|
saveSessionSummaries(all);
|
||
|
|
return id;
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
/** 删除会话 */
|
||
|
|
const deleteSession = useCallback((id: string) => {
|
||
|
|
setSessions((prev) => prev.filter((s) => s.id !== id));
|
||
|
|
deleteSessionMessages(id);
|
||
|
|
const remaining = sessionsRef.current.filter((s) => s.id !== id);
|
||
|
|
saveSessionSummaries(remaining);
|
||
|
|
// 如果删除的是当前会话,清空 active
|
||
|
|
setActiveSessionId((prev) => (prev === id ? null : prev));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
/** 重命名会话 */
|
||
|
|
const renameSession = useCallback((id: string, title: string) => {
|
||
|
|
setSessions((prev) => prev.map((s) => (s.id === id ? { ...s, title } : s)));
|
||
|
|
const updated = sessionsRef.current.map((s) => (s.id === id ? { ...s, title } : s));
|
||
|
|
saveSessionSummaries(updated);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
/** 保存指定会话的消息并更新摘要 */
|
||
|
|
const persistSession = useCallback((sessionId: string, messages: ChatMessage[]) => {
|
||
|
|
if (!sessionId) return;
|
||
|
|
saveSessionMessages(sessionId, messages);
|
||
|
|
// 更新摘要
|
||
|
|
const firstUserMsg = messages.find((m) => m.role === "user");
|
||
|
|
const title = firstUserMsg ? getPreview(firstUserMsg.content, 20) : "新对话";
|
||
|
|
const lastMsg = messages[messages.length - 1];
|
||
|
|
const summary: Partial<SessionSummary> = {
|
||
|
|
title,
|
||
|
|
messageCount: messages.length,
|
||
|
|
lastActiveAt: lastMsg?.timestamp || Date.now(),
|
||
|
|
preview: lastMsg ? getPreview(lastMsg.content) : "",
|
||
|
|
};
|
||
|
|
setSessions((prev) => {
|
||
|
|
const updated = prev.map((s) => (s.id === sessionId ? { ...s, ...summary } : s));
|
||
|
|
saveSessionSummaries(updated);
|
||
|
|
return updated;
|
||
|
|
});
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
/** 加载指定会话的消息历史 */
|
||
|
|
const loadMessages = useCallback((sessionId: string): ChatMessage[] => {
|
||
|
|
return loadSessionMessages(sessionId);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
/** 选择会话(返回需要加载的消息) */
|
||
|
|
const selectSession = useCallback((id: string): ChatMessage[] => {
|
||
|
|
setActiveSessionId(id);
|
||
|
|
return loadSessionMessages(id);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return {
|
||
|
|
sessions,
|
||
|
|
activeSessionId,
|
||
|
|
setActiveSessionId,
|
||
|
|
createSession,
|
||
|
|
deleteSession,
|
||
|
|
renameSession,
|
||
|
|
persistSession,
|
||
|
|
loadMessages,
|
||
|
|
selectSession,
|
||
|
|
};
|
||
|
|
}
|