import { EndingOverlay, Image, StudyGuideModal, Video } from '@/components' import { cn, getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils' import { useEffect, useMemo, useRef, useState } from 'react' import { useLocation, useNavigate, useParams } from 'react-router-dom' import { LOBBY_COLUMNS, LOBBY_LAYOUT, LOBBY_TIMELINE } from './lobby.config' import { saveNieerStore, useNieerStore } from '@/store/nieerStore' import { useDialogReducer } from './useDialogReducer' import DialogLayer from './DialogLayer' import { BADGE_CONFIG, getAllPlacesCompleted, getBadgeUnlockedMap, getGuideSceneId, getPlaceCompletionMap, getUnlockVideoSrcForScene, getUnlockedByIdMap, } from './reflectionProgress' import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext' import { setCourseTimeProgress } from '@/lib/utils/api-common' const starName = 'nieer' const NieerReflection = () => { const navigate = useNavigate() const location = useLocation() const fromReflectionSubpage = !!(location.state as { fromReflectionSubpage?: boolean } | null) ?.fromReflectionSubpage const { moduleName } = useParams<{ moduleName: string }>() const debugLobby = new URLSearchParams(window.location.search).get('debugLobby') === '1' const visitedReflectionCursors = useNieerStore((state) => state.visitedReflectionCursors) const markReflectionCursorVisited = useNieerStore((state) => state.markReflectionCursorVisited) const setReflectionWork = useNieerStore((state) => state.setReflectionWork) const hasSeenReflectionGuide = useNieerStore((state) => state.hasSeenReflectionGuide) const markReflectionGuideSeen = useNieerStore((state) => state.markReflectionGuideSeen) const hasSeenBoxHint = useNieerStore((state) => state.hasSeenBoxHint) const markBoxHintSeen = useNieerStore((state) => state.markBoxHintSeen) const [showOpeningVideo, setShowOpeningVideo] = useState(!fromReflectionSubpage) const showGuideModal = !showOpeningVideo && !hasSeenReflectionGuide const showBoxHint = !showOpeningVideo && !showGuideModal && !hasSeenBoxHint // 获取碎片动画触发方法 const { triggerPuzzleAnimation } = usePuzzleAnimation() const { state: dialogState, openScene, enterUnlock, handleShare, close, } = useDialogReducer(markReflectionCursorVisited, setReflectionWork) const placeCompletion = useMemo( () => getPlaceCompletionMap(visitedReflectionCursors), [visitedReflectionCursors] ) const unlockedById = useMemo(() => getUnlockedByIdMap(placeCompletion), [placeCompletion]) const badgeUnlocked = useMemo(() => getBadgeUnlockedMap(placeCompletion), [placeCompletion]) const guideSceneId = useMemo(() => getGuideSceneId(placeCompletion), [placeCompletion]) const allPlacesCompleted = useMemo( () => getAllPlacesCompleted(placeCompletion), [placeCompletion] ) const [endingVisible, setEndingVisible] = useState(false) const [pendingBadgeVideoSrc, setPendingBadgeVideoSrc] = useState() const [playingBadgeVideoSrc, setPlayingBadgeVideoSrc] = useState() const [pendingEndingVisible, setPendingEndingVisible] = useState(false) const prevDialogModeRef = useRef(dialogState.mode) useEffect(() => { if (prevDialogModeRef.current === 'result' && dialogState.mode === 'idle') { if (pendingBadgeVideoSrc) { setPlayingBadgeVideoSrc(pendingBadgeVideoSrc) setPendingBadgeVideoSrc(undefined) } if (allPlacesCompleted) { triggerPuzzleAnimation(14) if (pendingBadgeVideoSrc) { setPendingEndingVisible(true) } else { setEndingVisible(true) } } } prevDialogModeRef.current = dialogState.mode }, [dialogState.mode, allPlacesCompleted, pendingBadgeVideoSrc, triggerPuzzleAnimation]) const handleResultShare = (sceneId: number, payload?: Record) => { const unlockVideoSrc = getUnlockVideoSrcForScene(sceneId, visitedReflectionCursors) setPendingBadgeVideoSrc(unlockVideoSrc) handleShare({ payload }) } const finishBadgeVideo = () => { setPlayingBadgeVideoSrc(undefined) if (pendingEndingVisible) { setPendingEndingVisible(false) setEndingVisible(true) } } if (!moduleName) return null return (
{showOpeningVideo && (
)} navigate(`/${moduleName}/${starName}/home`)} />
琴弦 {LOBBY_COLUMNS.map((scene) => { const isUnlocked = unlockedById[scene.id as keyof typeof unlockedById] const isCompleted = placeCompletion[scene.id as keyof typeof placeCompletion] const iconSrc = isCompleted ? scene.icon : scene.iconBw const isClickable = isUnlocked || scene.id === guideSceneId const showGuideState = !allPlacesCompleted && scene.id === guideSceneId return ( ) })}
{LOBBY_TIMELINE.map((item) => { if (item.type === 'single') { return (
{item.label}
) } return (
{item.withLine && } {item.label} {item.withLine && }
) })}
{BADGE_CONFIG.map((badge) => { const isLit = badgeUnlocked[badge.id as keyof typeof badgeUnlocked] return (
徽章底板 {isLit && ( {`徽章${badge.id}`} )}
) })}
{showBoxHint && ( <>
点击「领取旅行箱」
旅行箱会记录你每段旅行的成果哦!
)} 旅行箱 { markBoxHintSeen() navigate(`/${moduleName}/${starName}/reflection/box`) }} />
{debugLobby && (
)} {playingBadgeVideoSrc && (
)} {`同学你好,欢迎来到“跨越时空·体悟”学习模块! 请登陆“时空回溯・亲历时代”体验仓,下一站:1936年,上海。 请带上你的旅行包,穿越时空解锁6个国歌故事,集齐精神徽章,绘制属于你的国歌精神图谱吧~ 别忘了右下角的旅行箱哦,它会记录你每段旅行的成果!时空门已开,请出发吧!`} { setEndingVisible(false) saveNieerStore() setCourseTimeProgress(14) navigate(`/${moduleName}/${starName}/home`) }} />
) } export default NieerReflection