@playpilot/tpi 8.10.2 → 8.11.0-beta.2

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.
Files changed (38) hide show
  1. package/.env +1 -1
  2. package/dist/editorial.mount.js +9 -9
  3. package/dist/link-injections.js +1 -1
  4. package/dist/mount.js +6 -6
  5. package/package.json +1 -1
  6. package/src/lib/data/translations.ts +5 -0
  7. package/src/lib/enums/TrackingEvent.ts +4 -4
  8. package/src/lib/fakeData.ts +32 -44
  9. package/src/lib/injection.ts +9 -10
  10. package/src/lib/modal.ts +1 -3
  11. package/src/lib/popover.ts +12 -13
  12. package/src/lib/types/injection.d.ts +0 -5
  13. package/src/routes/components/Editorial/Editor.svelte +3 -2
  14. package/src/routes/components/Editorial/EditorItem.svelte +9 -23
  15. package/src/routes/components/Editorial/ManualInjection.svelte +0 -1
  16. package/src/routes/components/Explore/ExploreLayout.svelte +9 -3
  17. package/src/routes/components/Explore/Routes/ExploreHome.svelte +6 -4
  18. package/src/routes/components/ListTitle.svelte +6 -27
  19. package/src/routes/components/Modals/RailModal.svelte +1 -1
  20. package/src/routes/components/Participant.svelte +6 -18
  21. package/src/routes/components/Playlinks/Playlinks.svelte +1 -2
  22. package/src/routes/components/Rails/TitlesRail.svelte +19 -4
  23. package/src/routes/components/Title.svelte +2 -2
  24. package/src/routes/components/{InjectionPopover.svelte → TitlePopover.svelte} +9 -26
  25. package/src/routes/elements/+page.svelte +3 -3
  26. package/src/tests/helpers.js +0 -1
  27. package/src/tests/lib/injection.test.js +3 -44
  28. package/src/tests/lib/popover.test.js +7 -7
  29. package/src/tests/routes/components/Editorial/EditorItem.test.js +0 -10
  30. package/src/tests/routes/components/Editorial/ManualInjection.test.js +0 -4
  31. package/src/tests/routes/components/Editorial/PlaylinkTypeSelect.test.js +0 -2
  32. package/src/tests/routes/components/ListTitle.test.js +0 -7
  33. package/src/tests/routes/components/Participant.test.js +0 -7
  34. package/src/tests/routes/components/Playlinks/AfterArticlePlaylinks.test.js +0 -4
  35. package/src/tests/routes/components/Playlinks/Playlinks.test.js +1 -1
  36. package/src/tests/routes/components/TitlePopover.test.js +78 -0
  37. package/vite._mount.config.js.timestamp-1780937644427-faf490b0d476d8.mjs +105 -0
  38. package/src/tests/routes/components/InjectionPopover.test.js +0 -117
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.10.2",
3
+ "version": "8.11.0-beta.2",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -166,6 +166,11 @@ export const translations = {
166
166
  [Language.Swedish]: 'Streaming Guide',
167
167
  [Language.Danish]: 'Streaming Guide',
168
168
  },
