src/stores/drafts.ts
此代码来自构建时的仓库快照。修改源码后重新构建即可更新。
import { createStore } from 'zustand/vanilla';
import {
createJSONStorage,
persist,
type StateStorage,
} from 'zustand/middleware';
import { ticketPriorities } from '@/shared/ticket';
import { z } from 'zod';
const entrySchema = z.object({
text: z.string().max(10000).optional(),
requestId: z.uuid().optional(),
ticket: z
.object({
title: z.string().max(100),
description: z.string().max(10000),
priority: z.enum(ticketPriorities),
})
.partial()
.optional(),
});
export type Draft = z.infer<typeof entrySchema>;
export type DraftState = {
drafts: Record<string, Draft>;
hydrated: boolean;
save: (key: string, draft: Draft) => void;
remove: (key: string) => void;
};
export const draftStorageKey = (userId: string) => `teamdesk-drafts:${userId}`;
export const draftKey = (teamId: string, ticketId: string) =>
`${teamId}:${ticketId}`;
// 工厂函数让每个登录账号拥有独立 store,避免 SSR 进程中的全局变量串号。
export function createDraftStore(userId: string, storage?: StateStorage) {
return createStore<DraftState>()(
persist(
(set) => ({
drafts: {},
hydrated: false,
save(key, draft) {
set((state) => {
const drafts = { ...state.drafts, [key]: draft };
// 本地草稿只保留最近编辑的 100 项,避免无限占用浏览器存储。
delete drafts[key];
drafts[key] = draft;
while (Object.keys(drafts).length > 100)
delete drafts[Object.keys(drafts)[0]];
return { drafts };
});
},
remove(key) {
set((state) => {
const drafts = { ...state.drafts };
delete drafts[key];
return { drafts };
});
},
}),
{
name: draftStorageKey(userId),
storage: createJSONStorage(() => storage ?? sessionStorage),
partialize: (state) => ({ drafts: state.drafts }),
skipHydration: true,
merge(persisted, current) {
// 浏览器存储也可能被改写;恢复时校验形状,只读取数据,不恢复任何函数。
const parsed = z
.object({ drafts: z.record(z.string(), entrySchema) })
.safeParse(persisted);
return {
...current,
drafts: parsed.success
? Object.fromEntries(
Object.entries(parsed.data.drafts).slice(-100),
)
: {},
};
},
},
),
);
}