Skip to content

Commit 999a15e

Browse files
committed
check version update daily
1 parent 7c29fbb commit 999a15e

4 files changed

Lines changed: 138 additions & 3 deletions

File tree

src/components/AchievementPanel.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { Trophy, X, RotateCcw, Lock, Check, Sparkles } from "lucide-react";
33
import type { GameStats } from "@/hooks/useAchievements";
44
import type { AchievementDef } from "@/data/achievements";
55
import { AchievementModal } from "./AchievementModal";
6+
import { Hint } from "./Hint";
7+
import { useVersionCheck } from "@/hooks/useVersionCheck";
68
import { cn } from "@/lib/utils";
79

810
interface AchievementPanelProps {
@@ -39,6 +41,7 @@ export const AchievementPanel = ({
3941
const [debugClicks, setDebugClicks] = useState(0);
4042
const showDebug = debugClicks >= 5;
4143
const [selectedAchievement, setSelectedAchievement] = useState<{ def: AchievementDef; unlocked: boolean } | null>(null);
44+
const { hasUpdate, latestVersion } = useVersionCheck();
4245

4346
return (
4447
<>
@@ -204,8 +207,12 @@ export const AchievementPanel = ({
204207
onReset();
205208
}
206209
}}
207-
className="mt-4 w-full flex items-center justify-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/30 text-destructive hover:bg-destructive/20 transition-colors shrink-0"
210+
className="mt-4 w-full relative flex items-center justify-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/30 text-destructive hover:bg-destructive/20 transition-colors shrink-0"
208211
>
212+
{hasUpdate && (
213+
<Hint>new version {latestVersion}, click to update</Hint>
214+
)}
215+
209216
<RotateCcw className="w-4 h-4" />
210217
Reset Progress
211218
</button>

src/components/D100Roller.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import { Hint } from "./Hint";
1111
import { useAchievements } from "@/hooks/useAchievements";
1212
import { useRollAnimation } from "@/hooks/useRollAnimation";
1313
import type { AchievementDef } from "@/data/achievements";
14+
import { useVersionCheck } from "@/hooks/useVersionCheck";
1415

1516
export const D100Roller = () => {
1617
const [isFullscreen, setIsFullscreen] = useState(false);
1718
const [achievementPanelOpen, setAchievementPanelOpen] = useState(false);
1819
const [modifiers, setModifiers] = useState<Modifier[]>(DEFAULT_MODIFIERS);
1920
const [skills, setSkills] = useState<Skill[]>(DEFAULT_SKILLS);
2021
const [selectedAchievement, setSelectedAchievement] = useState<AchievementDef | null>(null);
22+
const { clearVersion } = useVersionCheck();
2123

2224
const { stats, recordRoll, resetGame, setTotalRolls, unlockAchievement, unlockedDefs, unlockedDefsSorted, availableDefs, totalAchievementCount, latestUnlockedDef } = useAchievements();
2325

@@ -26,8 +28,9 @@ export const D100Roller = () => {
2628
resetGame();
2729
setModifiers(DEFAULT_MODIFIERS.map(m => ({ ...m, active: false })));
2830
setSkills(DEFAULT_SKILLS.map(s => ({ ...s, active: false })));
31+
clearVersion();
2932
window.location.reload();
30-
}, [resetGame]);
33+
}, [resetGame, clearVersion]);
3134

3235
// Handle skill trigger animations
3336
const handleSkillsTriggered = useCallback((triggeredIds: Set<string>) => {

src/hooks/useAchievements.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const DEFAULT_ACHIEVEMENTS: Achievement[] = ACHIEVEMENT_DEFS.map(def => ({
2222
unlockedAt: undefined,
2323
}));
2424

25-
const STORAGE_KEY = "d100-game-state";
25+
const STORAGE_KEY = "dioo-game-state";
2626

2727
interface StoredState {
2828
achievements: Achievement[];

src/hooks/useVersionCheck.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { useState, useEffect, useRef } from "react";
2+
3+
const VERSION_STORAGE_KEY = "dioo-version";
4+
const LAST_CHECK_KEY = "dioo-version-last-check";
5+
const GITHUB_TAGS_URL = "https://api.github.com/repos/globalworming/dioo/tags";
6+
const CHECK_INTERVAL_MS = 60 * 60 * 1000; // 1 hour interval to check if day changed
7+
8+
interface VersionState {
9+
currentVersion: string | null;
10+
latestVersion: string | null;
11+
hasUpdate: boolean;
12+
loading: boolean;
13+
}
14+
15+
const getDateString = (date: Date) => date.toISOString().split("T")[0];
16+
17+
const shouldCheckToday = () => {
18+
const lastCheck = localStorage.getItem(LAST_CHECK_KEY);
19+
const today = getDateString(new Date());
20+
return lastCheck !== today;
21+
};
22+
23+
export const useVersionCheck = () => {
24+
const [state, setState] = useState<VersionState>({
25+
currentVersion: null,
26+
latestVersion: null,
27+
hasUpdate: false,
28+
loading: true,
29+
});
30+
const intervalRef = useRef<NodeJS.Timeout | null>(null);
31+
32+
useEffect(() => {
33+
const checkVersion = async () => {
34+
// Skip if already checked today
35+
if (!shouldCheckToday()) {
36+
const storedVersion = localStorage.getItem(VERSION_STORAGE_KEY);
37+
setState({
38+
currentVersion: storedVersion,
39+
latestVersion: storedVersion,
40+
hasUpdate: false,
41+
loading: false,
42+
});
43+
return;
44+
}
45+
46+
try {
47+
const storedVersion = localStorage.getItem(VERSION_STORAGE_KEY);
48+
49+
const response = await fetch(GITHUB_TAGS_URL, {
50+
headers: { Accept: "application/vnd.github+json" },
51+
});
52+
53+
if (!response.ok) {
54+
setState(prev => ({ ...prev, loading: false }));
55+
return;
56+
}
57+
58+
const tags = await response.json();
59+
const latestTag = tags[0]?.name ?? null;
60+
61+
if (!latestTag) {
62+
setState(prev => ({ ...prev, loading: false }));
63+
return;
64+
}
65+
66+
// Mark today as checked
67+
localStorage.setItem(LAST_CHECK_KEY, getDateString(new Date()));
68+
69+
// If no stored version, store the latest and don't show update
70+
if (!storedVersion) {
71+
localStorage.setItem(VERSION_STORAGE_KEY, latestTag);
72+
setState({
73+
currentVersion: latestTag,
74+
latestVersion: latestTag,
75+
hasUpdate: false,
76+
loading: false,
77+
});
78+
return;
79+
}
80+
81+
// Compare versions
82+
const hasUpdate = storedVersion !== latestTag;
83+
84+
setState({
85+
currentVersion: storedVersion,
86+
latestVersion: latestTag,
87+
hasUpdate,
88+
loading: false,
89+
});
90+
} catch (error) {
91+
console.error("Failed to check version:", error);
92+
setState(prev => ({ ...prev, loading: false }));
93+
}
94+
};
95+
96+
// Initial check
97+
checkVersion();
98+
99+
// Set up interval to check if day changed
100+
intervalRef.current = setInterval(() => {
101+
if (shouldCheckToday()) {
102+
checkVersion();
103+
}
104+
}, CHECK_INTERVAL_MS);
105+
106+
return () => {
107+
if (intervalRef.current) {
108+
clearInterval(intervalRef.current);
109+
}
110+
};
111+
}, []);
112+
113+
const updateVersion = () => {
114+
if (state.latestVersion) {
115+
localStorage.setItem(VERSION_STORAGE_KEY, state.latestVersion);
116+
window.location.reload();
117+
}
118+
};
119+
120+
const clearVersion = () => {
121+
localStorage.removeItem(VERSION_STORAGE_KEY);
122+
};
123+
124+
return { ...state, updateVersion, clearVersion };
125+
};

0 commit comments

Comments
 (0)