feat: add first version of TougliGui with same features as on google sheet

This commit is contained in:
2026-04-21 22:02:20 +02:00
parent 79d5c4baaa
commit f571d8bb3f
53 changed files with 12416 additions and 0 deletions

142
src/store.ts Normal file
View File

@ -0,0 +1,142 @@
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 });
},
}));