feat: add Zustand stores with MMKV persistence

Wallpaper store (editor state, saved wallpapers, CRUD) and
settings store (export quality, FPS, haptics, auto depth map).
This commit is contained in:
Mathis Pruvot
2026-05-28 11:49:18 +00:00
parent 1d0b82f31c
commit bf9649d8fd
2 changed files with 165 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
import { create } from "zustand";
import { createMMKV } from "react-native-mmkv";
const storage = createMMKV({ id: "lively-settings" });
interface SettingsState {
exportQuality: "low" | "medium" | "high";
fps: 15 | 24 | 30;
hapticFeedback: boolean;
autoDepthMap: boolean;
setExportQuality: (quality: "low" | "medium" | "high") => void;
setFps: (fps: 15 | 24 | 30) => void;
setHapticFeedback: (enabled: boolean) => void;
setAutoDepthMap: (enabled: boolean) => void;
loadSettings: () => void;
}
export const useSettingsStore = create<SettingsState>((set) => ({
exportQuality: "high",
fps: 24,
hapticFeedback: true,
autoDepthMap: false,
setExportQuality: (quality) => {
storage.set("exportQuality", quality);
set({ exportQuality: quality });
},
setFps: (fps) => {
storage.set("fps", fps);
set({ fps });
},
setHapticFeedback: (enabled) => {
storage.set("hapticFeedback", enabled);
set({ hapticFeedback: enabled });
},
setAutoDepthMap: (enabled) => {
storage.set("autoDepthMap", enabled);
set({ autoDepthMap: enabled });
},
loadSettings: () => {
const quality = storage.getString("exportQuality");
const fps = storage.getNumber("fps");
const haptic = storage.getBoolean("hapticFeedback");
const autoDepth = storage.getBoolean("autoDepthMap");
set({
...(quality && { exportQuality: quality as "low" | "medium" | "high" }),
...(fps && { fps: fps as 15 | 24 | 30 }),
...(haptic !== undefined && { hapticFeedback: haptic }),
...(autoDepth !== undefined && { autoDepthMap: autoDepth }),
});
},
}));

View File

@@ -0,0 +1,110 @@
import { create } from "zustand";
import { createMMKV } from "react-native-mmkv";
import type {
AnimationType,
AnimationUniforms,
WallpaperConfig,
ParticlePreset,
} from "@/types/wallpaper";
const storage = createMMKV({ id: "lively-wallpapers" });
interface WallpaperState {
// Current editing session
sourceUri: string | null;
depthMapUri: string | null;
animation: AnimationType;
uniforms: AnimationUniforms;
particlePreset: ParticlePreset;
// Saved wallpapers
savedWallpapers: WallpaperConfig[];
// Actions
setSourceImage: (uri: string) => void;
setDepthMap: (uri: string | null) => void;
setAnimation: (type: AnimationType) => void;
setUniform: (key: string, value: number) => void;
setParticlePreset: (preset: ParticlePreset) => void;
resetEditor: () => void;
saveWallpaper: () => WallpaperConfig | null;
deleteWallpaper: (id: string) => void;
loadSavedWallpapers: () => void;
}
const DEFAULT_UNIFORMS_VALUES: AnimationUniforms = {
intensity: 0.5,
speed: 0.3,
direction: 0,
scale: 1.0,
};
export const useWallpaperStore = create<WallpaperState>((set, get) => ({
sourceUri: null,
depthMapUri: null,
animation: "ken-burns",
uniforms: { ...DEFAULT_UNIFORMS_VALUES },
particlePreset: "snow",
savedWallpapers: [],
setSourceImage: (uri) => set({ sourceUri: uri }),
setDepthMap: (uri) => set({ depthMapUri: uri }),
setAnimation: (type) => set({ animation: type }),
setUniform: (key, value) =>
set((state) => ({
uniforms: { ...state.uniforms, [key]: value },
})),
setParticlePreset: (preset) => set({ particlePreset: preset }),
resetEditor: () =>
set({
sourceUri: null,
depthMapUri: null,
animation: "ken-burns",
uniforms: { ...DEFAULT_UNIFORMS_VALUES },
particlePreset: "snow",
}),
saveWallpaper: () => {
const { sourceUri, depthMapUri, animation, uniforms, particlePreset } = get();
if (!sourceUri) return null;
const config: WallpaperConfig = {
id: `wp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
sourceUri,
depthMapUri: depthMapUri ?? undefined,
animation,
uniforms: { ...uniforms },
particlePreset: animation === "particles" ? particlePreset : undefined,
fps: 24,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
set((state) => {
const updated = [config, ...state.savedWallpapers];
storage.set("wallpapers", JSON.stringify(updated));
return { savedWallpapers: updated };
});
return config;
},
deleteWallpaper: (id) =>
set((state) => {
const updated = state.savedWallpapers.filter((w) => w.id !== id);
storage.set("wallpapers", JSON.stringify(updated));
return { savedWallpapers: updated };
}),
loadSavedWallpapers: () => {
const raw = storage.getString("wallpapers");
if (raw) {
try {
set({ savedWallpapers: JSON.parse(raw) });
} catch {
storage.remove("wallpapers");
}
}
},
}));