feat: 引入持久化访问状态管理,优化展品访问逻辑
- 在 Luxun 和 Sunzhongshan 的多个组件中引入 useLuxunStore 和 useSunzhongshanStore,替代原有的访问状态管理方式。 - 通过 markExhibitVisited 方法标记展品为已访问,提升状态管理的效率和一致性。 - 从 store 获取已访问展品的状态,确保数据的持久性和准确性。
This commit is contained in:
1 parent
966b47ede3
commit
ac5a2159a1
15 files changed
+392
-173
No files matched your search
@@ -17,6 +17,7 @@ import { SuccessAlert, DragQuizPanel } from "@/components/layout";
|
||||
import { MultipleChoiceQuizPanel } from "@/components/layout/quiz";
|
||||
import { useCursorController } from "@/hooks/useCursorController";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
// starName 固定为 sunzhongshan
|
||||
const starName = "sunzhongshan";
|
||||
@@ -50,8 +51,13 @@ const SunzhongshanPlace1 = () => {
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<
|
||||
1 | 2 | 3 | 4 | 5 | null
|
||||
>(null);
|
||||
// 已浏览展品的 ID 集合
|
||||
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||||
// 使用持久化的访问状态
|
||||
const place = "place1";
|
||||
const { markExhibitVisited, getVisitedExhibits } = useSunzhongshanStore();
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place);
|
||||
const visited = new Set(visitedExhibits);
|
||||
|
||||
// 展品2窗口关闭的对话状态
|
||||
const [guide2CloseVisible, setGuide2CloseVisible] = useState(false);
|
||||
@@ -256,11 +262,7 @@ const SunzhongshanPlace1 = () => {
|
||||
return;
|
||||
}
|
||||
if (currentId !== null) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(currentId);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, currentId);
|
||||
}
|
||||
setActiveExhibitId(null);
|
||||
}}
|
||||
@@ -328,11 +330,7 @@ const SunzhongshanPlace1 = () => {
|
||||
onVisibleChange={(nextVisible) => {
|
||||
setGuide2Visible(nextVisible);
|
||||
if (!nextVisible) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(2);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 2);
|
||||
}
|
||||
}}
|
||||
showMask={false}
|
||||
@@ -386,11 +384,7 @@ const SunzhongshanPlace1 = () => {
|
||||
onVisibleChange={(nextVisible) => {
|
||||
setGuide5Visible(nextVisible);
|
||||
if (!nextVisible) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(5);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 5);
|
||||
}
|
||||
}}
|
||||
showMask={false}
|
||||
@@ -514,11 +508,7 @@ const SunzhongshanPlace1 = () => {
|
||||
onContinue={() => {
|
||||
setSuccessAlert4Visible(false);
|
||||
// setShowVideo4Second(true);
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(4);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 4);
|
||||
}}
|
||||
onClose={() => {
|
||||
setSuccessAlert4Visible(false);
|
||||
@@ -536,8 +526,8 @@ const SunzhongshanPlace1 = () => {
|
||||
showSkipButton
|
||||
onEnded={() => {
|
||||
setShowVideo4Second(false);
|
||||
|
||||
setVisited((prev) => new Set([...prev, 3]));
|
||||
// 视频播放结束后,标记展品3为已访问
|
||||
markExhibitVisited(place, 3);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -645,20 +635,12 @@ const SunzhongshanPlace1 = () => {
|
||||
onContinue={() => {
|
||||
setSuccessAlert1Visible(false);
|
||||
// 修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(1);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 1);
|
||||
}}
|
||||
onClose={() => {
|
||||
setSuccessAlert1Visible(false);
|
||||
// 修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(1);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 1);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useCursorController } from "@/hooks/useCursorController";
|
||||
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
|
||||
@@ -35,7 +36,14 @@ const SunzhongshanPlace2 = () => {
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | 4 | null>(
|
||||
null
|
||||
);
|
||||
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = "place2";
|
||||
const { markExhibitVisited, getVisitedExhibits } = useSunzhongshanStore();
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place);
|
||||
const visited = new Set(visitedExhibits);
|
||||
|
||||
// Cursor 顺序控制:1 -> 2 -> 3 -> 4
|
||||
const { isEnabled } = useCursorController({
|
||||
@@ -186,11 +194,7 @@ const SunzhongshanPlace2 = () => {
|
||||
// 光标4:正常关闭,更新visited状态
|
||||
}
|
||||
if (currentId !== null) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(currentId);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, currentId);
|
||||
}
|
||||
setActiveExhibitId(null);
|
||||
}}
|
||||
@@ -237,11 +241,7 @@ const SunzhongshanPlace2 = () => {
|
||||
onEnded={() => {
|
||||
setShowVideo1(false);
|
||||
// 修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(1);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 1);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -287,11 +287,7 @@ const SunzhongshanPlace2 = () => {
|
||||
onEnded={() => {
|
||||
setShowVideo2(false);
|
||||
// 修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(2);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 2);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { SuccessAlert, DragQuizPanel } from "@/components/layout";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
|
||||
@@ -37,7 +38,14 @@ const SunzhongshanPlace3 = () => {
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | 4 | null>(
|
||||
null
|
||||
);
|
||||
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = "place3";
|
||||
const { markExhibitVisited, getVisitedExhibits } = useSunzhongshanStore();
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place);
|
||||
const visited = new Set(visitedExhibits);
|
||||
|
||||
// Cursor 顺序控制:1 -> 2 -> 3 -> 4
|
||||
const { isEnabled } = useCursorController({
|
||||
@@ -195,11 +203,7 @@ const SunzhongshanPlace3 = () => {
|
||||
}
|
||||
|
||||
if (currentId !== null) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(currentId);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, currentId);
|
||||
}
|
||||
setActiveExhibitId(null);
|
||||
}}
|
||||
@@ -338,22 +342,14 @@ const SunzhongshanPlace3 = () => {
|
||||
<SuccessAlert
|
||||
visible={successAlert2Visible}
|
||||
onContinue={() => {
|
||||
setSuccessAlert2Visible(false);
|
||||
// 修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(2);
|
||||
return next;
|
||||
});
|
||||
setSuccessAlert2Visible(false);
|
||||
// 修改cursor状态
|
||||
markExhibitVisited(place, 2);
|
||||
}}
|
||||
onClose={() => {
|
||||
setSuccessAlert2Visible(false);
|
||||
// 修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(2);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 2);
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { useCursorController } from "@/hooks/useCursorController";
|
||||
import { getBackgroundImage, getStaticImage, getPublicPath } from "@/lib/utils";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
|
||||
@@ -39,7 +40,14 @@ const SunzhongshanPlace4 = () => {
|
||||
const [showInitialDialog, setShowInitialDialog] = useState(true);
|
||||
const [initialDialogStep, setInitialDialogStep] = useState(0);
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | null>(null);
|
||||
const [visited, setVisited] = useState<Set<number>>(new Set());
|
||||
|
||||
// 使用持久化的访问状态
|
||||
const place = "place4";
|
||||
const { markExhibitVisited, getVisitedExhibits } = useSunzhongshanStore();
|
||||
|
||||
// 从 store 获取已访问的展品,转换为 Set 用于兼容现有代码
|
||||
const visitedExhibits = getVisitedExhibits(place);
|
||||
const visited = new Set(visitedExhibits);
|
||||
|
||||
// Cursor 顺序控制:1 -> 2
|
||||
const { isEnabled } = useCursorController({
|
||||
@@ -143,11 +151,7 @@ const SunzhongshanPlace4 = () => {
|
||||
}
|
||||
|
||||
if (currentId !== null) {
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(currentId);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, currentId);
|
||||
}
|
||||
setActiveExhibitId(null);
|
||||
}}
|
||||
@@ -177,11 +181,7 @@ const SunzhongshanPlace4 = () => {
|
||||
onContinue={() => {
|
||||
setShowVideo1Guide(false);
|
||||
// 学习引导关闭后,修改cursor状态
|
||||
setVisited((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(1);
|
||||
return next;
|
||||
});
|
||||
markExhibitVisited(place, 1);
|
||||
}}
|
||||
animationSpeed={200}
|
||||
audioSrc={getPublicPath("audio/sunzhongshan/mp3_sunzhongshan_46.mp3")}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Image, StudyGuideModal } from "@/components";
|
||||
import { getBackgroundImage, getPublicPath, getStaticImage } from "@/lib/utils";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
const GUIDE_STORAGE_KEY = `${starName}-reflection-guide-shown`;
|
||||
@@ -24,17 +25,19 @@ const SunzhongshanReflection = () => {
|
||||
scene3: false,
|
||||
});
|
||||
|
||||
const { getReflectionFlag, setReflectionFlag } = useSunzhongshanStore();
|
||||
|
||||
// 检查场景完成状态 - 每次组件挂载或从场景页面返回时重新检查
|
||||
const checkSceneCompleted = () => {
|
||||
const scene1Completed =
|
||||
localStorage.getItem(`${starName}-reflection-scene1-completed`) ===
|
||||
"true";
|
||||
const scene2Completed =
|
||||
localStorage.getItem(`${starName}-reflection-scene2-completed`) ===
|
||||
"true";
|
||||
const scene3Completed =
|
||||
localStorage.getItem(`${starName}-reflection-scene3-completed`) ===
|
||||
"true";
|
||||
const scene1Completed = getReflectionFlag(
|
||||
`${starName}-reflection-scene1-completed`
|
||||
);
|
||||
const scene2Completed = getReflectionFlag(
|
||||
`${starName}-reflection-scene2-completed`
|
||||
);
|
||||
const scene3Completed = getReflectionFlag(
|
||||
`${starName}-reflection-scene3-completed`
|
||||
);
|
||||
|
||||
setSceneCompleted({
|
||||
scene1: scene1Completed,
|
||||
@@ -55,15 +58,16 @@ const SunzhongshanReflection = () => {
|
||||
return () => {
|
||||
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// 检查是否是第一次进入,显示学习引导
|
||||
useEffect(() => {
|
||||
const hasShownGuide = localStorage.getItem(GUIDE_STORAGE_KEY);
|
||||
const hasShownGuide = getReflectionFlag(GUIDE_STORAGE_KEY);
|
||||
if (!hasShownGuide) {
|
||||
setShowGuide(true);
|
||||
}
|
||||
}, []);
|
||||
}, [getReflectionFlag]);
|
||||
|
||||
// 检查场景是否可解锁
|
||||
const isSceneUnlocked = (sceneNumber: number): boolean => {
|
||||
@@ -82,7 +86,7 @@ const SunzhongshanReflection = () => {
|
||||
|
||||
const handleGuideClose = () => {
|
||||
setShowGuide(false);
|
||||
localStorage.setItem(GUIDE_STORAGE_KEY, "true");
|
||||
setReflectionFlag(GUIDE_STORAGE_KEY, true);
|
||||
};
|
||||
|
||||
const handleStartExplore = () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { getBackgroundImage, getStaticImage, getPublicPath } from "@/lib/utils";
|
||||
import { useClueStore } from "@/store/clueStore";
|
||||
import type { ClueHint } from "@/components/clue";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
const sceneId = `${starName}/reflection/scene1`;
|
||||
@@ -69,9 +70,10 @@ const SunzhongshanReflectionScene1 = () => {
|
||||
const { moduleName } = useParams<{
|
||||
moduleName: string;
|
||||
}>();
|
||||
|
||||
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation();
|
||||
const { getReflectionFlag, setReflectionFlag } = useSunzhongshanStore();
|
||||
|
||||
const [showClueHint, setShowClueHint] = useState(false);
|
||||
const [showClueBook, setShowClueBook] = useState(false);
|
||||
@@ -179,7 +181,7 @@ const SunzhongshanReflectionScene1 = () => {
|
||||
const handleVideoEnd = () => {
|
||||
setShowVideo(false);
|
||||
// 检查是否需要显示学习引导
|
||||
const hasShownGuide = localStorage.getItem(GUIDE_STORAGE_KEY);
|
||||
const hasShownGuide = getReflectionFlag(GUIDE_STORAGE_KEY);
|
||||
if (!hasShownGuide) {
|
||||
setShowGuide(true);
|
||||
}
|
||||
@@ -188,7 +190,7 @@ const SunzhongshanReflectionScene1 = () => {
|
||||
// 处理学习引导关闭
|
||||
const handleGuideClose = () => {
|
||||
setShowGuide(false);
|
||||
localStorage.setItem(GUIDE_STORAGE_KEY, "true");
|
||||
setReflectionFlag(GUIDE_STORAGE_KEY, true);
|
||||
};
|
||||
|
||||
// 检查是否所有线索都已收集
|
||||
@@ -240,7 +242,7 @@ const SunzhongshanReflectionScene1 = () => {
|
||||
const handleCompleteGuideClose = () => {
|
||||
setShowCompleteGuide(false);
|
||||
// 保存场景1完成状态
|
||||
localStorage.setItem(`${starName}-reflection-scene1-completed`, "true");
|
||||
setReflectionFlag(`${starName}-reflection-scene1-completed`, true);
|
||||
if (moduleName && starName) {
|
||||
navigate(`/${moduleName}/${starName}/reflection2`);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { getBackgroundImage, getStaticImage, getPublicPath } from "@/lib/utils";
|
||||
import { useClueStore } from "@/store/clueStore";
|
||||
import type { ClueHint } from "@/components/clue";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
const sceneId = `${starName}/reflection/scene2`;
|
||||
@@ -82,6 +83,7 @@ const SunzhongshanReflectionScene2 = () => {
|
||||
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation();
|
||||
const { getReflectionFlag, setReflectionFlag } = useSunzhongshanStore();
|
||||
|
||||
const [showClueHint, setShowClueHint] = useState(false);
|
||||
const [showClueBook, setShowClueBook] = useState(false);
|
||||
@@ -189,7 +191,7 @@ const SunzhongshanReflectionScene2 = () => {
|
||||
const handleVideoEnd = () => {
|
||||
setShowVideo(false);
|
||||
// 检查是否需要显示学习引导
|
||||
const hasShownGuide = localStorage.getItem(GUIDE_STORAGE_KEY);
|
||||
const hasShownGuide = getReflectionFlag(GUIDE_STORAGE_KEY);
|
||||
if (!hasShownGuide) {
|
||||
setShowGuide(true);
|
||||
}
|
||||
@@ -198,7 +200,7 @@ const SunzhongshanReflectionScene2 = () => {
|
||||
// 处理学习引导关闭
|
||||
const handleGuideClose = () => {
|
||||
setShowGuide(false);
|
||||
localStorage.setItem(GUIDE_STORAGE_KEY, "true");
|
||||
setReflectionFlag(GUIDE_STORAGE_KEY, true);
|
||||
};
|
||||
|
||||
// 检查是否所有线索都已收集
|
||||
@@ -250,7 +252,7 @@ const SunzhongshanReflectionScene2 = () => {
|
||||
const handleCompleteGuideClose = () => {
|
||||
setShowCompleteGuide(false);
|
||||
// 保存场景2完成状态
|
||||
localStorage.setItem(`${starName}-reflection-scene2-completed`, "true");
|
||||
setReflectionFlag(`${starName}-reflection-scene2-completed`, true);
|
||||
if (moduleName && starName) {
|
||||
navigate(`/${moduleName}/${starName}/reflection3`);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { getBackgroundImage, getStaticImage, getPublicPath } from "@/lib/utils";
|
||||
import { useClueStore } from "@/store/clueStore";
|
||||
import type { ClueHint } from "@/components/clue";
|
||||
import { usePuzzleAnimation } from "@/contexts/PuzzleAnimationContext";
|
||||
import { useSunzhongshanStore } from "@/store/sunzhongshanStore";
|
||||
|
||||
const starName = "sunzhongshan";
|
||||
const sceneId = `${starName}/reflection/scene3`;
|
||||
@@ -73,6 +74,7 @@ const SunzhongshanReflectionScene3 = () => {
|
||||
|
||||
// 获取碎片动画触发方法
|
||||
const { triggerPuzzleAnimation } = usePuzzleAnimation();
|
||||
const { getReflectionFlag, setReflectionFlag } = useSunzhongshanStore();
|
||||
|
||||
const [showClueHint, setShowClueHint] = useState(false);
|
||||
const [showClueBook, setShowClueBook] = useState(false);
|
||||
@@ -202,7 +204,7 @@ const SunzhongshanReflectionScene3 = () => {
|
||||
const handleVideoEnd = () => {
|
||||
setShowVideo(false);
|
||||
// 检查是否需要显示学习引导
|
||||
const hasShownGuide = localStorage.getItem(GUIDE_STORAGE_KEY);
|
||||
const hasShownGuide = getReflectionFlag(GUIDE_STORAGE_KEY);
|
||||
if (!hasShownGuide) {
|
||||
setShowGuide(true);
|
||||
}
|
||||
@@ -211,7 +213,7 @@ const SunzhongshanReflectionScene3 = () => {
|
||||
// 处理学习引导关闭
|
||||
const handleGuideClose = () => {
|
||||
setShowGuide(false);
|
||||
localStorage.setItem(GUIDE_STORAGE_KEY, "true");
|
||||
setReflectionFlag(GUIDE_STORAGE_KEY, true);
|
||||
};
|
||||
|
||||
// 检查是否所有线索都已收集
|
||||
@@ -262,7 +264,7 @@ const SunzhongshanReflectionScene3 = () => {
|
||||
const handleCompleteGuideClose = () => {
|
||||
setShowCompleteGuide(false);
|
||||
// 保存场景3完成状态
|
||||
localStorage.setItem(`${starName}-reflection-scene3-completed`, "true");
|
||||
setReflectionFlag(`${starName}-reflection-scene3-completed`, true);
|
||||
if (moduleName && starName) {
|
||||
navigate(`/${moduleName}/${starName}/home`);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user