212 lines
6.7 KiB
TypeScript
212 lines
6.7 KiB
TypeScript
import { useState, useRef, useEffect } from 'react'
|
|
import { ConfidenceTestProps, ReviewCardData } from '@/types/review'
|
|
import { useReviewLogic } from '@/hooks/useReviewLogic'
|
|
import {
|
|
CardHeader,
|
|
AudioSection,
|
|
ConfidenceButtons,
|
|
ErrorReportButton
|
|
} from '@/components/review/shared'
|
|
|
|
// 優化後的 FlipMemoryTest 組件
|
|
export const FlipMemoryTestNew: React.FC<ConfidenceTestProps> = ({
|
|
cardData,
|
|
onAnswer,
|
|
onConfidenceSubmit,
|
|
onReportError,
|
|
disabled = false
|
|
}) => {
|
|
// 使用共用邏輯 Hook
|
|
const {
|
|
confidence,
|
|
submitConfidence,
|
|
generateResult
|
|
} = useReviewLogic({
|
|
cardData,
|
|
testType: 'FlipMemoryTest'
|
|
})
|
|
|
|
// 翻卡特定狀態
|
|
const [isFlipped, setIsFlipped] = useState(false)
|
|
const [cardHeight, setCardHeight] = useState<number>(400)
|
|
const frontRef = useRef<HTMLDivElement>(null)
|
|
const backRef = useRef<HTMLDivElement>(null)
|
|
|
|
// 動態計算卡片高度
|
|
useEffect(() => {
|
|
const updateCardHeight = () => {
|
|
if (backRef.current) {
|
|
const backHeight = backRef.current.scrollHeight
|
|
const minHeightByScreen = window.innerWidth <= 480 ? 300 :
|
|
window.innerWidth <= 768 ? 350 : 400
|
|
const finalHeight = Math.max(minHeightByScreen, backHeight)
|
|
setCardHeight(finalHeight)
|
|
}
|
|
}
|
|
|
|
setTimeout(updateCardHeight, 100)
|
|
window.addEventListener('resize', updateCardHeight)
|
|
return () => window.removeEventListener('resize', updateCardHeight)
|
|
}, [cardData])
|
|
|
|
// 處理信心度提交
|
|
const handleConfidenceSubmit = (level: number) => {
|
|
submitConfidence(level as any)
|
|
onConfidenceSubmit(level)
|
|
|
|
// 生成並傳遞答案結果
|
|
const result = generateResult()
|
|
onAnswer(`confidence_${level}`)
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* 翻卡區域 */}
|
|
<div className="relative mb-8">
|
|
<div
|
|
className="relative w-full mx-auto perspective-1000"
|
|
style={{ height: `${cardHeight}px` }}
|
|
>
|
|
<div
|
|
className={`relative w-full h-full transition-transform duration-700 transform-style-preserve-3d cursor-pointer ${
|
|
isFlipped ? 'rotate-y-180' : ''
|
|
}`}
|
|
onClick={() => setIsFlipped(!isFlipped)}
|
|
>
|
|
{/* 正面 - 單字 */}
|
|
<div
|
|
ref={frontRef}
|
|
className={`absolute inset-0 w-full h-full backface-hidden ${
|
|
isFlipped ? 'pointer-events-none' : ''
|
|
}`}
|
|
>
|
|
<div className="bg-gradient-to-br from-blue-500 to-purple-600 rounded-2xl shadow-2xl h-full flex flex-col justify-center items-center text-white p-8">
|
|
<div className="text-center space-y-6">
|
|
<h1 className="text-4xl md:text-6xl font-bold mb-4">
|
|
{cardData.word}
|
|
</h1>
|
|
|
|
<AudioSection
|
|
word={cardData.word}
|
|
pronunciation={cardData.pronunciation}
|
|
className="text-white"
|
|
/>
|
|
|
|
<p className="text-xl opacity-90">
|
|
點擊翻面查看答案
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 背面 - 詳細資訊 */}
|
|
<div
|
|
ref={backRef}
|
|
className={`absolute inset-0 w-full h-full backface-hidden rotate-y-180 ${
|
|
!isFlipped ? 'pointer-events-none' : ''
|
|
}`}
|
|
>
|
|
<div className="bg-white rounded-2xl shadow-2xl h-full border border-gray-200 overflow-y-auto">
|
|
<div className="p-8 space-y-6">
|
|
<CardHeader
|
|
cardData={cardData}
|
|
showTranslation={true}
|
|
className="mb-6"
|
|
/>
|
|
|
|
{/* 例句區域 */}
|
|
<div className="bg-gray-50 rounded-xl p-6">
|
|
<h3 className="font-semibold text-gray-900 mb-3">例句</h3>
|
|
<p className="text-gray-800 leading-relaxed mb-2">
|
|
{cardData.example}
|
|
</p>
|
|
<p className="text-gray-600 text-sm">
|
|
{cardData.exampleTranslation}
|
|
</p>
|
|
</div>
|
|
|
|
{/* 音頻播放 */}
|
|
<AudioSection
|
|
word={cardData.word}
|
|
pronunciation={cardData.pronunciation}
|
|
className="justify-center"
|
|
/>
|
|
|
|
<p className="text-center text-gray-500 text-sm">
|
|
請評估你對這個單字的熟悉程度
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 信心度評估 */}
|
|
{isFlipped && (
|
|
<div className="space-y-6">
|
|
<ConfidenceButtons
|
|
selectedLevel={confidence}
|
|
onSelect={handleConfidenceSubmit}
|
|
disabled={disabled}
|
|
/>
|
|
|
|
<div className="flex justify-center">
|
|
<ErrorReportButton
|
|
onClick={onReportError}
|
|
disabled={disabled}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 翻卡提示 */}
|
|
{!isFlipped && (
|
|
<div className="text-center">
|
|
<p className="text-gray-600 text-sm">
|
|
💡 點擊卡片可以翻面查看詳細資訊
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// 用於向後相容的包裝器 (暫時保留舊介面)
|
|
interface LegacyFlipMemoryTestProps {
|
|
word: string
|
|
definition: string
|
|
example: string
|
|
exampleTranslation: string
|
|
pronunciation?: string
|
|
synonyms?: string[]
|
|
difficultyLevel: string
|
|
onConfidenceSubmit: (level: number) => void
|
|
onReportError: () => void
|
|
disabled?: boolean
|
|
}
|
|
|
|
// 預設匯出使用 Legacy 包裝器以保持向後相容
|
|
export const FlipMemoryTest: React.FC<LegacyFlipMemoryTestProps> = (props) => {
|
|
const cardData: ReviewCardData = {
|
|
id: `temp_${props.word}`,
|
|
word: props.word,
|
|
definition: props.definition,
|
|
example: props.example,
|
|
translation: props.exampleTranslation || '', // 使用 exampleTranslation 作為 translation
|
|
pronunciation: props.pronunciation,
|
|
synonyms: props.synonyms || [],
|
|
difficultyLevel: props.difficultyLevel,
|
|
exampleTranslation: props.exampleTranslation
|
|
}
|
|
|
|
return (
|
|
<FlipMemoryTestNew
|
|
cardData={cardData}
|
|
onAnswer={() => {}}
|
|
onConfidenceSubmit={props.onConfidenceSubmit}
|
|
onReportError={props.onReportError}
|
|
disabled={props.disabled}
|
|
/>
|
|
)
|
|
} |