Files
MailUI4Agents/client/electron/src/stores/sessionStore.ts

169 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { create } from 'zustand';
import type { HumanSession, Mail, RenameProposal, Session, SessionBudget } from '../types';
import * as api from '../api/client';
interface SessionState {
sessions: HumanSession[];
currentSession: Session | null;
currentSessionMails: Mail[];
loading: boolean;
error: string | null;
/** Agent 在正文里提的改名建议;null = 无待处理建议 */
renameProposal: RenameProposal | null;
fetchSessions: () => Promise<void>;
selectSession: (sessionId: string) => Promise<void>;
clearSession: () => void;
/** 重新拉取当前会话的改名建议(收到新邮件时调) */
refreshRenameProposal: () => Promise<void>;
/** 接受建议:改名成功后清掉提示条并刷新会话 */
acceptRename: () => Promise<void>;
/** 驳回建议:服务端记下来,不再反复弹同一个 */
dismissRename: () => Promise<void>;
/** 本任务的往返预算;null = 尚未取到 */
budget: SessionBudget | null;
/** 改本会话预算(对话页里随时调)。reset 把已用次数归零。 */
/** 改本会话预算(对话页里随时调)。reset 把已用次数归零。 */
setBudget: (patch: { max_rounds?: number; reset?: boolean }) => Promise<void>;
/** 改本会话权限档位(对话页里随时调)。人是权限的源头,可以任改三档。 */
setPermissionMode: (mode: string) => Promise<void>;
/** 重新拉取预算(Agent 发信后剩余会变) */
refreshBudget: () => Promise<void>;
/** 归档后若正查看该会话则退出 */
dropSessionIfCurrent: (sessionId: string) => void;
}
export const useSessionStore = create<SessionState>((set, get) => ({
sessions: [],
currentSession: null,
currentSessionMails: [],
renameProposal: null,
budget: null,
loading: false,
error: null,
fetchSessions: async () => {
set({ loading: true, error: null });
try {
const { sessions } = await api.getHumanSessions();
set({ sessions: sessions || [], loading: false });
} catch (err) {
set({ error: err instanceof Error ? err.message : String(err), loading: false });
}
},
selectSession: async sessionId => {
set({ loading: true, error: null, renameProposal: null, budget: null });
try {
const detail = await api.getSessionDetail(sessionId);
set({
currentSession: detail.session,
currentSessionMails: detail.mails || [],
loading: false
});
// 改名建议与预算单独取:拿不到不该让整个会话打不开
get().refreshRenameProposal();
get().refreshBudget();
} catch (err) {
set({ error: err instanceof Error ? err.message : String(err), loading: false });
}
},
refreshRenameProposal: async () => {
const id = get().currentSession?.session_id;
if (!id) return;
try {
const { proposal } = await api.getRenameProposal(id);
// 期间用户可能已切走,别把上一会话的建议贴到新会话上
if (get().currentSession?.session_id === id) {
set({ renameProposal: proposal });
}
} catch {
// 建议是锦上添花,失败静默
}
},
acceptRename: async () => {
const s = get();
const id = s.currentSession?.session_id;
const alias = s.renameProposal?.alias;
if (!id || !alias) return;
try {
await api.updateSessionAlias(id, alias);
set({ renameProposal: null });
// 别名变了,会话详情与列表里的地址都要跟着更新
await get().selectSession(id);
await get().fetchSessions();
} catch (err) {
// 别名被别人占用(409)等情况要让用户看到,不能默默失败
set({ error: err instanceof Error ? err.message : String(err) });
}
},
dismissRename: async () => {
const id = get().currentSession?.session_id;
if (!id) return;
// 先隐藏提示条再发请求:驳回是纯本地意图,等一个往返才消失显得迟钝
set({ renameProposal: null });
try {
await api.dismissRenameProposal(id);
} catch {
// 记不下来最坏的后果是下次打开又弹一次,不值得打扰用户
}
},
refreshBudget: async () => {
const id = get().currentSession?.session_id;
if (!id) return;
try {
const b = await api.getSessionBudget(id);
// 期间用户可能已切走,别把上一会话的预算贴到新会话上
if (get().currentSession?.session_id === id) set({ budget: b });
} catch {
// 预算读不到不影响看邮件,静默
}
},
setBudget: async patch => {
const id = get().currentSession?.session_id;
if (!id) return;
try {
const b = await api.updateSessionBudget(id, patch);
set({ budget: b });
// 列表里也显示预算,改完要跟着刷新
await get().fetchSessions();
} catch (err) {
set({ error: err instanceof Error ? err.message : String(err) });
}
},
setPermissionMode: async mode => {
const id = get().currentSession?.session_id;
if (!id) return;
try {
const res = await api.updateSessionPermission(id, mode);
const cur = get().currentSession;
if (cur && cur.session_id === id) {
set({ currentSession: { ...cur, permission_mode: res.permission_mode, permission_enforcement: res.permission_enforcement } });
}
await get().fetchSessions();
} catch (err) {
set({ error: err instanceof Error ? err.message : String(err) });
}
},
clearSession: () =>
set({ currentSession: null, currentSessionMails: [], renameProposal: null, budget: null }),
dropSessionIfCurrent: sessionId => {
if (get().currentSession?.session_id === sessionId) {
set({ currentSession: null, currentSessionMails: [], renameProposal: null, budget: null });
}
set(state => ({
sessions: state.sessions.filter(s => s.session_id !== sessionId)
}));
}
}));