feat: add cloud save timestamp and force sync button

Shows a relative time label (e.g. "☁️ 2m ago") in the resource bar
seeded from the server on load and updated after every auto-save or
manual save. A 💾 button lets players trigger an immediate cloud save
outside the 30-second auto-save cycle, with a  spinner and disabled
state while the request is in-flight.
This commit is contained in:
2026-03-06 18:22:28 -08:00
committed by Naomi Carrigan
parent 285c38255b
commit 24beaf3131
4 changed files with 97 additions and 2 deletions
+4 -1
View File
@@ -27,7 +27,7 @@ const TABS: { id: Tab; label: string }[] = [
];
export const GameLayout = (): React.JSX.Element => {
const { state, isLoading, error, battleResult, dismissBattle } = useGame();
const { state, isLoading, error, battleResult, dismissBattle, lastSavedAt, isSyncing, forceSync } = useGame();
const [activeTab, setActiveTab] = useState<Tab>("adventurers");
const [editingProfile, setEditingProfile] = useState(false);
@@ -58,6 +58,9 @@ export const GameLayout = (): React.JSX.Element => {
prestigeCount={state.prestige.count}
profileUrl={profileUrl}
onEditProfile={() => { setEditingProfile(true); }}
lastSavedAt={lastSavedAt}
isSyncing={isSyncing}
onForceSync={forceSync}
/>
<OfflineModal />
<AchievementToast />
@@ -6,13 +6,29 @@ interface ResourceBarProps {
prestigeCount: number;
profileUrl: string;
onEditProfile: () => void;
lastSavedAt: number | null;
isSyncing: boolean;
onForceSync: () => Promise<void>;
}
const formatRelativeTime = (timestamp: number): string => {
const seconds = Math.floor((Date.now() - timestamp) / 1000);
if (seconds < 10) return "just now";
if (seconds < 60) return `${seconds}s ago`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
return `${hours}h ago`;
};
export const ResourceBar = ({
resources,
prestigeCount,
profileUrl,
onEditProfile,
lastSavedAt,
isSyncing,
onForceSync,
}: ResourceBarProps): React.JSX.Element => (
<header className="resource-bar">
<div className="resource">
@@ -41,6 +57,20 @@ export const ResourceBar = ({
</div>
)}
<div className="profile-buttons">
{lastSavedAt !== null && (
<span className="save-status" title={new Date(lastSavedAt).toLocaleString()}>
{formatRelativeTime(lastSavedAt)}
</span>
)}
<button
className="force-save-button"
disabled={isSyncing}
onClick={onForceSync}
title="Force cloud save"
type="button"
>
{isSyncing ? "⏳" : "💾"}
</button>
<a
className="profile-link-button"
href={profileUrl}