Files
ttstd-infinite-canvas/web/src/stores/use-creation-preferences-store.ts

97 lines
3.9 KiB
TypeScript

import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import { scopedLocalStorage } from "@/lib/user-scope";
export type CreationModePreference = "text" | "image" | "video";
export type CreationImagePreferences = {
ratio?: string;
quality?: string;
count?: string;
};
export type CreationVideoPreferences = {
ratio?: string;
seconds?: string;
videoQuality?: string;
};
export type CreationComposerPreferences = {
mode?: CreationModePreference;
image?: CreationImagePreferences;
video?: CreationVideoPreferences;
};
type CreationPreferencesStore = {
hydrated: boolean;
preferences: CreationComposerPreferences;
rememberMode: (mode: CreationModePreference) => void;
rememberImageSettings: (settings: CreationImagePreferences) => void;
rememberVideoSettings: (settings: CreationVideoPreferences) => void;
};
export const CREATION_PREFERENCES_STORE_KEY = "open_ai_canvas:creation_preferences";
function nonEmptyString(value: unknown): value is string {
return typeof value === "string" && Boolean(value.trim());
}
function normalizeImagePreferences(value: unknown): CreationImagePreferences | undefined {
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
const raw = value as Record<string, unknown>;
const preferences = {
...(nonEmptyString(raw.ratio) ? { ratio: raw.ratio } : {}),
...(nonEmptyString(raw.quality) ? { quality: raw.quality } : {}),
...(nonEmptyString(raw.count) ? { count: raw.count } : {}),
};
return Object.keys(preferences).length ? preferences : undefined;
}
function normalizeVideoPreferences(value: unknown): CreationVideoPreferences | undefined {
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
const raw = value as Record<string, unknown>;
const preferences = {
...(nonEmptyString(raw.ratio) ? { ratio: raw.ratio } : {}),
...(nonEmptyString(raw.seconds) ? { seconds: raw.seconds } : {}),
...(nonEmptyString(raw.videoQuality) ? { videoQuality: raw.videoQuality } : {}),
};
return Object.keys(preferences).length ? preferences : undefined;
}
export function normalizeCreationComposerPreferences(value: unknown): CreationComposerPreferences {
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
const raw = value as Record<string, unknown>;
const image = normalizeImagePreferences(raw.image);
const video = normalizeVideoPreferences(raw.video);
return {
...(raw.mode === "text" || raw.mode === "image" || raw.mode === "video" ? { mode: raw.mode } : {}),
...(image ? { image } : {}),
...(video ? { video } : {}),
};
}
export const useCreationPreferencesStore = create<CreationPreferencesStore>()(
persist(
(set) => ({
hydrated: false,
preferences: {},
rememberMode: (mode) => set((state) => ({ preferences: { ...state.preferences, mode } })),
rememberImageSettings: (settings) => set((state) => ({ preferences: { ...state.preferences, image: { ...state.preferences.image, ...settings } } })),
rememberVideoSettings: (settings) => set((state) => ({ preferences: { ...state.preferences, video: { ...state.preferences.video, ...settings } } })),
}),
{
name: CREATION_PREFERENCES_STORE_KEY,
storage: createJSONStorage(() => scopedLocalStorage),
partialize: (state) => ({ preferences: state.preferences }),
merge: (persisted, current) => {
const stored = (persisted || {}) as Partial<CreationPreferencesStore>;
return { ...current, preferences: normalizeCreationComposerPreferences(stored.preferences) };
},
onRehydrateStorage: () => () => {
useCreationPreferencesStore.setState({ hydrated: true });
},
},
),
);