@playpilot/tpi 8.16.0 → 8.17.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.16.0",
3
+ "version": "8.17.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -16,7 +16,7 @@
16
16
  "release": "node release.js"
17
17
  },
18
18
  "devDependencies": {
19
- "@playpilot/retargeting-tracking": "^1.1.0",
19
+ "@playpilot/retargeting-tracking": "^1.2.0",
20
20
  "@sveltejs/adapter-auto": "^7.0.1",
21
21
  "@sveltejs/kit": "^2.56.1",
22
22
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
@@ -1,3 +1,4 @@
1
+ import { SupportedRegion } from '$lib/enums/Region'
1
2
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
2
3
  import { track } from '$lib/tracking'
3
4
 
@@ -26,6 +27,16 @@ export async function getRegionBasedOnIp(timeout = 5000): Promise<string> {
26
27
  }
27
28
  }
28
29
 
30
+ export async function isUserRegionSupported(region: string = ''): Promise<boolean> {
31
+ try {
32
+ region ||= await getRegionBasedOnIp()
33
+
34
+ return !region || Object.values(SupportedRegion).some(supportedRegion => supportedRegion === region)
35
+ } catch {
36
+ return true
37
+ }
38
+ }
39
+
29
40
  function setSavedRegion(region: string): void {
30
41
  if (!window.PlayPilotLinkInjections) return
31
42
 
@@ -4,7 +4,7 @@ import type { APIPaginatedResult } from '$lib/types/api'
4
4
  import { paramsToString } from '$lib/url'
5
5
  import type { TitleData } from '../types/title'
6
6
  import { api } from './api'
7
- import { getRegionBasedOnIp } from './region'
7
+ import { getRegionBasedOnIp, isUserRegionSupported } from './region'
8
8
 
9
9
  export async function fetchTitles(params: Record<string, any> = {}): Promise<APIPaginatedResult<TitleData>> {
10
10
  const apiToken = getApiToken()
@@ -17,7 +17,13 @@ export async function fetchTitles(params: Record<string, any> = {}): Promise<API
17
17
  include_count: 'false',
18
18
  }
19
19
 
20
- if (params.region !== null) params.region = params.region || await getRegionBasedOnIp()
20
+ if (params.region !== null) {
21
+ const region = params.region || await getRegionBasedOnIp()
22
+
23
+ if (!await isUserRegionSupported(region)) return { next: '', previous: '', results: [] }
24
+
25
+ params.region = region
26
+ }
21
27
 
22
28
  return await api<APIPaginatedResult<TitleData>>(`/titles/browse?api-token=${apiToken}&` + paramsToString(params))
23
29
  }
@@ -296,6 +296,11 @@ export const translations = {
296
296
  [Language.Swedish]: 'Nämnda i den här artikeln',
297
297
  [Language.Danish]: 'Nævnt i denne artikel',
298
298
  },
299
+ 'Sorry Region Not Supported': {
300
+ [Language.English]: 'Sorry, your region is not supported',
301
+ [Language.Swedish]: 'Tyvärr stöds inte din region',
302
+ [Language.Danish]: 'Beklager, din region understøttes ikke',
303
+ },
299
304
 
300
305
  // List titles
