Public Access
perf(dashboard): load core data in parallel with /me
The dashboard no longer waits for /me before it calls the library. History loads when games-count says canSync or fails. Refs #40.
This commit is contained in:
1 parent
84b16a2ca8
commit
4b25e7f5fe
3 files changed
+40
-15
No files matched your search
@@ -60,17 +60,16 @@ export default function useDashboardData() {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserData = async () => {
|
||||
const {ok, status, data} = await reloadUser();
|
||||
if (ok) {
|
||||
// Games added by hand need no Steam ID. The history and the achievements (Steam achievements,
|
||||
// PlayStation trophies) also come from a PlayStation link, which games-count reports as canSync.
|
||||
const [counts] = await Promise.all([fetchSteamCount(), fetchTopGames(), fetchPlaying(), fetchUpNext(),
|
||||
fetchSummary(), ...(data.steamId ? [fetchActivity(), fetchAchievements()] : [])]);
|
||||
if (!data.steamId && counts?.canSync) await Promise.all([fetchActivity(), fetchAchievements()]);
|
||||
} else if (status === 0) {
|
||||
setError('INFO.ERROR.NETWORK_FAILURE');
|
||||
} else if (status !== 401) {
|
||||
setError('INFO.ERROR.LOAD_PROFILE');
|
||||
// Core calls do not wait for /me. canSync is true with a Steam ID or a PlayStation link; when games-count
|
||||
// fails there is no answer, so the history is asked anyway.
|
||||
const withHistory = fetchSteamCount().then(async (counts) => {
|
||||
if (counts == null || counts.canSync) await Promise.all([fetchActivity(), fetchAchievements()]);
|
||||
});
|
||||
const [{ok, status}] = await Promise.all([reloadUser(), withHistory, fetchTopGames(), fetchPlaying(),
|
||||
fetchUpNext(), fetchSummary()]);
|
||||
if (!ok) {
|
||||
if (status === 0) setError('INFO.ERROR.NETWORK_FAILURE');
|
||||
else if (status !== 401) setError('INFO.ERROR.LOAD_PROFILE');
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ const dashboardRoutes = (settings) => ({
|
||||
'GET /api/library/steam-profile': json(200, {personaName: 'Lobo', avatarUrl: avatar}),
|
||||
'GET /api/library/activity': json(200, {totalMinutes: 90, days: [{day: '2026-09-23', minutes: 0}, {day: '2026-09-24', minutes: 90}], recentGames: [
|
||||
{appid: 1086940, name: "Baldur's Gate 3", playtimeMinutes: 900, playtime2Weeks: 90, status: 'PLAYING'}]}),
|
||||
'GET /api/library/games-count': json(200, {count: 1, hasSteamId: true}),
|
||||
'GET /api/library/games-count': json(200, {count: 1, hasSteamId: true, canSync: true}),
|
||||
'GET /api/library/backlog': json(200, {games: [{appid: 7, name: 'Hades'}]}),
|
||||
'GET /api/library/achievements/summary': json(200, {unlocked: 24, startedGames: 4, perfectGames: 2,
|
||||
averageCompletion: 71.3, pendingGames: 0, hiddenGames: 0, closest: []}),
|
||||
@@ -81,6 +81,22 @@ describe('Dashboard', () => {
|
||||
});
|
||||
|
||||
const order = (container) => [...container.querySelectorAll('[data-tile]')].map((el) => el.dataset.tile);
|
||||
const asked = (path) => fetch.mock.calls.some(([url]) => url.includes(path));
|
||||
|
||||
it('asks for the library before /me answers', async () => {
|
||||
let answerMe;
|
||||
const meAnswer = new Promise((resolve) => { answerMe = resolve; });
|
||||
const routes = dashboardRoutes();
|
||||
const me = routes['GET /api/users/me'];
|
||||
fetchRoutes({...routes, 'GET /api/users/me': () => meAnswer});
|
||||
const container = renderDashboard().container;
|
||||
|
||||
await waitFor(() => expect(asked('/api/library/games')).toBe(true));
|
||||
expect(asked('/api/library/summary')).toBe(true);
|
||||
|
||||
answerMe(me);
|
||||
await waitFor(() => expect(container.querySelector(`img[src="${avatar}"]`)).not.toBeNull());
|
||||
});
|
||||
|
||||
it('shows today\'s tiles without a saved layout', async () => {
|
||||
const container = mockDashboard();
|
||||
@@ -227,20 +243,28 @@ describe('Dashboard', () => {
|
||||
it('watches the sync the server started for this visit', async () => {
|
||||
fetchRoutes({
|
||||
...dashboardRoutes(),
|
||||
'GET /api/library/games-count': json(200, {count: 1, hasSteamId: true, syncStarted: true}),
|
||||
'GET /api/library/games-count': json(200, {count: 1, hasSteamId: true, canSync: true, syncStarted: true}),
|
||||
});
|
||||
renderDashboard();
|
||||
|
||||
await waitFor(() => expect(watchSync).toHaveBeenCalledTimes(1));
|
||||
});
|
||||
|
||||
it('loads the activity and the achievements of a Steam user when games-count fails', async () => {
|
||||
fetchRoutes({...dashboardRoutes(), 'GET /api/library/games-count': json(500, {})});
|
||||
renderDashboard();
|
||||
|
||||
expect(await screen.findByText(/DASHBOARD.HELLO/)).toBeTruthy();
|
||||
await waitFor(() => expect(asked('/api/library/activity')).toBe(true));
|
||||
await waitFor(() => expect(asked('/api/library/achievements/summary')).toBe(true));
|
||||
});
|
||||
|
||||
describe('of a user with a PlayStation link and no Steam ID', () => {
|
||||
const psnOnlyRoutes = (canSync) => ({
|
||||
...dashboardRoutes(),
|
||||
'GET /api/users/me': json(200, {email: 'a@b.de', displayName: 'Lobo', settings: {}}),
|
||||
'GET /api/library/games-count': json(200, {count: 1, hasSteamId: false, canSync}),
|
||||
});
|
||||
const asked = (path) => fetch.mock.calls.some(([url]) => url.includes(path));
|
||||
|
||||
it('loads the activity, the achievements and the sync result, but no Steam profile', async () => {
|
||||
fetchRoutes(psnOnlyRoutes(true));
|
||||
|
||||
@@ -17,7 +17,9 @@ vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
|
||||
|
||||
// The last column: what the page loads besides GET /api/users/me of the provider.
|
||||
const pages = [
|
||||
['Dashboard', '/', '/', Dashboard, 'INFO.ERROR.LOAD_PROFILE', []],
|
||||
['Dashboard', '/', '/', Dashboard, 'INFO.ERROR.LOAD_PROFILE',
|
||||
['/api/library/games', '/api/library/games-count', '/api/library/backlog', '/api/library/summary',
|
||||
'/api/library/activity', '/api/library/achievements/summary']],
|
||||
['GameDetail', '/games/:appid', '/games/220', GameDetail, 'GAME_DETAIL.ERROR_LOAD', ['/api/library/games/220']],
|
||||
['GameLibrary', '/library', '/library', GameLibrary, 'LIBRARY.ERROR_LOAD',
|
||||
['/api/library/games', '/api/library/games-count', '/api/library/sync-status']],
|
||||
|
||||
Reference in new issue
Block a user