v0.5.2: offline report + tab UI + persist race fix

- New: Offline earnings report dialog (welcome back popup with gain/duration/rate/progress)
  - src/lib/game/offlineReport.ts: module-level store + custom event
  - src/components/game/OfflineReportDialog.tsx: Radix Dialog UI
  - gameStore.init: setPendingOfflineReport before set()
- Fix: useGameLoop waits for persist.hasHydrated() before init
  - Root cause: Zustand persist rehydrate is async (Promise.resolve wrapper)
  - Before fix: init read INITIAL_STATE (lastTick=undefined), skipped offline branch
  - After fix: init waits for rehydrate, correctly reads saved lastTick
- UI: Tab bar redesign with per-tab accent colors + flex-col layout
- Version: v0.5 -> v0.5.2
This commit is contained in:
2026-06-23 17:04:28 +00:00
parent bd28219b65
commit 296c609479
5 changed files with 273 additions and 33 deletions
+16 -1
View File
@@ -66,6 +66,7 @@ import {
type BeaconDailyChallenge,
type BeaconDailyProgress,
} from "@/lib/game/beacon";
import { setPendingOfflineReport } from "@/lib/game/offlineReport";
interface GameActions {
// 生命周期
@@ -229,8 +230,22 @@ export const useGameStore = create<Store>()(
const cap = 8 * 3600;
const secs = Math.min(elapsed, cap);
const gain = s.crystalsPerSec * secs * s.offlineEff;
const crystalsBefore = s.crystals;
const crystalsAfter = Math.min(s.crystalCap, s.crystals + gain);
// 设置离线报告(v0.5.2):通知 UI 弹出"欢迎回来"对话框
setPendingOfflineReport({
elapsedSec: secs,
rawElapsedSec: elapsed,
gain: crystalsAfter - crystalsBefore,
rate: s.crystalsPerSec,
eff: s.offlineEff,
capped: elapsed > cap,
crystalsBefore,
crystalsAfter,
crystalCap: s.crystalCap,
});
set({
crystals: Math.min(s.crystalCap, s.crystals + gain),
crystals: crystalsAfter,
lastTick: now,
activePuzzle,
achievements,