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

114 lines
3.2 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 { Contact } from '../types';
import * as api from '../api/client';
/** 中间栏的呈现方式:列表(紧凑,答"跟谁在聊")或卡片(答"在聊什么、进展如何" */
export type ContactView = 'list' | 'card';
const VIEW_KEY = 'agentmail.contactView';
/**
* 视图偏好存 localStorage。
*
* 这是纯展示偏好,不值得为它建一张表、加一个 API —— 而每次刷新都退回默认视图
* 会让人反复点同一个按钮。读取时容错localStorage 在隐私模式下可能抛异常。
*/
function loadView(): ContactView {
try {
return localStorage.getItem(VIEW_KEY) === 'card' ? 'card' : 'list';
} catch {
return 'list';
}
}
interface ContactState {
contacts: Contact[];
archivedContacts: Contact[];
showArchived: boolean;
loading: boolean;
error: string | null;
/** 正在等待归档确认的地址 */
pendingArchive: string | null;
/** 中间栏呈现方式(持久化到 localStorage */
view: ContactView;
setView: (v: ContactView) => void;
fetchContacts: () => Promise<void>;
fetchArchived: () => Promise<void>;
toggleArchivedView: () => void;
requestArchive: (address: string) => void;
cancelArchive: () => void;
archive: (contact: Contact) => Promise<void>;
/** SSE session_archived 到达时本地即时移除 */
removeSessionLocally: (sessionId: string) => void;
}
export const useContactStore = create<ContactState>((set, get) => ({
contacts: [],
archivedContacts: [],
showArchived: false,
loading: false,
error: null,
pendingArchive: null,
view: loadView(),
setView: v => {
set({ view: v });
try {
localStorage.setItem(VIEW_KEY, v);
} catch {
/* 存不下只是下次回到默认视图,不该让切换本身失败 */
}
},
fetchContacts: async () => {
set({ loading: true, error: null });
try {
const { contacts } = await api.listContacts(false);
set({ contacts: contacts || [], loading: false });
} catch (err) {
set({ error: err instanceof Error ? err.message : String(err), loading: false });
}
},
fetchArchived: async () => {
try {
const { contacts } = await api.listContacts(true);
set({ archivedContacts: contacts || [] });
} catch (err) {
set({ error: err instanceof Error ? err.message : String(err) });
}
},
toggleArchivedView: () => {
const next = !get().showArchived;
set({ showArchived: next });
if (next) get().fetchArchived();
},
requestArchive: address => set({ pendingArchive: address }),
cancelArchive: () => set({ pendingArchive: null }),
archive: async contact => {
try {
await api.archiveContact({ session_id: contact.session_id });
// 本地即时移除,不等 SSE
set(state => ({
contacts: state.contacts.filter(c => c.session_id !== contact.session_id),
pendingArchive: null
}));
if (get().showArchived) get().fetchArchived();
} catch (err) {
set({
error: err instanceof Error ? err.message : String(err),
pendingArchive: null
});
}
},
removeSessionLocally: sessionId =>
set(state => ({
contacts: state.contacts.filter(c => c.session_id !== sessionId)
}))
}));