169
+ 'Streaming Guide Heading': {
170
+ [Language.English]: 'Discover and search all movies and tv-shows',
171
+ [Language.Swedish]: 'Upptäck och sök bland alla filmer och tv-serier',
172
+ [Language.Danish]: 'Opdag og søg i alle film og tv-serier',
173
+ },
169
174
  'Streaming Guide Description': {
170
175
  [Language.English]: 'Find where to watch movies online - the ultimate guide that helps you find the best movies and shows across streaming services.',
171
176
  [Language.Swedish]: 'Sök bland alla filmer och serier för att ta reda på var du kan streama dem',
@@ -16,10 +16,10 @@ export const TrackingEvent = {
16
16
  ParticipantModalClose: 'ali_participant_modal_close',
17
17
 
18
18
  // Popover
19
- InjectionPopoverView: 'ali_injection_popover_view',
20
- InjectionPopoverClose: 'ali_injection_popover_close',
21
- InjectionPopoverSaveClick: 'ali_injection_popover_save_click',
22
- InjectionPopoverPlaylinkClick: 'ali_injection_popover_playlink_click',
19
+ TitlePopoverView: 'ali_title_popover_view',
20
+ TitlePopoverClose: 'ali_title_popover_close',
21
+ TitlePopoverSaveClick: 'ali_title_popover_save_click',
22
+ TitlePopoverPlaylinkClick: 'ali_title_popover_playlink_click',
23
23
 
24
24
  // Rails
25
25
  SimilarTitleClick: 'ali_similar_title_click',
@@ -43,6 +43,38 @@ export const title: TitleData = {
43
43
  embeddable_url: null,
44
44
  }
45
45
 
46
+ export const linkInjections: LinkInjection[] = [{
47
+ sid: '1',
48
+ title: 'Quan',
49
+ sentence: 'In an interview with Epire Magazine, Quan reveals he quested starring in Love Hurts',
50
+ playpilot_url: 'https://playpilot.com/movie/example/',
51
+ key: 'some-key-1',
52
+ title_details: title,
53
+ }, {
54
+ sid: '2',
55
+ title: 'The Long Kiss Goodnight',
56
+ sentence: 'The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody',
57
+ playpilot_url: 'https://playpilot.com/movie/example-2/',
58
+ key: 'some-key-2',
59
+ after_article: false,
60
+ title_details: title,
61
+ }, {
62
+ sid: '3',
63
+ title: 'Nobody',
64
+ sentence: 'The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody',
65
+ playpilot_url: 'https://playpilot.com/movie/example-3/',
66
+ key: 'some-key-3',
67
+ after_article: true,
68
+ title_details: title,
69
+ manual: false,
70
+ }, {
71
+ sid: '4',
72
+ title: 'The Wheel of Time',
73
+ sentence: '(The more I think about the Bene Gesserit the more I see how much they influenced not just the Jedi in Star Wars but also the Aes Sedai in The Wheel of Time and even the sorceresses in The Witcher books, though Herbert\'s order is the most ominous and terrifying of them all)',
74
+ playpilot_url: 'https://playpilot.com/movie/example-4/',
75
+ key: 'some-key-4',
76
+ }]
77
+
46
78
  export const participants: ParticipantData[] = [
47
79
  {
48
80
  sid: 'pr5C5W',
@@ -112,50 +144,6 @@ export const participants: ParticipantData[] = [
112
144
  },
113
145
  ]
114
146
 
115
- export const linkInjections: LinkInjection[] = [{
116
- sid: '1',
117
- title: 'Quan',
118
- sentence: 'In an interview with Epire Magazine, Quan reveals he quested starring in Love Hurts',
119
- playpilot_url: 'https://playpilot.com/movie/example/',
120
- key: 'some-key-1',
121
- title_details: title,
122
- type: 'title',
123
- }, {
124
- sid: '2',
125
- title: 'The Long Kiss Goodnight',
126
- sentence: 'The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody',
127
- playpilot_url: 'https://playpilot.com/movie/example-2/',
128
- key: 'some-key-2',
129
- after_article: false,
130
- title_details: title,
131
- type: 'title',
132
- }, {
133
- sid: '3',
134
- title: 'Nobody',
135
- sentence: 'The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody',
136
- playpilot_url: 'https://playpilot.com/movie/example-3/',
137
- key: 'some-key-3',
138
- after_article: true,
139
- title_details: title,
140
- manual: false,
141
- type: 'title',
142
- }, {
143
- sid: '4',
144
- title: 'The Wheel of Time',
145
- sentence: '(The more I think about the Bene Gesserit the more I see how much they influenced not just the Jedi in Star Wars but also the Aes Sedai in The Wheel of Time and even the sorceresses in The Witcher books, though Herbert\'s order is the most ominous and terrifying of them all)',
146
- playpilot_url: 'https://playpilot.com/movie/example-4/',
147
- key: 'some-key-4',
148
- type: 'title',
149
- }, {
150
- sid: '5',
151
- title: 'Jack Black',
152
- sentence: 'Jason Momoa (”Aquaman”), Jack Black (”Nacho Libre”) och Jennifer Coolidge (”The White Lotus”) medverkar i den Jared Hess-regisserade (”Napolen Dynamite”) filmen.',
153
- playpilot_url: 'https://playpilot.com/name/pr875J-jack-black/',
154
- key: 'some-key-5',
155
- participant_details: participants[2],
156
- type: 'participant',
157
- }]
158
-
159
147
  export const campaign: Campaign = {
160
148
  campaign_format: 'card',
161
149
  campaign_type: 'image',
@@ -168,7 +168,7 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
168
168
  // The function itself will decide whether or not it should actually insert the component based on the config.
169
169
  if (document.querySelector(keySelector)) insertInTextDisclaimer(elements)
170
170
 
171
- return mergedInjections.filter(injection => hasValidTypeData(injection)).map((injection, index) => {
171
+ return mergedInjections.filter(i => i.title_details).map((injection, index) => {
172
172
  const hasManualEquivalent = !injection.manual && isAvailableAsManualInjection(injection, index, mergedInjections)
173
173
  const duplicate = injection.duplicate ?? hasManualEquivalent
174
174
 
@@ -376,11 +376,11 @@ export function separateLinkInjectionTypes(injections: LinkInjection[]): LinkInj
376
376
  }
377
377
 
378
378
  export function isValidInjection(injection: LinkInjection): boolean {
379
- return !injection.inactive && !injection.removed && !injection.duplicate && hasValidTypeData(injection) && isValidPlaylinkType(injection)
379
+ return !injection.inactive && !injection.removed && !injection.duplicate && !!injection.title_details && isValidPlaylinkType(injection)
380
380
  }
381
381
 
382
382
  /**
383
- * An injection can be of various playlink types, when all are false equivalent, the link is not valid.
383
+ * An injection can have be of various playlink types, when all are false equivalent, the link is not valid.
384
384
  * It should be treated similar to an inactive playlink in this case.
385
385
  */
386
386
  export function isValidPlaylinkType(injection: LinkInjection): boolean {
@@ -388,10 +388,16 @@ export function isValidPlaylinkType(injection: LinkInjection): boolean {
388
388
  return !!injection.after_article
389
389
  }
390
390
 
391
+ /**
392
+ * Filter links for in-text injections, removing after article, inactive, removed, duplicate, and items without title_details
393
+ */
391
394
  export function filterInvalidInTextInjections(injections: LinkInjection[]): LinkInjection[] {
392
395
  return filterRemovedAndInactiveInjections(injections).filter(i => i.in_text !== false && isValidInjection(i))
393
396
  }
394
397
 
398
+ /**
399
+ * Filter links for after article injections, removing in-text only, inactive, removed, duplicate, and items without title_details
400
+ */
395
401
  export function filterInvalidAfterArticleInjections(injections: LinkInjection[]): LinkInjection[] {
396
402
  return filterRemovedAndInactiveInjections(injections).filter(i => i.after_article === true && isValidInjection(i))
397
403
  }
@@ -436,13 +442,6 @@ export function isEquivalentInjection(injection1: LinkInjection, injection2: Lin
436
442
  return injection1.title === injection2.title && cleanPhrase(injection1.sentence) === cleanPhrase(injection2.sentence)
437
443
  }
438
444
 
439
- export function hasValidTypeData(injection: LinkInjection): boolean {
440
- if (injection.type === 'title' && !!injection.title_details) return true
441
- if (injection.type === 'participant' && !!injection.participant_details) return true
442
-
443
- return false
444
- }
445
-
446
445
  export function getPlayPilotWrapperElement(): Element {
447
446
  return document.querySelector('[data-playpilot-link-injections]') || document.body
448
447
  }
package/src/lib/modal.ts CHANGED
@@ -133,9 +133,7 @@ export function openModalForInjectedLink(event: MouseEvent, injections: LinkInje
133
133
  event.preventDefault()
134
134
 
135
135
  playFallbackViewTransition(() => {
136
- const data = injection.title_details || injection.participant_details
137
-
138
136
  destroyLinkPopover(false)
139
- openModal({ event, injection, data, type: injection.type })
137
+ openModal({ event, injection, data: injection.title_details })
140
138
  }, !prefersReducedMotion.current && window.innerWidth >= mobileBreakpoint && !window.matchMedia('(pointer: coarse)').matches)
141
139
  }
@@ -1,30 +1,29 @@
1
1
  import { mount, unmount } from 'svelte'
2
+ import TitlePopover from '../routes/components/TitlePopover.svelte'
2
3
  import { getPlayPilotWrapperElement, keyDataAttribute, keySelector } from './injection'
3
4
  import type { LinkInjection } from './types/injection'
4
- import InjectionPopover from '../routes/components/InjectionPopover.svelte'
5
5
 
6
6
  export let currentlyHoveredInjection: EventTarget | null = null
7
7
  export let activePopoverInsertedComponent: object | null = null
8
8
 
9
+ /**
10
+ * When a link is hovered it is shown as a popover. The component is mounted when a mouse enters the link,
11
+ * and removed when clicked or on mouseleave.
12
+ */
9
13
  export function openPopoverForInjectedLink(event: MouseEvent, injection: LinkInjection): void {
14
+ // Skip touch devices
10
15
  if (window.matchMedia('(pointer: coarse)').matches) return
11
16
  if (activePopoverInsertedComponent) destroyLinkPopover()
12
17
 
13
- const currentTarget = event.currentTarget as Element
14
- currentlyHoveredInjection = currentTarget
18
+ const target = event.currentTarget as Element
19
+ currentlyHoveredInjection = target
15
20
 
16
21
  // Only show if the link is hovered for more than 100ms. This is to prevent the popover from showing
17
22
  // when a user just happens to mouseover as they are moving their mouse about.
18
23
  setTimeout(() => {
19
- if (currentlyHoveredInjection !== currentTarget) return
24
+ if (currentlyHoveredInjection !== target) return // User is no longer hovering this link
20
25
 
21
- const target = getPlayPilotWrapperElement()
22
-
23
- if (injection.type === 'title') {
24
- activePopoverInsertedComponent = mount(InjectionPopover, { target, props: { event, type: 'title', data: injection.title_details! } })
25
- } else if (injection.type === 'participant') {
26
- activePopoverInsertedComponent = mount(InjectionPopover, { target, props: { event, type: 'participant', data: injection.participant_details! } })
27
- }
26
+ activePopoverInsertedComponent = mount(TitlePopover, { target: getPlayPilotWrapperElement(), props: { event, title: injection.title_details! } })
28
27
  }, 100)
29
28
  }
30
29
 
@@ -45,7 +44,7 @@ export async function destroyLinkPopover(outro: boolean = true): Promise<void> {
45
44
  // In that case we remove the element straight from the dom.
46
45
  // Doing this will prevent the outro animation from playing, but this being a fallback, that's ok.
47
46
  // TODO: Find the actual cause of this bug.
48
- document.querySelectorAll<HTMLElement>('[data-playpilot-injection-popover]').forEach(element => element.remove())
47
+ document.querySelectorAll<HTMLElement>('[data-playpilot-title-popover]').forEach(element => element.remove())
49
48
  }
50
49
 
51
50
  export function destroyLinkPopoverOnMouseleave(event: MouseEvent): void {
@@ -54,7 +53,7 @@ export function destroyLinkPopoverOnMouseleave(event: MouseEvent): void {
54
53
  const target = event.target as Element
55
54
 
56
55
  // Mousemove is inside of popover or link that popover
57
- if (target.hasAttribute('data-playpilot-injection-popover') || target.closest('[data-playpilot-injection-popover]') ||
56
+ if (target.hasAttribute('data-playpilot-title-popover') || target.closest('[data-playpilot-title-popover]') ||
58
57
  target.hasAttribute(keyDataAttribute) || target.closest(keySelector)) return
59
58
 
60
59
  destroyLinkPopover()
@@ -1,8 +1,5 @@
1
- import type { ParticipantData } from './participant'
2
1
  import type { TitleData } from './title'
3
2
 
4
- export type LinkInjectionDataType = 'title' | 'participant'
5
-
6
3
  export type LinkInjection = {
7
4
  sid: string
8
5
  title: string
@@ -10,7 +7,6 @@ export type LinkInjection = {
10
7
  playpilot_url: string
11
8
  key: string
12
9
  title_details?: TitleData
13
- participant_details?: ParticipantData
14
10
  phrase_before?: string | null
15
11
  phrase_after?: string | null
16
12
  inactive?: boolean
@@ -23,7 +19,6 @@ export type LinkInjection = {
23
19
  removed?: boolean
24
20
  duplicate?: boolean
25
21
  matchingElement?: null | Element
26
- type: LinkInjectionDataType
27
22
  }
28
23
 
29
24
  export type LinkInjectionRanges = Record<string, { elementIndex: number, from: number, to: number }>
@@ -58,7 +58,8 @@
58
58
  const linkInjectionsString = $derived(JSON.stringify(linkInjections))
59
59
  const showControls = $derived(!aiRunning && allowEditing)
60
60
  const hasChanged = $derived(initialStateString && initialStateString !== linkInjectionsString)
61
- const filteredInjections = $derived(linkInjections.filter((i) => (i.title_details || i.participant_details) && !i.removed && !i.duplicate && (i.manual || (!i.manual && !i.failed))))
61
+ // Filter out injections without title_details, injections that are removed, duplicate, or are AI injections that failed to inject
62
+ const filteredInjections = $derived(linkInjections.filter((i) => i.title_details && !i.removed && !i.duplicate && (i.manual || (!i.manual && !i.failed))))
62
63
  const sortedInjections = $derived(sortInjections(filteredInjections))
63
64
  const initialAiRunning = $derived(!loading && untrack(() => aiStatus.aiRunning))
64
65
 
@@ -74,7 +75,7 @@
74
75
  function sortInjections(injections: LinkInjection[]): LinkInjection[] {
75
76
  return injections.sort((a, b) => {
76
77
  if (a.failed !== b.failed) return a.failed ? 1 : -1
77
- return (a.title_details?.title || a.participant_details?.name)!.localeCompare((b.title_details?.title || b.participant_details?.name)!)
78
+ return a.title_details!.title.localeCompare(b.title_details!.title)
78
79
  })
79
80
  }
80
81
 
@@ -11,6 +11,7 @@
11
11
  import { track } from '$lib/tracking'
12
12
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
13
13
  import type { LinkInjection } from '$lib/types/injection'
14
+ import type { TitleData } from '$lib/types/title'
14
15
  import { cleanPhrase, truncateAroundPhrase } from '$lib/text'
15
16
  import { isValidPlaylinkType } from '$lib/injection'
16
17
  import { getLinkInjectionElements, getLinkInjectionsParentElement } from '$lib/injectionElements'
@@ -26,8 +27,9 @@
26
27
 
27
28
  const { linkInjection = $bindable(), onremove = () => null, onhighlight = () => null }: Props = $props()
28
29
 
29
- const { key, type, sentence, title_details, participant_details, failed, failed_message, inactive } = $derived(linkInjection || {})
30
+ const { key, sentence, title_details, failed, failed_message, inactive } = $derived(linkInjection || {})
30
31
 
32
+ const title: TitleData = $derived(title_details!)
31
33
  const truncatedSentence = $derived(truncateAroundPhrase(linkInjection.sentence, linkInjection.title, 60))
32
34
 
33
35
  let expanded = $state(false)
@@ -36,7 +38,7 @@
36
38
  let element: HTMLElement | null = $state(null)
37
39
 
38
40
  onMount(() => {
39
- if (failed) track(TrackingEvent.InjectionFailed, type === 'title' ? title_details : null, { phrase: linkInjection.title, sentence})
41
+ if (failed) track(TrackingEvent.InjectionFailed, title, { phrase: linkInjection.title, sentence})
40
42
  })
41
43
 
42
44
  /**
@@ -111,14 +113,10 @@
111
113
  bind:this={element}
112
114
  out:slide|global={{ duration: 200 }}>
113
115
  <div class="header">
114
- {#if type === 'title'}
115
- <img class="poster" src={removeImageUrlPrefix(title_details!.standing_poster)} alt="" width="32" height="48" onerror={({ target }) => (target as HTMLImageElement).src = imagePlaceholderDataUrl} />
116
- {:else}
117
- <div class="placeholder-image"></div>
118
- {/if}
116
+ <img class="poster" src={removeImageUrlPrefix(title.standing_poster)} alt="" width="32" height="48" onerror={({ target }) => (target as HTMLImageElement).src = imagePlaceholderDataUrl} />
119
117
 
120
118
  <div class="info">
121
- <div class="title">{title_details?.title || participant_details?.name}</div>
119
+ <div class="title">{title.title}</div>
122
120
 
123
121
  <div class="sentence">
124
122
  <!-- eslint-disable-next-line svelte/no-at-html-tags -->
@@ -145,13 +143,9 @@
145
143
  <Alert>{failed_message}</Alert>
146
144
  {:else}
147
145
  <div class="actions">
148
- {#if type === 'title'}
149
- <button class="expand" onclick={() => expanded = !expanded} aria-label="Expand" aria-expanded={expanded}>
150
- <IconChevron {expanded} />
151
- </button>
152
- {:else}
153
- <div></div>
154
- {/if}
146
+ <button class="expand" onclick={() => expanded = !expanded} aria-label="Expand" aria-expanded={expanded}>
147
+ <IconChevron {expanded} />
148
+ </button>
155
149
 
156
150
  {#if !isValidPlaylinkType(linkInjection)}
157
151
  <div class="warning" transition:fade={{ duration: 100 }} aria-label="Invalid playlink settings">
@@ -216,14 +210,6 @@
216
210
  }
217
211
  }
218
212
 
219
- .placeholder-image {
220
- flex: 0 0 margin(2);
221
- width: margin(2);
222
- height: margin(2);
223
- border-radius: 50%;
224
- background: theme(content);
225
- }
226
-
227
213
  .title {
228
214
  font-size: margin(0.875);
229
215
  word-break: break-word;
@@ -169,7 +169,6 @@
169
169
  key: generateInjectionKey(selectedTitle.sid),
170
170
  title_details: selectedTitle,
171
171
  manual: true,
172
- type: 'title',
173
172
  }
174
173
 
175
174
  onsave(linkInjection)
@@ -21,6 +21,7 @@
21
21
 
22
22
  let { navigate = () => null, searchQuery = $bindable(''), filter = $bindable({}), children }: Props = $props()
23
23
 
24
+ // svelte-ignore non_reactive_update
24
25
  let element: HTMLElement | null = null
25
26
  let height: string | null = $state(null)
26
27
  let clientWidth: number = $state(window.innerWidth)
@@ -44,7 +45,7 @@
44
45
  <div class="divider"></div>
45
46
 
46
47
  <div class="heading" use:heading>
47
- {t('Streaming Guide')}
48
+ {t('Streaming Guide Heading')}
48
49
  </div>
49
50
 
50
51
  {#if !useExploreRouter()}
@@ -98,8 +99,13 @@
98
99
  display: flex;
99
100
  flex-direction: column;
100
101
  gap: margin(0.5);
101
- margin: theme(explore-header-margin, 0 0 margin(2));
102
+ margin: theme(explore-header-margin, 0 0 margin(1));
102
103
  width: 100%;
104
+ max-width: margin(12);
105
+
106
+ @include desktop {
107
+ max-width: margin(20);
108
+ }
103
109
  }
104
110
 
105
111
  .divider {
@@ -112,7 +118,7 @@
112
118
  .heading {
113
119
  margin: theme(explore-heading-margin, margin(0.25) 0);
114
120
  color: theme(text-color);
115
- font-size: theme(explore-heading-size, clamp(margin(1.5), 5vw, margin(2)));
121
+ font-size: theme(explore-heading-size, clamp(margin(1), 2.5vw, margin(1.5)));
116
122
  font-weight: theme(explore-heading-font-weight, font-bold);
117
123
  text-transform: theme(explore-heading-text-transform, normal);
118
124
  line-height: theme(explore-heading-line-height, 1.5);
@@ -6,9 +6,11 @@
6
6
  import { track } from '$lib/tracking'
7
7
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
8
8
  import TitlesRail from '../../Rails/TitlesRail.svelte'
9
+ import { mobileBreakpoint } from '$lib/constants'
9
10
 
10
11
  let expandedTitle: TitleData | null = $state(null)
11
12
  let expandedRailKey: string | null = $state(null)
13
+ let windowWidth: number = $state(window.innerWidth)
12
14
 
13
15
  const rails: { heading: string, params: Record<string, any>, properties: Record<string, any> }[] = [{
14
16
  heading: 'List: Trending',
@@ -17,7 +19,7 @@
17
19
  }, {
18
20
  heading: 'List: New',
19
21
  params: { from_playlist_sid: 'li42WR', include_playable_types: 'SVOD,FREE' },
20
- properties: { aside: true },
22
+ properties: { aside: true, size: 'flexible' },
21
23
  }, {
22
24
  heading: 'List: Upcoming',
23
25
  params: { from_playlist_sid: 'li42wf', region: null, no_region_filter: true },
@@ -29,7 +31,7 @@
29
31
  }, {
30
32
  heading: 'List: Demand',
31
33
  params: { from_playlist_sid: 'licBS' },
32
- properties: { aside: true },
34
+ properties: { aside: true, size: 'flexible' },
33
35
  }]
34
36
 
35
37
  async function getListTitles(params: Record<string, any> = {}): Promise<TitleData[]> {
@@ -43,7 +45,7 @@
43
45
  }
44
46
  </script>
45
47
 
46
- <svelte:window {onscroll} />
48
+ <svelte:window {onscroll} bind:innerWidth={windowWidth} />
47
49
 
48
50
  <div data-testid="explore-home"></div>
49
51
 
@@ -52,7 +54,7 @@
52
54
  <TitlesRail
53
55
  heading={t(heading)}
54
56
  titles={getListTitles(params)}
55
- size="flexible"
57
+ size={windowWidth >= mobileBreakpoint ? 'flexible' : 'huge'}
56
58
  minimumLength={7}
57
59
  {...properties}
58
60
  bind:expandedTitle
@@ -8,16 +8,15 @@
8
8
 
9
9
  interface Props {
10
10
  title: TitleData
11
- compact?: boolean
12
11
  onclick?: (event: MouseEvent) => void
13
12
  }
14
13
 
15
- const { title, compact = false, onclick = () => null }: Props = $props()
14
+ const { title, onclick = () => null }: Props = $props()
16
15
 
17
16
  const noAffiliate = !!window.PlayPilotLinkInjections?.no_affiliate
18
17
  </script>
19
18
 
20
- <button class="title" class:compact {onclick} data-testid="title">
19
+ <button class="title" {onclick} data-testid="title">
21
20
  <div class="poster">
22
21
  <TitlePoster {title} width={30} height={43} lazy />
23
22
  </div>
@@ -39,14 +38,12 @@
39
38
  {/if}
40
39
  </div>
41
40
 
42
- {#if !compact}
43
- <div class="description" class:large={noAffiliate}>
44
- {title.description}
45
- </div>
46
- {/if}
41
+ <div class="description" class:large={noAffiliate}>
42
+ {title.description}
43
+ </div>
47
44
 
48
45
  {#if !noAffiliate}
49
- <PlaylinksCompact playlinks={title.providers} size={compact ? 24 : 30} />
46
+ <PlaylinksCompact playlinks={title.providers} />
50
47
  {/if}
51
48
  </div>
52
49
 
@@ -87,10 +84,6 @@
87
84
  border-radius: theme(detail-image-border-radius, border-radius);
88
85
  background: theme(detail-image-background, content);
89
86
  overflow: hidden;
90
-
91
- .compact & {
92
- width: margin(3.5);
93
- }
94
87
  }
95
88
 
96
89
  .content {
@@ -104,17 +97,11 @@
104
97
  @include desktop() {
105
98
  padding-right: margin(1);
106
99
  }
107
-
108
- .compact & {
109
- padding-left: margin(0.75);
110
- padding-right: 0;
111
- }
112
100
  }
113
101
 
114
102
  .heading {
115
103
  color: theme(list-item-title-text-color, text-color);
116
104
  font-weight: theme(list-item-title-font-weight, font-bold);
117
- line-height: 1.2;
118
105
  }
119
106
 
120
107
  .meta {
@@ -129,10 +116,6 @@
129
116
  > div {
130
117
  text-transform: capitalize;
131
118
  }
132
-
133
- .compact & {
134
- margin: margin(0.125) 0 margin(0.25) 0;
135
- }
136
119
  }
137
120
 
138
121
  .imdb {
@@ -169,9 +152,5 @@
169
152
  &:active {
170
153
  color: theme(list-item-action-hover-color, text-color);
171
154
  }
172
-
173
- .compact & {
174
- display: none;
175
- }
176
155
  }
177
156
  </style>
@@ -131,7 +131,7 @@
131
131
  border-radius: theme(rail-modal-item-border-radius, border-radius-huge) theme(rail-modal-item-border-radius, border-radius-huge) 0 0;
132
132
  background: theme(rail-modal-item-background, light);
133
133
  box-shadow: none;
134
- height: 80vh;
134
+ height: 90vh;
135
135
  overflow: auto;
136
136
  overscroll-behavior: contain;
137
137
  transition: box-shadow var(--transition-duration);
@@ -13,10 +13,9 @@
13
13
 
14
14
  interface Props {
15
15
  participant: ParticipantData
16
- small?: boolean
17
16
  }
18
17
 
19
- const { participant, small = false }: Props = $props()
18
+ const { participant }: Props = $props()
20
19
 
21
20
  const { name, birth_date, death_date } = $derived(participant)
22
21
 
@@ -53,8 +52,8 @@
53
52
  }
54
53
  </script>
55
54
 
56
- <div class="header" class:small>
57
- <div class="heading" use:heading={2} id="heading">{name}</div>
55
+ <div class="header">
56
+ <div class="heading" use:heading={2} id="name">{name}</div>
58
57
 
59
58
  {#if birth_date}
60
59
  <p class="dates">
@@ -64,13 +63,11 @@
64
63
  </div>
65
64
 
66
65
  <div class="content">
67
- {#if !small}
68
- <div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
69
- {/if}
66
+ <div class="heading small" use:heading={3} id="credits">{t('Credits')}</div>
70
67
 
71
68
  <div class="list">
72
69
  {#each titles as title}
73
- <ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
70
+ <ListTitle {title} onclick={(event) => openModal({ event, data: title })} />
74
71
  {/each}
75
72
 
76
73
  {#if loading}
@@ -91,10 +88,6 @@
91
88
  font-size: theme(detail-font-size, font-size-base);
92
89
  line-height: theme(participant-description-line-height, normal);
93
90
  color: theme(detail-text-color, text-color);
94
-
95
- &.small {
96
- padding: margin(1);
97
- }
98
91
  }
99
92
 
100
93
  .heading {
@@ -105,7 +98,7 @@
105
98
  line-height: normal;
106
99
  font-style: theme(detail-title-font-style, normal);
107
100
 
108
- &.subheading {
101
+ &.small {
109
102
  margin: 0 0 margin(0.5);
110
103
  font-size: theme(detail-title-small-font-size, margin(1.25));
111
104
  }
@@ -123,11 +116,6 @@
123
116
  font-size: theme(detail-font-size, font-size-base);
124
117
  line-height: normal;
125
118
  font-style: normal;
126
-
127
- ::view-transition-old(content),
128
- ::view-transition-new(content) {
129
- height: 100%;
130
- }
131
119
  }
132
120
 
133
121
  .list {
@@ -22,7 +22,6 @@
22
22
  const { playlinks, title }: Props = $props()
23
23
 
24
24
  const isModal = getContext('scope') === 'modal'
25
- const type = getContext('type')
26
25
  const displayAd = getFirstAdOfType('card')
27
26
  const categorize = !!window.PlayPilotLinkInjections?.config?.categorize_playlinks
28
27
 
@@ -38,7 +37,7 @@
38
37
  const list = $derived(outerWidth < 500 || !!displayAd || mergedPlaylinks.length === 1)
39
38
 
40
39
  function onclick(playlink: string): void {
41
- track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.InjectionPopoverPlaylinkClick, title, { playlink, type })
40
+ track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.TitlePopoverPlaylinkClick, title, { playlink })
42
41
  }
43
42
 
44
43
  function categorizePlaylinks(playlinks: PlaylinkData[]): CategorizedPlaylinks {