dramaling-vocab-learning/frontend/hooks/learn/useReviewSession.ts

156 lines
4.6 KiB
TypeScript

import { useState, useEffect } from 'react'
import { flashcardsService, type Flashcard } from '@/lib/services/flashcards'
import { getReviewTypesByCEFR } from '@/lib/utils/cefrUtils'
// 擴展的Flashcard接口
interface ExtendedFlashcard extends Omit<Flashcard, 'nextReviewDate'> {
nextReviewDate?: string
currentInterval?: number
isOverdue?: boolean
overdueDays?: number
baseMasteryLevel?: number
lastReviewDate?: string
synonyms?: string[]
exampleImage?: string
}
// 複習模式類型
type ReviewMode = 'flip-memory' | 'vocab-choice' | 'vocab-listening' | 'sentence-listening' | 'sentence-fill' | 'sentence-reorder' | 'sentence-speaking'
// Hook狀態接口
interface ReviewSessionState {
currentCard: ExtendedFlashcard | null
dueCards: ExtendedFlashcard[]
currentCardIndex: number
isLoadingCard: boolean
mode: ReviewMode
isAutoSelecting: boolean
showNoDueCards: boolean
showComplete: boolean
}
// Hook返回接口
interface UseReviewSessionReturn extends ReviewSessionState {
loadDueCards: () => Promise<void>
setCurrentCard: (card: ExtendedFlashcard | null) => void
setCurrentCardIndex: (index: number) => void
setMode: (mode: ReviewMode) => void
setIsAutoSelecting: (selecting: boolean) => void
setShowNoDueCards: (show: boolean) => void
setShowComplete: (show: boolean) => void
nextCard: () => void
previousCard: () => void
restart: () => Promise<void>
}
export const useReviewSession = (): UseReviewSessionReturn => {
// 核心複習狀態
const [currentCard, setCurrentCard] = useState<ExtendedFlashcard | null>(null)
const [dueCards, setDueCards] = useState<ExtendedFlashcard[]>([])
const [currentCardIndex, setCurrentCardIndex] = useState(0)
const [isLoadingCard, setIsLoadingCard] = useState(false)
const [mode, setMode] = useState<ReviewMode>('flip-memory')
const [isAutoSelecting, setIsAutoSelecting] = useState(true)
const [showNoDueCards, setShowNoDueCards] = useState(false)
const [showComplete, setShowComplete] = useState(false)
// 載入到期詞卡
const loadDueCards = async (): Promise<void> => {
try {
setIsLoadingCard(true)
console.log('🔍 開始載入到期詞卡...')
const apiResult = await flashcardsService.getDueFlashcards(50)
console.log('📡 API回應結果:', apiResult)
if (apiResult.success && apiResult.data && apiResult.data.length > 0) {
const cardsToUse = apiResult.data
console.log('✅ 載入後端API數據成功:', cardsToUse.length, '張詞卡')
setDueCards(cardsToUse)
setCurrentCardIndex(0)
setCurrentCard(cardsToUse[0])
// 自動選擇複習模式
const userCEFRLevel = localStorage.getItem('userEnglishLevel') || 'A2'
const wordCEFRLevel = cardsToUse[0].difficultyLevel || 'A2'
const reviewTypes = getReviewTypesByCEFR(userCEFRLevel, wordCEFRLevel)
if (reviewTypes.length > 0) {
const selectedMode = reviewTypes[0] as ReviewMode
setMode(selectedMode)
}
setIsAutoSelecting(false)
setShowNoDueCards(false)
setShowComplete(false)
} else {
console.log('❌ 沒有到期詞卡')
setDueCards([])
setCurrentCard(null)
setShowNoDueCards(true)
setShowComplete(false)
}
} catch (error) {
console.error('💥 載入到期詞卡失敗:', error)
setDueCards([])
setCurrentCard(null)
setShowNoDueCards(true)
} finally {
setIsLoadingCard(false)
}
}
// 下一張詞卡
const nextCard = (): void => {
if (currentCardIndex < dueCards.length - 1) {
const nextIndex = currentCardIndex + 1
setCurrentCardIndex(nextIndex)
setCurrentCard(dueCards[nextIndex])
} else {
setShowComplete(true)
}
}
// 上一張詞卡
const previousCard = (): void => {
if (currentCardIndex > 0) {
const prevIndex = currentCardIndex - 1
setCurrentCardIndex(prevIndex)
setCurrentCard(dueCards[prevIndex])
}
}
// 重新開始
const restart = async (): Promise<void> => {
setCurrentCardIndex(0)
setShowComplete(false)
setShowNoDueCards(false)
await loadDueCards()
}
return {
// 狀態
currentCard,
dueCards,
currentCardIndex,
isLoadingCard,
mode,
isAutoSelecting,
showNoDueCards,
showComplete,
// 操作函數
loadDueCards,
setCurrentCard,
setCurrentCardIndex,
setMode,
setIsAutoSelecting,
setShowNoDueCards,
setShowComplete,
nextCard,
previousCard,
restart
}
}