Public Access
feat(play-next): add FRIENDS row and reason text
Shows games from followed users. Reason displays friend name and finish or playtime status. Supports reroll without mood. Added English and German translations.
This commit is contained in:
1 parent
e041c71cf8
commit
6ae2abee07
5 files changed
+37
-7
No files matched your search
@@ -27,7 +27,7 @@ const BUDGETS = [
|
||||
// games without that data.
|
||||
const MOODS = ['ANY', 'STORY', 'COOP', 'CHILL', 'ACTION', 'SCARY', 'SPEEDRUN'];
|
||||
// The rows in the order Core sends them; RANDOM is the mood row.
|
||||
const ROW_ORDER = ['NEXT_UP', 'SERIES', 'SIMILAR', 'STUDIO', 'LONGEST', 'RANDOM'];
|
||||
const ROW_ORDER = ['NEXT_UP', 'SERIES', 'SIMILAR', 'STUDIO', 'FRIENDS', 'LONGEST', 'RANDOM'];
|
||||
// The backend reads only this many excludes. With a bigger backlog the oldest seen games can come back.
|
||||
const MAX_EXCLUDES = 100;
|
||||
const ROW_CLASS = 'flex gap-4 overflow-x-auto snap-x snap-mandatory pb-2 sm:grid sm:grid-cols-3 sm:overflow-visible';
|
||||
@@ -259,6 +259,11 @@ const reasonText = (reason, t, language) => {
|
||||
if (reason?.source === 'STUDIO') {
|
||||
return t('PLAY_NEXT.REASON.STUDIO', {name: reason.studioName});
|
||||
}
|
||||
if (reason?.source === 'FRIENDS') {
|
||||
return reason.friendFinished
|
||||
? t('PLAY_NEXT.REASON.FRIENDS_FINISHED', {name: reason.friendName})
|
||||
: t('PLAY_NEXT.REASON.FRIENDS_PLAYED', {name: reason.friendName, playtime: formatPlaytime(reason.friendPlaytimeMinutes)});
|
||||
}
|
||||
if (reason?.source === 'LONGEST') {
|
||||
return t('PLAY_NEXT.REASON.LONGEST', {date: formatDate(reason.firstSeenAt, language, {dateStyle: 'medium'})});
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ const ALL = () => [
|
||||
row('SERIES', game(6, {reason: {source: 'SERIES', seriesName: 'Mass Effect'}})),
|
||||
row('SIMILAR', game(3, {reason: {source: 'SIMILAR', anchorAppid: 9, anchorName: 'Celeste', anchorPlaytimeMinutes: 600}})),
|
||||
row('STUDIO', game(8, {reason: {source: 'STUDIO', studioName: 'BioWare'}})),
|
||||
row('FRIENDS',
|
||||
game(11, {reason: {source: 'FRIENDS', friendName: 'Max', friendFinished: true, friendPlaytimeMinutes: null}}),
|
||||
game(12, {reason: {source: 'FRIENDS', friendName: 'Anna', friendFinished: false, friendPlaytimeMinutes: 2400}})),
|
||||
row('LONGEST', game(4, {reason: {source: 'LONGEST', firstSeenAt: '2024-03-12T10:00:00Z'}})),
|
||||
row('RANDOM', game(5, {reason: {source: 'RANDOM'}})),
|
||||
];
|
||||
@@ -78,7 +81,7 @@ describe('PlayNextPage', () => {
|
||||
await screen.findByText('Game 1');
|
||||
expect(screen.getAllByRole('heading', {level: 2}).map((h) => h.textContent)).toEqual([
|
||||
'PLAY_NEXT.CATEGORY.NEXT_UP', 'PLAY_NEXT.CATEGORY.SERIES', 'PLAY_NEXT.CATEGORY.SIMILAR',
|
||||
'PLAY_NEXT.CATEGORY.STUDIO', 'PLAY_NEXT.CATEGORY.LONGEST', 'PLAY_NEXT.CATEGORY.RANDOM']);
|
||||
'PLAY_NEXT.CATEGORY.STUDIO', 'PLAY_NEXT.CATEGORY.FRIENDS', 'PLAY_NEXT.CATEGORY.LONGEST', 'PLAY_NEXT.CATEGORY.RANDOM']);
|
||||
const params = pickerUrls()[0];
|
||||
expect(params.has('count')).toBe(false);
|
||||
expect(params.has('category')).toBe(false);
|
||||
@@ -191,6 +194,20 @@ describe('PlayNextPage', () => {
|
||||
expect(pickerUrls()[2].has('mood')).toBe(false);
|
||||
});
|
||||
|
||||
it('rerolls the friends row with category=FRIENDS and without a mood', async () => {
|
||||
mockRows(ALL(), {FRIENDS: [row('FRIENDS', game(13))]});
|
||||
renderPage();
|
||||
await screen.findByText('Game 1');
|
||||
fireEvent.click(screen.getByRole('button', {name: 'PLAY_NEXT.MOOD.STORY'}));
|
||||
await screen.findByText('Game 5');
|
||||
|
||||
fireEvent.click(rerollOf('PLAY_NEXT.CATEGORY.FRIENDS'));
|
||||
|
||||
await screen.findByText('Game 13');
|
||||
expect(pickerUrls()[2].get('category')).toBe('FRIENDS');
|
||||
expect(pickerUrls()[2].has('mood')).toBe(false);
|
||||
});
|
||||
|
||||
it('starts a row over when it has no unseen game left', async () => {
|
||||
mockBackend((params) => {
|
||||
if (!params.get('category')) return ALL();
|
||||
@@ -244,7 +261,7 @@ describe('PlayNextPage', () => {
|
||||
expect(await screen.findByText('PLAY_NEXT.EMPTY_MOOD')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows a reason line for the series, similar, studio and longest rows only', async () => {
|
||||
it('shows a reason line for the series, similar, studio, friends and longest rows only', async () => {
|
||||
mockRows();
|
||||
|
||||
renderPage();
|
||||
@@ -252,6 +269,8 @@ describe('PlayNextPage', () => {
|
||||
expect(await screen.findByText('PLAY_NEXT.REASON.SIMILAR')).toBeTruthy();
|
||||
expect(screen.getByText('PLAY_NEXT.REASON.SERIES')).toBeTruthy();
|
||||
expect(screen.getByText('PLAY_NEXT.REASON.STUDIO')).toBeTruthy();
|
||||
expect(screen.getByText('PLAY_NEXT.REASON.FRIENDS_FINISHED')).toBeTruthy();
|
||||
expect(screen.getByText('PLAY_NEXT.REASON.FRIENDS_PLAYED')).toBeTruthy();
|
||||
expect(screen.getByText('PLAY_NEXT.REASON.LONGEST')).toBeTruthy();
|
||||
expect(screen.queryByText(/PLAY_NEXT\.REASON\.(RANDOM|NEXT_UP)/)).toBeNull();
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user