143 lines
4.6 KiB
TypeScript
143 lines
4.6 KiB
TypeScript
import { create } from "zustand";
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
import { Profile, GuideListItem, GuideData, SyncResult } from "./types";
|
|
|
|
interface AppState {
|
|
profiles: Profile[];
|
|
activeProfileId: string | null;
|
|
guides: GuideListItem[];
|
|
activeGuideGid: string | null;
|
|
activeGuideData: GuideData | null;
|
|
completedQuests: Set<string>;
|
|
alwaysOnTop: boolean;
|
|
syncing: boolean;
|
|
syncProgress: { current: number; total: number; label: string } | null;
|
|
view: "home" | "guide";
|
|
|
|
loadProfiles: () => Promise<void>;
|
|
setActiveProfile: (id: string) => Promise<void>;
|
|
createProfile: (name: string) => Promise<void>;
|
|
deleteProfile: (id: string) => Promise<void>;
|
|
|
|
loadGuides: () => Promise<void>;
|
|
openGuide: (gid: string) => Promise<void>;
|
|
closeGuide: () => void;
|
|
|
|
toggleQuest: (questName: string) => Promise<void>;
|
|
|
|
syncGuides: () => Promise<SyncResult>;
|
|
syncSingleGuide: (gid: string, name: string) => Promise<void>;
|
|
toggleAlwaysOnTop: () => Promise<void>;
|
|
}
|
|
|
|
export const useStore = create<AppState>((set, get) => ({
|
|
profiles: [],
|
|
activeProfileId: null,
|
|
guides: [],
|
|
activeGuideGid: null,
|
|
activeGuideData: null,
|
|
completedQuests: new Set(),
|
|
alwaysOnTop: true,
|
|
syncing: false,
|
|
syncProgress: null,
|
|
view: "home",
|
|
|
|
loadProfiles: async () => {
|
|
const profiles = await invoke<Profile[]>("get_profiles");
|
|
const saved = await invoke<string | null>("get_setting", { key: "active_profile" });
|
|
const activeId = saved && profiles.find(p => p.id === saved) ? saved : profiles[0]?.id ?? null;
|
|
set({ profiles, activeProfileId: activeId });
|
|
if (activeId) {
|
|
const completed = await invoke<string[]>("get_completed_quests", { profileId: activeId });
|
|
set({ completedQuests: new Set(completed) });
|
|
}
|
|
},
|
|
|
|
setActiveProfile: async (id) => {
|
|
await invoke("set_setting", { key: "active_profile", value: id });
|
|
const completed = await invoke<string[]>("get_completed_quests", { profileId: id });
|
|
set({ activeProfileId: id, completedQuests: new Set(completed) });
|
|
await get().loadGuides();
|
|
},
|
|
|
|
createProfile: async (name) => {
|
|
const profile = await invoke<Profile>("create_profile", { name });
|
|
set(state => ({ profiles: [...state.profiles, profile] }));
|
|
await get().setActiveProfile(profile.id);
|
|
},
|
|
|
|
deleteProfile: async (id) => {
|
|
await invoke("delete_profile", { profileId: id });
|
|
const { profiles, activeProfileId } = get();
|
|
const remaining = profiles.filter(p => p.id !== id);
|
|
const newActive = activeProfileId === id ? remaining[0]?.id ?? null : activeProfileId;
|
|
set({ profiles: remaining, activeProfileId: newActive });
|
|
if (newActive) await get().setActiveProfile(newActive);
|
|
},
|
|
|
|
loadGuides: async () => {
|
|
const { activeProfileId } = get();
|
|
if (!activeProfileId) return;
|
|
const guides = await invoke<GuideListItem[]>("get_guides_list", { profileId: activeProfileId });
|
|
set({ guides });
|
|
},
|
|
|
|
openGuide: async (gid) => {
|
|
const data = await invoke<GuideData>("get_guide", { gid });
|
|
set({ activeGuideGid: gid, activeGuideData: data, view: "guide" });
|
|
},
|
|
|
|
closeGuide: () => {
|
|
set({ activeGuideGid: null, activeGuideData: null, view: "home" });
|
|
},
|
|
|
|
toggleQuest: async (questName) => {
|
|
const { activeProfileId, completedQuests } = get();
|
|
if (!activeProfileId) return;
|
|
const isNowCompleted = await invoke<boolean>("toggle_quest", {
|
|
profileId: activeProfileId,
|
|
questName,
|
|
});
|
|
const next = new Set(completedQuests);
|
|
if (isNowCompleted) next.add(questName);
|
|
else next.delete(questName);
|
|
set({ completedQuests: next });
|
|
|
|
// Refresh guide list progress counts
|
|
await get().loadGuides();
|
|
},
|
|
|
|
syncGuides: async () => {
|
|
const tabs = await invoke<{ gid: string; name: string }[]>("get_tabs_list");
|
|
const total = tabs.length;
|
|
const errors: string[] = [];
|
|
let synced = 0;
|
|
|
|
set({ syncing: true, syncProgress: { current: 0, total, label: "Démarrage…" } });
|
|
|
|
for (const tab of tabs) {
|
|
set({ syncProgress: { current: synced, total, label: tab.name } });
|
|
try {
|
|
await invoke("sync_single_guide", { gid: tab.gid, name: tab.name });
|
|
synced++;
|
|
} catch (e) {
|
|
errors.push(`${tab.name}: ${e}`);
|
|
}
|
|
}
|
|
|
|
set({ syncing: false, syncProgress: null });
|
|
await get().loadGuides();
|
|
return { synced, errors };
|
|
},
|
|
|
|
syncSingleGuide: async (gid, name) => {
|
|
await invoke("sync_single_guide", { gid, name });
|
|
},
|
|
|
|
toggleAlwaysOnTop: async () => {
|
|
const next = !get().alwaysOnTop;
|
|
await invoke("set_always_on_top", { value: next });
|
|
set({ alwaysOnTop: next });
|
|
},
|
|
}));
|