301
306
  'List: Trending': {
@@ -1,5 +1,5 @@
1
- export const Language = Object.freeze({
1
+ export const Language = {
2
2
  English: 'en-US',
3
3
  Swedish: 'sv-SE',
4
4
  Danish: 'da-DK',
5
- })
5
+ } as const
@@ -0,0 +1,30 @@
1
+ export const SupportedRegion = {
2
+ Argentina: 'ar',
3
+ Australia: 'au',
4
+ Austria: 'at',
5
+ Belgium: 'be',
6
+ Brazil: 'br',
7
+ Canada: 'ca',
8
+ Chile: 'cl',
9
+ Colombia: 'co',
10
+ Denmark: 'dk',
11
+ Finland: 'fi',
12
+ France: 'fr',
13
+ Germany: 'de',
14
+ India: 'in',
15
+ Ireland: 'ie',
16
+ Italy: 'it',
17
+ Mexico: 'mx',
18
+ Netherlands: 'nl',
19
+ Norway: 'no',
20
+ Peru: 'pe',
21
+ Poland: 'pl',
22
+ Portugal: 'pt',
23
+ Russia: 'ru',
24
+ Singapore: 'sg',
25
+ Spain: 'es',
26
+ Sweden: 'se',
27
+ Switzerland: 'ch',
28
+ UK: 'uk',
29
+ USA: 'us',
30
+ } as const
@@ -6,6 +6,7 @@
6
6
  import { track } from '$lib/tracking'
7
7
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
8
8
  import { fetchAds } from '$lib/api/ads'
9
+ import { isUserRegionSupported } from '$lib/api/region'
9
10
  import { onMount, type Snippet } from 'svelte'
10
11
  import Search from './Filter/Search.svelte'
11
12
  import Filter from './Filter/Filter.svelte'
@@ -79,6 +80,14 @@
79
80
  </div>
80
81
  {/if}
81
82
 
83
+ {#await isUserRegionSupported() then isSupported}
84
+ {#if !isSupported}
85
+ <div class="unsupported-region">
86
+ {t('Sorry Region Not Supported')}
87
+ </div>
88
+ {/if}
89
+ {/await}
90
+
82
91
  {@render children()}
83
92
  </div>
84
93
 
@@ -165,4 +174,12 @@
165
174
  width: 100%;
166
175
  margin: margin(0.5) 0 margin(1.5);
167
176
  }
177
+
178
+ .unsupported-region {
179
+ padding: margin(0.5) margin(1);
180
+ margin: margin(0.5) 0;
181
+ border: 1px solid theme(warning);
182
+ background: theme(warning-dark);
183
+ border-radius: theme(border-radius);
184
+ }
168
185
  </style>
@@ -1,6 +1,6 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest'
2
2
 
3
- import { getRegionBasedOnIp } from '$lib/api/region'
3
+ import { getRegionBasedOnIp, isUserRegionSupported } from '$lib/api/region'
4
4
  import { fakeFetch } from '../../helpers'
5
5
  import { track } from '$lib/tracking'
6
6
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
@@ -9,7 +9,7 @@ vi.mock('$lib/tracking', () => ({
9
9
  track: vi.fn(),
10
10
  }))
11
11
 
12
- describe('getRegionBasedOnIp', () => {
12
+ describe('region.ts', () => {
13
13
  beforeEach(() => {
14
14
  vi.resetAllMocks()
15
15
  fakeFetch()
@@ -18,79 +18,109 @@ describe('getRegionBasedOnIp', () => {
18
18
  window.PlayPilotLinkInjections = {}
19
19
  })
20
20
 
21
- it('Should return saved region if present', async () => {
22
- // @ts-ignore
23
- window.PlayPilotLinkInjections = {
24
- region: 'se',
25
- }
21
+ describe('getRegionBasedOnIp', () => {
22
+ it('Should return saved region if present', async () => {
23
+ // @ts-ignore
24
+ window.PlayPilotLinkInjections = {
25
+ region: 'se',
26
+ }
26
27
 
27
- expect(await getRegionBasedOnIp()).toBe('se')
28
- expect(global.fetch).not.toHaveBeenCalled()
29
- })
28
+ expect(await getRegionBasedOnIp()).toBe('se')
29
+ expect(global.fetch).not.toHaveBeenCalled()
30
+ })
30
31
 
31
- it('Should fetch region and return country code as lowercase', async () => {
32
- fakeFetch({ response: { ip: '127.0.0.1', country: 'SE' }})
32
+ it('Should fetch region and return country code as lowercase', async () => {
33
+ fakeFetch({ response: { ip: '127.0.0.1', country: 'SE' }})
33
34
 
34
- // @ts-ignore
35
- window.PlayPilotLinkInjections = {}
35
+ // @ts-ignore
36
+ window.PlayPilotLinkInjections = {}
36
37
 
37
- const result = await getRegionBasedOnIp()
38
+ const result = await getRegionBasedOnIp()
38
39
 
39
- expect(global.fetch).toHaveBeenCalledWith('https://api.country.is/', expect.any(Object))
40
- expect(result).toBe('se')
41
- expect(window.PlayPilotLinkInjections.region).toBe('se')
42
- })
40
+ expect(global.fetch).toHaveBeenCalledWith('https://api.country.is/', expect.any(Object))
41
+ expect(result).toBe('se')
42
+ expect(window.PlayPilotLinkInjections.region).toBe('se')
43
+ })
43
44
 
44
- it('Should return empty string if API returns no country, saved region was empty, and no fallback region is given', async () => {
45
- fakeFetch({ response: { ip: '127.0.0.1' }})
45
+ it('Should return empty string if API returns no country, saved region was empty, and no fallback region is given', async () => {
46
+ fakeFetch({ response: { ip: '127.0.0.1' }})
46
47
 
47
- // @ts-ignore
48
- window.PlayPilotLinkInjections = {}
48
+ // @ts-ignore
49
+ window.PlayPilotLinkInjections = {}
49
50
 
50
- expect(await getRegionBasedOnIp()).toBe('')
51
- })
51
+ expect(await getRegionBasedOnIp()).toBe('')
52
+ })
52
53
 
53
- it('Should return fallback region if fetch throws', async () => {
54
- fakeFetch({ ok: false })
54
+ it('Should return fallback region if fetch throws', async () => {
55
+ fakeFetch({ ok: false })
55
56
 
56
- // @ts-ignore
57
- window.PlayPilotLinkInjections = {
58
- config: { fallback_region: 'dk' },
59
- }
57
+ // @ts-ignore
58
+ window.PlayPilotLinkInjections = {
59
+ config: { fallback_region: 'dk' },
60
+ }
60
61
 
61
- expect(await getRegionBasedOnIp()).toBe('dk')
62
- expect(global.fetch).toHaveBeenCalled()
63
- expect(track).toHaveBeenCalledWith(TrackingEvent.RegionRequestFailed, null, { message: expect.any(String) })
64
- })
62
+ expect(await getRegionBasedOnIp()).toBe('dk')
63
+ expect(global.fetch).toHaveBeenCalled()
64
+ expect(track).toHaveBeenCalledWith(TrackingEvent.RegionRequestFailed, null, { message: expect.any(String) })
65
+ })
65
66
 
66
- it('Should not throw if PlayPilotLinkInjections is missing when saving region', async () => {
67
- fakeFetch({ response: { ip: '127.0.0.1', country: 'NO' } })
67
+ it('Should not throw if PlayPilotLinkInjections is missing when saving region', async () => {
68
+ fakeFetch({ response: { ip: '127.0.0.1', country: 'NO' } })
68
69
 
69
- expect(await getRegionBasedOnIp()).toBe('no')
70
- })
70
+ expect(await getRegionBasedOnIp()).toBe('no')
71
+ })
71
72
 
72
- it('Should use the fallback region if api request takes longer than the given timeout', async () => {
73
- vi.useFakeTimers()
73
+ it('Should use the fallback region if api request takes longer than the given timeout', async () => {
74
+ vi.useFakeTimers()
74
75
 
75
- // @ts-ignore
76
- window.PlayPilotLinkInjections = {
77
- config: { fallback_region: 'dk' },
78
- }
76
+ // @ts-ignore
77
+ window.PlayPilotLinkInjections = {
78
+ config: { fallback_region: 'dk' },
79
+ }
80
+
81
+ fakeFetch({ response: { ip: '127.0.0.1', country: 'NO' }, responseTime: 1000 })
79
82
 
80
- fakeFetch({ response: { ip: '127.0.0.1', country: 'NO' }, responseTime: 1000 })
83
+ const result = await getRegionBasedOnIp()
81
84
 
82
- const result = await getRegionBasedOnIp()
85
+ vi.advanceTimersByTime(2000)
83
86
 
84
- vi.advanceTimersByTime(2000)
87
+ expect(result).toBe('dk')
85
88
 
86
- expect(result).toBe('dk')
89
+ vi.useRealTimers()
90
+ })
87
91
 
88
- vi.useRealTimers()
92
+ it('Should use uk region when GB is returned by api endpoint', async () => {
93
+ fakeFetch({ response: { ip: '127.0.0.1', country: 'GB' } })
94
+
95
+ expect(await getRegionBasedOnIp()).toBe('uk')
96
+ })
89
97
  })
90
98
 
91
- it('Should use uk region when GB is returned by api endpoint', async () => {
92
- fakeFetch({ response: { ip: '127.0.0.1', country: 'GB' } })
99
+ describe('isUserRegionSupported', () => {
100
+ it('Should return false if region is not supported', async () => {
101
+ // @ts-ignore
102
+ window.PlayPilotLinkInjections.region = 'not'
103
+
104
+ expect(await isUserRegionSupported()).toBe(false)
105
+ })
106
+
107
+ it('Should return true if region is supported', async () => {
108
+ // @ts-ignore
109
+ window.PlayPilotLinkInjections.region = 'se'
110
+
111
+ expect(await isUserRegionSupported()).toBe(true)
112
+ })
113
+
114
+ it('Should return true if no region was returned', async () => {
115
+ fakeFetch({ response: { ip: '127.0.0.1', country: '' } })
116
+
117
+ expect(await isUserRegionSupported()).toBe(true)
118
+ })
119
+
120
+ it('Should return true if region request failed', async () => {
121
+ fakeFetch({ ok: false })
93
122
 
94
- expect(await getRegionBasedOnIp()).toBe('uk')
123
+ expect(await isUserRegionSupported()).toBe(true)
124
+ })
95
125
  })
96
126
  })
@@ -5,6 +5,7 @@ import { fetchSimilarTitles, fetchTitleBySid, fetchTitles } from '$lib/api/title
5
5
  import { title } from '$lib/fakeData'
6
6
  import { getApiToken } from '$lib/token'
7
7
  import { fakeFetch } from '../../helpers'
8
+ import { getRegionBasedOnIp, isUserRegionSupported } from '$lib/api/region'
8
9
 
9
10
  vi.mock('$lib/token', () => ({
10
11
  getApiToken: vi.fn(),
@@ -14,11 +15,20 @@ vi.mock('$lib/api/api', () => ({
14
15
  api: vi.fn(),
15
16
  }))
16
17
 
18
+ vi.mock('$lib/api/region', () => ({
19
+ getRegionBasedOnIp: vi.fn(),
20
+ isUserRegionSupported: vi.fn(),
21
+ }))
22
+
17
23
  describe('$lib/api/titles', () => {
18
24
  beforeEach(() => {
19
25
  vi.resetAllMocks()
20
26
  vi.mocked(getApiToken).mockReturnValue('some-token')
27
+ vi.mocked(isUserRegionSupported).mockResolvedValue(true)
28
+
21
29
  fakeFetch()
30
+
31
+ window.PlayPilotLinkInjections.region = ''
22
32
  })
23
33
 
24
34
  describe('fetchTitles', () => {
@@ -38,7 +48,7 @@ describe('$lib/api/titles', () => {
38
48
  })
39
49
 
40
50
  it('Should include region if set', async () => {
41
- window.PlayPilotLinkInjections.region = 'nl'
51
+ vi.mocked(getRegionBasedOnIp).mockResolvedValue('nl')
42
52
 
43
53
  await fetchTitles()
44
54
 
@@ -56,6 +66,22 @@ describe('$lib/api/titles', () => {
56
66
 
57
67
  expect(api).toHaveBeenCalledWith('/titles/browse?api-token=some-token&region=be&language=en-US&include_count=false')
58
68
  })
69
+
70
+ it('Should not fetch and return empty results if region is not supported', async () => {
71
+ vi.mocked(isUserRegionSupported).mockResolvedValue(false)
72
+
73
+ await fetchTitles()
74
+
75
+ expect(api).not.toHaveBeenCalled()
76
+ })
77
+
78
+ it('Should fetch if region is unsupported but region was given as null', async () => {
79
+ vi.mocked(isUserRegionSupported).mockResolvedValue(false)
80
+
81
+ await fetchTitles({ region: null })
82
+
83
+ expect(api).toHaveBeenCalled()
84
+ })
59
85
  })
60
86
 
61
87
  describe('fetchSimilarTitles', () => {
@@ -65,4 +65,26 @@ describe('ExploreLayout.svelte', () => {
65
65
 
66
66
  expect(fetchAds).not.toHaveBeenCalled()
67
67
  })
68
+
69
+ it('Should show message for unsupported regions', async () => {
70
+ // @ts-ignore
71
+ window.PlayPilotLinkInjections.region = 'not'
72
+
73
+ const { getByText } = render(ExploreLayout, { children })
74
+
75
+ await waitFor(() => {
76
+ expect(getByText('Sorry, your region is not supported')).toBeTruthy()
77
+ })
78
+ })
79
+
80
+ it('Should not show message for supported regions', async () => {
81
+ // @ts-ignore
82
+ window.PlayPilotLinkInjections.region = 'se'
83
+
84
+ const { queryByText } = render(ExploreLayout, { children })
85
+
86
+ await new Promise(res => setTimeout(res, 10))
87
+
88
+ expect(queryByText('Sorry, your region is not supported')).not.toBeTruthy()
89
+ })
68
90
  })