generated from nhcarrigan/template
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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user