feat(game): play confetti for finished games

This commit is contained in:
lobo committed 2026-09-28 13:36:36 +02:00
1 parent 09e1f767d8
commit a5458b3f86
9 files changed
+260 -9

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
const FINISHED = ['FINISHED', 'COMPLETED'];
/**
* Every achievement unlocked. Only while the stats are visible: Core can still send the old unlock count after they turn private.
*/
export const allAchievementsUnlocked = (game) => game.hasAchievements && game.statsVisible
&& game.achievementsTotal > 0 && game.achievementsUnlocked >= game.achievementsTotal;
/**
* 0: not finished, 1: finished, 2: finished and every achievement unlocked.
*/
export const celebrationLevel = (game) => {
if (!FINISHED.includes(game.status)) return 0;
return allAchievementsUnlocked(game) ? 2 : 1;
};
/**
* The confetti to play when the page shows this game: on opening a finished game, and when the level rises on the
* same page (e.g. the user sets FINISHED). A reload of the same game plays nothing.
*
* @param before {appid, level} of the game shown before, null on opening the page
* @returns 'small', 'big' or null
*/
export const celebrationFor = (before, game) => {
const level = celebrationLevel(game);
const from = before?.appid === game.appid ? before.level : 0;
if (level <= from) return null;
return level === 2 ? 'big' : 'small';
};
+38
View File
@@ -0,0 +1,38 @@
import {describe, expect, it} from 'vitest';
import {celebrationFor, celebrationLevel} from './confetti.js';
const game = (status, unlocked = 0, total = 0, appid = 620) =>
({appid, status, hasAchievements: total > 0, statsVisible: true, achievementsUnlocked: unlocked, achievementsTotal: total});
describe('celebrationLevel', () => {
it('is 0 for a game not finished, 1 for finished, 2 for finished with every achievement', () => {
expect(celebrationLevel(game('PLAYING', 4, 4))).toBe(0);
expect(celebrationLevel(game('FINISHED'))).toBe(1);
expect(celebrationLevel(game('COMPLETED', 3, 4))).toBe(1);
expect(celebrationLevel(game('FINISHED', 4, 4))).toBe(2);
expect(celebrationLevel(game('COMPLETED', 4, 4))).toBe(2);
});
it('counts no achievements while the stats are hidden', () => {
expect(celebrationLevel({...game('FINISHED', 4, 4), statsVisible: false})).toBe(1);
});
});
describe('celebrationFor', () => {
it('plays on opening a finished game', () => {
expect(celebrationFor(null, game('FINISHED'))).toBe('small');
expect(celebrationFor(null, game('COMPLETED', 4, 4))).toBe('big');
expect(celebrationFor(null, game('BACKLOG'))).toBeNull();
});
it('plays when the status rises on the same page, not when the game loads again', () => {
expect(celebrationFor({appid: 620, level: 0}, game('FINISHED'))).toBe('small');
expect(celebrationFor({appid: 620, level: 1}, game('FINISHED', 4, 4))).toBe('big');
expect(celebrationFor({appid: 620, level: 1}, game('FINISHED'))).toBeNull();
expect(celebrationFor({appid: 620, level: 2}, game('PLAYING'))).toBeNull();
});
it('plays for another game opened from the page', () => {
expect(celebrationFor({appid: 620, level: 1}, game('FINISHED', 0, 0, 220))).toBe('small');
});
});