@playpilot/tpi 8.31.0 → 8.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.31.0",
3
+ "version": "8.32.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -1,3 +1,4 @@
1
+ import { Sorting } from '$lib/enums/Sorting'
1
2
  import { getLanguage } from '$lib/language'
2
3
  import { getApiToken } from '$lib/token'
3
4
  import type { ParticipantData } from '$lib/types/participant'
@@ -6,8 +7,8 @@ import type { TitleData } from '../types/title'
6
7
  import { api } from './api'
7
8
  import { getRegionBasedOnIp } from './region'
8
9
 
9
- export async function fetchTitlesForParticipant(participant: ParticipantData, { page = 1 }: { page?: number } = {}): Promise<TitleData[]> {
10
- const params = {
10
+ export async function fetchTitlesForParticipant(participant: ParticipantData, { page = 1, sorting = null }: { page?: number, sorting?: (typeof Sorting)[keyof typeof Sorting] | null } = {}): Promise<TitleData[]> {
11
+ const params: Record<string, any> = {
11
12
  language: getLanguage(),
12
13
  region: await getRegionBasedOnIp(),
13
14
  include_count: false,
@@ -15,6 +16,8 @@ export async function fetchTitlesForParticipant(participant: ParticipantData, {
15
16
  page,
16
17
  }
17
18
 
19
+ if (sorting) params.ordering = sorting
20
+
18
21
  const response = await api<{ results: TitleData[] }>(`/titles/browse?api-token=${getApiToken()}&${paramsToString(params)}`)
19
22
 
20
23
  return response.results
@@ -2,4 +2,5 @@ export const Sorting = Object.freeze({
2
2
  Popular: '-popularity',
3
3
  New: '-new',
4
4
  Best: '-best',
5
+ Chronological: '-year',
5
6
  })
@@ -5,28 +5,40 @@
5
5
  import { openModal } from '$lib/modal'
6
6
  import type { ParticipantData } from '$lib/types/participant'
7
7
  import type { TitleData } from '$lib/types/title'
8
+ import { Sorting } from '$lib/enums/Sorting'
8
9
  import { t } from '$lib/localization'
9
10
  import { isRetargetingAllowed } from '$lib/retargeting'
10
11
  import { trackViaPixel } from '@playpilot/retargeting-tracking'
11
12
  import { MetaEvent } from '$lib/enums/TrackingEvent'
12
13
  import ParticipantImage from './ParticipantImage.svelte'
13
14
  import ListTitle from '../ListTitle.svelte'
15
+ import Dropdown from '../Explore/Filter/Dropdown.svelte'
16
+ import Button from '../Button.svelte'
14
17
 
15
18
  interface Props {
16
19
  participant: ParticipantData
17
20
  small?: boolean
18
21
  }
19
22
 
23
+ type TitleSorting = { label: string; value: (typeof Sorting)[keyof typeof Sorting] }
24
+
20
25
  const { participant, small = false }: Props = $props()
21
26
 
22
27
  const { name, birth_date, death_date } = $derived(participant)
23
28
 
29
+ const sortings: TitleSorting[] = [
30
+ { label: 'Popularity', value: Sorting.Popular },
31
+ { label: 'Top Rated', value: Sorting.Best },
32
+ { label: 'Year', value: Sorting.Chronological },
33
+ ]
34
+
24
35
  const pageSize = 30
25
36
 
26
37
  let titles: TitleData[] = $state([])
27
38
  let page = $state(1)
28
39
  let hasMorePages = $state(true)
29
40
  let loading = $state(true)
41
+ let currentSorting = $state(sortings[0])
30
42
 
31
43
  onMount(loadMore)
32
44
 
@@ -41,7 +53,7 @@
41
53
  loading = true
42
54
 
43
55
  try {
44
- const results = await fetchTitlesForParticipant(participant, { page })
56
+ const results = await fetchTitlesForParticipant(participant, { page, sorting: currentSorting.value })
45
57
 
46
58
  titles = [...titles, ...results]
47
59
  hasMorePages = results?.length >= pageSize
@@ -52,6 +64,14 @@
52
64
  page += 1
53
65
  }
54
66
  }
67
+
68
+ function setSorting(sorting: TitleSorting): void {
69
+ currentSorting = sorting
70
+ titles = []
71
+ page = 1
72
+
73
+ loadMore()
74
+ }
55
75
  </script>
56
76
 
57
77
  <div class="header" class:small>
@@ -69,8 +89,28 @@
69
89
  </div>
70
90
 
71
91
  <div class="content">
72
- {#if !small}
73
- <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
92
+ {#if !small && (titles.length && !loading)}
93
+ <div class="list-header">
94
+ <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
95
+
96
+ <div class="sort">
97
+ {t('Sort By')}:
98
+
99
+ <Dropdown>
100
+ {#snippet button({ toggle })}
101
+ <Button variant="border" onclick={toggle}>{t(currentSorting.label)}</Button>
102
+ {/snippet}
103
+
104
+ {#snippet content()}
105
+ <div class="sortings">
106
+ {#each sortings as sorting}
107
+ <Button variant="link" onclick={() => setSorting(sorting)}>{t(sorting.label)}</Button>
108
+ {/each}
109
+ </div>
110
+ {/snippet}
111
+ </Dropdown>
112
+ </div>
113
+ </div>
74
114
  {/if}
75
115
 
76
116
  <div class="list">
@@ -121,7 +161,7 @@
121
161
  font-style: theme(detail-title-font-style, normal);
122
162
 
123
163
  &.subheading {
124
- margin: 0 0 margin(0.5);
164
+ margin: 0;
125
165
  font-size: theme(detail-title-small-font-size, margin(1.25));
126
166
  }
127
167
 
@@ -155,6 +195,13 @@
155
195
  gap: margin(0.5);
156
196
  }
157
197
 
198
+ .list-header {
199
+ display: flex;
200
+ align-items: flex-start;
201
+ justify-content: space-between;
202
+ margin-bottom: margin(0.5);
203
+ }
204
+
158
205
  .skeleton {
159
206
  min-height: margin(7.25);
160
207
  border-radius: theme(playlink-border-radius, border-radius);
@@ -176,4 +223,26 @@
176
223
  filter: brightness(1.2);
177
224
  }
178
225
  }
226
+
227
+ .sort {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: margin(0.25);
231
+ color: theme(text-color-alt);
232
+ }
233
+
234
+ .sortings {
235
+ :global(.button) {
236
+ display: block;
237
+ width: 100%;
238
+ padding: margin(0.5) margin(1);
239
+ text-align: left;
240
+ white-space: nowrap;
241
+ }
242
+
243
+ :global(.button:hover) {
244
+ background: theme(content-light);
245
+ border-radius: 0;
246
+ }
247
+ }
179
248
  </style>
@@ -23,6 +23,7 @@
23
23
  import ParticipantsRail from '../components/Rails/ParticipantsRail.svelte'
24
24
  import InfiniteTitlesRail from '../components/Rails/InfiniteTitlesRail.svelte'
25
25
  import YouTubeEmbed from '../components/YouTubeEmbed.svelte'
26
+ import Participant from '../components/Participants/Participant.svelte'
26
27
 
27
28
  if (browser) {
28
29
  // @ts-ignore
@@ -137,6 +138,11 @@
137
138
  <h2>Participants</h2>
138
139
 
139
140
  <div class="group">
141
+ <div>
142
+ <h3>Participant.svelte</h3>
143
+ <div class="item"><Participant participant={participants[0]} /></div>
144
+ </div>
145
+
140
146
  <div>
141
147
  <h3>Participants.svelte</h3>
142
148
  <div class="item"><ParticipantsRail {title} /></div>
@@ -5,6 +5,7 @@ import { fetchTitlesForParticipant } from '$lib/api/participants'
5
5
  import { participants, title } from '$lib/fakeData'
6
6
  import { getApiToken } from '$lib/token'
7
7
  import { fakeFetch } from '../../helpers'
8
+ import { Sorting } from '$lib/enums/Sorting'
8
9
 
9
10
  vi.mock('$lib/token', () => ({
10
11
  getApiToken: vi.fn(),
@@ -57,5 +58,21 @@ describe('$lib/api/participants', () => {
57
58
 
58
59
  expect(api).toHaveBeenCalledWith(`/titles/browse?api-token=some-token&language=en-US&region=nl&include_count=false&participant_sid=${participants[0].sid}&page=1`)
59
60
  })
61
+
62
+ it('Should use given sorting', async () => {
63
+ vi.mocked(api).mockResolvedValueOnce({ results: [title] })
64
+
65
+ await fetchTitlesForParticipant(participants[0], { page: 5, sorting: Sorting.New })
66
+
67
+ expect(api).toHaveBeenCalledWith(expect.stringContaining(`ordering=${Sorting.New}`))
68
+ })
69
+
70
+ it('Should not add ordering when not sorting is given', async () => {
71
+ vi.mocked(api).mockResolvedValueOnce({ results: [title] })
72
+
73
+ await fetchTitlesForParticipant(participants[0], { page: 5 })
74
+
75
+ expect(api).toHaveBeenCalledWith(expect.not.stringContaining('ordering'))
76
+ })
60
77
  })
61
78
  })
@@ -4,6 +4,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest'
4
4
  import Participant from '../../../../routes/components/Participants/Participant.svelte'
5
5
  import { participants, title } from '$lib/fakeData'
6
6
  import { fetchTitlesForParticipant } from '$lib/api/participants'
7
+ import { Sorting } from '$lib/enums/Sorting'
7
8
 
8
9
  vi.mock('$lib/tracking', () => ({
9
10
  track: vi.fn(),
@@ -21,7 +22,7 @@ describe('Participant.svelte', () => {
21
22
  it('Should call fetchTitlesForParticipant on mount', () => {
22
23
  render(Participant, { participant: participants[0] })
23
24
 
24
- expect(fetchTitlesForParticipant).toHaveBeenCalledWith(participants[0], { page: 1 })
25
+ expect(fetchTitlesForParticipant).toHaveBeenCalledWith(participants[0], { page: 1, sorting: Sorting.Popular })
25
26
  })
26
27
 
27
28
  it('Should render fetched titles after showing skeletons', async () => {
@@ -71,7 +72,7 @@ describe('Participant.svelte', () => {
71
72
 
72
73
  await fireEvent.click(getByText('Show more'))
73
74
 
74
- expect(fetchTitlesForParticipant).toHaveBeenCalledWith(participants[0], { page: 2 })
75
+ expect(fetchTitlesForParticipant).toHaveBeenCalledWith(participants[0], { page: 2, sorting: Sorting.Popular })
75
76
  })
76
77
 
77
78
  it('Should render as small variant when given', () => {