Public Access
feat(dashboard): new layout with sync button and top 5 most played
Remove the placeholder tiles and the settings tile, fix the mobile header width. iconUrl/formatPlaytime move to utils/games.js (599 min now shows 10h, not 10.0h), and the sync button logic to the useForceSync hook shared with the library.
This commit is contained in:
1 parent
84e31b23ea
commit
76a9ac3e6f
9 files changed
+216
-128
No files matched your search
@@ -3,6 +3,8 @@ import {Link, useNavigate, useSearchParams} from 'react-router-dom';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import useLibrarySync from '../hooks/useLibrarySync.js';
|
||||
import SyncStatusBadge from '../components/SyncStatusBadge.jsx';
|
||||
import useForceSync from '../hooks/useForceSync.js';
|
||||
import {formatPlaytime, iconUrl} from '../utils/games.js';
|
||||
|
||||
const PAGE_SIZES = [20, 40, 80];
|
||||
const DEFAULT_SIZE = 20;
|
||||
@@ -14,17 +16,6 @@ const SORT_OPTIONS = [
|
||||
{value: 'firstSeenAt,desc', labelKey: 'LIBRARY.SORT_FIRST_SEEN'},
|
||||
];
|
||||
|
||||
const iconUrl = (appid, hash) =>
|
||||
hash
|
||||
? `https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/${appid}/${hash}.jpg`
|
||||
: null;
|
||||
|
||||
const formatPlaytime = (minutes) => {
|
||||
if (!minutes || minutes <= 0) return '0h';
|
||||
const hours = minutes / 60;
|
||||
return hours >= 10 ? `${Math.round(hours)}h` : `${hours.toFixed(1)}h`;
|
||||
};
|
||||
|
||||
export default function GameLibrary() {
|
||||
const {t} = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -109,40 +100,7 @@ export default function GameLibrary() {
|
||||
updateParams({page: target});
|
||||
};
|
||||
|
||||
const [syncBusy, setSyncBusy] = useState(false);
|
||||
const [syncMessage, setSyncMessage] = useState(null);
|
||||
|
||||
const triggerSync = async () => {
|
||||
const token = localStorage.getItem('tbp_jwt');
|
||||
if (!token) {
|
||||
navigate('/login');
|
||||
return;
|
||||
}
|
||||
setSyncBusy(true);
|
||||
setSyncMessage(null);
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.VITE_CORE_API_URL}/api/library/sync`, {
|
||||
method: 'POST',
|
||||
headers: {'Authorization': `Bearer ${token}`},
|
||||
});
|
||||
if (response.status === 429) {
|
||||
const json = await response.json().catch(() => ({}));
|
||||
setSyncMessage(t('LIBRARY.SYNC_COOLDOWN', {seconds: json.retryAfterSeconds ?? 60}));
|
||||
return;
|
||||
}
|
||||
if (!response.ok) {
|
||||
setSyncMessage(t('LIBRARY.SYNC_FAILED'));
|
||||
return;
|
||||
}
|
||||
setSyncMessage(t('LIBRARY.SYNC_STARTED'));
|
||||
setTimeout(load, 4000);
|
||||
} catch (err) {
|
||||
console.error('sync trigger error:', err);
|
||||
setSyncMessage(t('INFO.ERROR.NETWORK_FAILURE'));
|
||||
} finally {
|
||||
setSyncBusy(false);
|
||||
}
|
||||
};
|
||||
const {trigger: triggerSync, busy: syncBusy, message: syncMessage} = useForceSync(load);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 dark:bg-slate-900 p-8">
|
||||
|
||||
Reference in new issue
Block a user