import { create } from 'zustand'; import type { Word, StudyProgress, StudyStats, DifficultyRating } from '../types/vocabulary'; import { calculateNextReview } from '../utils/srs'; import { db, initDatabase, exportData, importData } from '../db/database'; interface AppState { words: Word[]; progress: Map; stats: StudyStats; currentWordIndex: number; isFlipped: boolean; isLoading: boolean; // Actions init: () => Promise; setWords: (words: Word[]) => void; addWords: (words: Word[]) => Promise; flipCard: () => void; rateWord: (rating: DifficultyRating) => Promise; nextWord: () => void; getCurrentWord: () => Word | null; getDueWords: () => Word[]; exportData: () => Promise; importData: (data: any) => Promise; speak: (text: string) => void; } export const useAppStore = create((set, get) => ({ words: [], progress: new Map(), stats: { totalWords: 0, masteredWords: 0, studyingWords: 0, streakDays: 0, todayStudied: 0, todayNewWords: 0, }, currentWordIndex: 0, isFlipped: false, isLoading: true, init: async () => { await initDatabase(); // Load words from DB const wordsFromDB = await db.words.toArray(); // Load progress from DB const progressFromDB = await db.progress.toArray(); const progressMap = new Map(); progressFromDB.forEach(p => progressMap.set(p.wordId, p)); // Load stats from DB const statsFromDB = await db.stats.get('main'); set({ words: wordsFromDB, progress: progressMap, stats: statsFromDB || { totalWords: wordsFromDB.length, masteredWords: 0, studyingWords: 0, streakDays: 0, todayStudied: 0, todayNewWords: 0, }, isLoading: false, }); }, setWords: (words) => set({ words, stats: { ...get().stats, totalWords: words.length } }), addWords: async (newWords) => { const wordsWithDate = newWords.map(w => ({ ...w, addedAt: new Date() })); await db.words.bulkAdd(wordsWithDate); const allWords = await db.words.toArray(); set({ words: allWords, stats: { ...get().stats, totalWords: allWords.length } }); }, flipCard: () => set((state) => ({ isFlipped: !state.isFlipped })), rateWord: async (rating) => { const state = get(); const currentWord = state.getCurrentWord(); if (!currentWord) return; const existingProgress = state.progress.get(currentWord.id); const newProgress = calculateNextReview( existingProgress || { wordId: currentWord.id, interval: 0, repetitions: 0, easeFactor: 2.5, nextReviewDate: new Date(), lastStudiedDate: new Date(), }, rating ); // Save to IndexedDB await db.progress.put(newProgress); const newProgressMap = new Map(state.progress); newProgressMap.set(currentWord.id, newProgress); const newStats = { ...state.stats, todayStudied: state.stats.todayStudied + 1, }; await db.stats.put({ ...newStats, id: 'main' }); set({ progress: newProgressMap, isFlipped: false, stats: newStats, }); setTimeout(() => get().nextWord(), 300); }, nextWord: () => { const state = get(); const dueWords = state.getDueWords(); if (dueWords.length === 0) { set({ currentWordIndex: -1 }); return; } const nextIndex = state.words.findIndex(w => w.id === dueWords[0].id); set({ currentWordIndex: nextIndex, isFlipped: false }); }, getCurrentWord: () => { const state = get(); if (state.currentWordIndex < 0 || state.currentWordIndex >= state.words.length) { return null; } return state.words[state.currentWordIndex]; }, getDueWords: () => { const state = get(); const now = new Date(); return state.words.filter((word) => { const progress = state.progress.get(word.id); if (!progress) return true; return new Date(progress.nextReviewDate) <= now; }); }, exportData: async () => { return await exportData(); }, importData: async (data) => { await importData(data); await get().init(); }, speak: (text: string) => { if ('speechSynthesis' in window) { const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'fr-FR'; utterance.rate = 0.8; window.speechSynthesis.speak(utterance); } }, }));