feat(assets): 素材回收站与画布历史 - 合并 PR #370 并修复删除一致性

This commit is contained in:
ddCat
2026-09-01 18:50:30 +08:00
21 changed files with 1766 additions and 342 deletions

View File

@@ -0,0 +1,67 @@
import { create } from "zustand";
import { persist, type PersistStorage } from "zustand/middleware";
import { localForageStorageForScope } from "@/lib/localforage-storage";
import type { CanvasProject } from "@/stores/canvas/use-canvas-store";
export type DeletedCanvasHistoryItem = {
id: string;
title: string;
createdAt: string;
updatedAt: string;
deletedAt: string;
nodeCount: number;
coverUrl?: string;
};
type CanvasHistoryStore = {
deletedProjects: DeletedCanvasHistoryItem[];
recordDeletedProjects: (projects: CanvasProject[]) => void;
removeDeletedHistoryItem: (id: string) => void;
clearDeletedHistory: () => void;
};
export const CANVAS_HISTORY_STORE_KEY = "infinite-canvas:deleted_history_store";
const historyStorage: PersistStorage<CanvasHistoryStore> = {
getItem: async (name) => {
const value = await localForageStorageForScope().getItem(name);
return value ? JSON.parse(value) : null;
},
setItem: (name, value) => localForageStorageForScope().setItem(name, JSON.stringify(value)),
removeItem: (name) => localForageStorageForScope().removeItem(name),
};
export const useCanvasHistoryStore = create<CanvasHistoryStore>()(
persist(
(set) => ({
deletedProjects: [],
recordDeletedProjects: (projects) => {
const now = new Date().toISOString();
const newItems: DeletedCanvasHistoryItem[] = projects.map((p) => ({
id: p.id,
title: p.title || "未命名画布",
createdAt: p.createdAt || p.updatedAt || now,
updatedAt: p.updatedAt || now,
deletedAt: now,
nodeCount: p.nodes?.length || 0,
}));
set((state) => {
const existingIds = new Set(newItems.map((item) => item.id));
const filtered = state.deletedProjects.filter((item) => !existingIds.has(item.id));
return {
deletedProjects: [...newItems, ...filtered].slice(0, 200),
};
});
},
removeDeletedHistoryItem: (id) =>
set((state) => ({
deletedProjects: state.deletedProjects.filter((item) => item.id !== id),
})),
clearDeletedHistory: () => set({ deletedProjects: [] }),
}),
{
name: CANVAS_HISTORY_STORE_KEY,
storage: historyStorage,
},
),
);

View File

@@ -22,6 +22,7 @@ export type RuntimeLimits = {
activeTaskLimit: number;
resourceUploadMB: number;
sessionUploadMB: number;
recycleBinRetentionDays?: number;
};
export type FeatureAvailability = {
@@ -66,13 +67,13 @@ type UserStore = {
export const useUserStore = create<UserStore>()((set) => ({
hydrated: false,
user: null,
runtimeLimits: { activeTaskLimit: 5, resourceUploadMB: 50, sessionUploadMB: 32 },
runtimeLimits: { activeTaskLimit: 5, resourceUploadMB: 50, sessionUploadMB: 32, recycleBinRetentionDays: 30 },
drawingEngine: { defaultEngine: DEFAULT_DRAWING_ENGINE },
features: defaultFeatureAvailability,
setUser: (user) => set({ user }),
setRuntimeLimits: (runtimeLimits) => set({ runtimeLimits: runtimeLimits || { activeTaskLimit: 5, resourceUploadMB: 50, sessionUploadMB: 32 } }),
setRuntimeLimits: (runtimeLimits) => set({ runtimeLimits: runtimeLimits || { activeTaskLimit: 5, resourceUploadMB: 50, sessionUploadMB: 32, recycleBinRetentionDays: 30 } }),
setDrawingEngine: (drawingEngine) => set({ drawingEngine: drawingEngine || { defaultEngine: DEFAULT_DRAWING_ENGINE } }),
setFeatures: (features) => set({ features: features ? { ...defaultFeatureAvailability, ...features } : defaultFeatureAvailability }),
setHydrated: (hydrated) => set({ hydrated }),
clearSession: () => set({ user: null, runtimeLimits: { activeTaskLimit: 5, resourceUploadMB: 50, sessionUploadMB: 32 }, drawingEngine: { defaultEngine: DEFAULT_DRAWING_ENGINE }, features: defaultFeatureAvailability }),
clearSession: () => set({ user: null, runtimeLimits: { activeTaskLimit: 5, resourceUploadMB: 50, sessionUploadMB: 32, recycleBinRetentionDays: 30 }, drawingEngine: { defaultEngine: DEFAULT_DRAWING_ENGINE }, features: defaultFeatureAvailability }),
}));