@playpilot/tpi 3.3.0 → 3.3.1
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/dist/link-injections.js +8 -8
- package/package.json +1 -1
- package/src/lib/linkInjection.ts +29 -13
- package/src/lib/scss/_mixins.scss +13 -0
- package/src/routes/+page.svelte +1 -23
- package/src/routes/components/AfterArticlePlaylinks.svelte +5 -6
- package/src/routes/components/Editorial/Editor.svelte +1 -0
- package/src/routes/components/Modal.svelte +1 -0
- package/src/routes/components/Popover.svelte +1 -0
- package/src/tests/lib/linkInjection.test.js +51 -56
- package/src/tests/routes/components/Editorial/EditorItem.test.js +7 -7
package/package.json
CHANGED
package/src/lib/linkInjection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mount, unmount } from 'svelte'
|
|
2
|
+
import TitleModal from '../routes/components/TitleModal.svelte'
|
|
2
3
|
import TitlePopover from '../routes/components/TitlePopover.svelte'
|
|
3
4
|
import AfterArticlePlaylinks from '../routes/components/AfterArticlePlaylinks.svelte'
|
|
4
5
|
import { cleanPhrase, findTextNodeContaining, isNodeInLink, replaceStartingFrom } from './text'
|
|
@@ -12,6 +13,7 @@ const activePopovers: Record<string, { injection: LinkInjection; component: obje
|
|
|
12
13
|
|
|
13
14
|
let currentlyHoveredInjection: EventTarget | null = null
|
|
14
15
|
let afterArticlePlaylinkInsertedComponent: object | null = null
|
|
16
|
+
let activeModalInsertedComponent: object | null = null
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Return a list of all valid text containing elements that may get injected into.
|
|
@@ -28,7 +30,7 @@ export function getLinkInjectionElements(parentElement: HTMLElement): HTMLElemen
|
|
|
28
30
|
if (validElements.includes(element)) continue
|
|
29
31
|
|
|
30
32
|
// Ignore links, buttons, and headers
|
|
31
|
-
if (/^(A|BUTTON|SCRIPT|NOSCRIPT|STYLE|IFRAME|H[1-6])$/.test(element.tagName)) continue
|
|
33
|
+
if (/^(A|BUTTON|SCRIPT|NOSCRIPT|STYLE|IFRAME|FIGCAPTION|H[1-6])$/.test(element.tagName)) continue
|
|
32
34
|
|
|
33
35
|
// Check if this element has a direct text node
|
|
34
36
|
const hasTextNode = Array.from(element.childNodes).some(
|
|
@@ -90,9 +92,10 @@ export function getLinkInjectionsParentElement(): HTMLElement {
|
|
|
90
92
|
* Replace all found injections within all given elements on the page
|
|
91
93
|
* @returns Returns an array of injections with injections that failed to be inserted marked as `failed`.
|
|
92
94
|
*/
|
|
93
|
-
export function injectLinksInDocument(elements: HTMLElement[],
|
|
94
|
-
|
|
95
|
+
export function injectLinksInDocument(elements: HTMLElement[], injections: LinkInjectionTypes = { aiInjections: [], manualInjections: [] }): LinkInjection[] {
|
|
96
|
+
clearLinkInjections()
|
|
95
97
|
|
|
98
|
+
const mergedInjections = mergeInjectionTypes(injections)
|
|
96
99
|
if (!mergedInjections) return []
|
|
97
100
|
|
|
98
101
|
// Find injection in text content of all elements together, ignore potential HTML elements.
|
|
@@ -162,11 +165,11 @@ export function injectLinksInDocument(elements: HTMLElement[], onclick: (LinkInj
|
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
|
|
165
|
-
addLinkInjectionEventListeners(validInjections
|
|
168
|
+
addLinkInjectionEventListeners(validInjections)
|
|
166
169
|
addCSSVariablesToLinks()
|
|
167
170
|
|
|
168
171
|
const afterArticleInjections = filterInvalidAfterArticleInjections(mergedInjections)
|
|
169
|
-
if (afterArticleInjections.length) insertAfterArticlePlaylinks(elements, afterArticleInjections
|
|
172
|
+
if (afterArticleInjections.length) insertAfterArticlePlaylinks(elements, afterArticleInjections)
|
|
170
173
|
|
|
171
174
|
return mergedInjections.filter(i => i.title_details).map((injection, index) => {
|
|
172
175
|
// Favour manual injections over AI injections
|
|
@@ -219,7 +222,7 @@ function addCSSVariablesToLinks(): void {
|
|
|
219
222
|
/**
|
|
220
223
|
* Add event listeners to all injected links. These events are for both the popover and the modal.
|
|
221
224
|
*/
|
|
222
|
-
function addLinkInjectionEventListeners(injections: LinkInjection[]
|
|
225
|
+
function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
223
226
|
// Open modal on click
|
|
224
227
|
window.addEventListener('click', (event) => {
|
|
225
228
|
const target = event.target as HTMLElement | null
|
|
@@ -231,7 +234,7 @@ function addLinkInjectionEventListeners(injections: LinkInjection[], onclick: (i
|
|
|
231
234
|
const injection = injections.find(injection => key === injection.key)
|
|
232
235
|
if (!injection) return
|
|
233
236
|
|
|
234
|
-
openLinkModal(event, injection
|
|
237
|
+
openLinkModal(event, injection)
|
|
235
238
|
})
|
|
236
239
|
|
|
237
240
|
const createdInjectionElements = Array.from(document.querySelectorAll(keySelector)) as HTMLElement[]
|
|
@@ -250,16 +253,27 @@ function addLinkInjectionEventListeners(injections: LinkInjection[], onclick: (i
|
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
256
|
+
* Open modal for the corresponding injection by mounting the component and saving it to a variable.
|
|
257
|
+
* Ignore clicks that used modifier keys or that were not left click.
|
|
255
258
|
*/
|
|
256
|
-
function openLinkModal(event: MouseEvent, injection: LinkInjection
|
|
259
|
+
function openLinkModal(event: MouseEvent, injection: LinkInjection): void {
|
|
257
260
|
if (event.ctrlKey || event.metaKey || event.button !== 0) return
|
|
261
|
+
if (activeModalInsertedComponent) return
|
|
258
262
|
|
|
259
263
|
event.preventDefault()
|
|
260
264
|
|
|
261
|
-
onclick(injection)
|
|
262
265
|
destroyLinkPopover(injection)
|
|
266
|
+
activeModalInsertedComponent = mount(TitleModal, { target: document.body, props: { title: injection.title_details!, onclose: destroyLinkModal } })
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Unmount the modal, removing it from the dom
|
|
271
|
+
*/
|
|
272
|
+
function destroyLinkModal(outro: boolean = true): void {
|
|
273
|
+
if (!activeModalInsertedComponent) return
|
|
274
|
+
|
|
275
|
+
unmount(activeModalInsertedComponent, { outro })
|
|
276
|
+
activeModalInsertedComponent = null
|
|
263
277
|
}
|
|
264
278
|
|
|
265
279
|
/**
|
|
@@ -303,7 +317,8 @@ function destroyLinkPopover(injection: LinkInjection, outro: boolean = true) {
|
|
|
303
317
|
* The config object contains a selector option as well as a position. This way a selector can be given and you can
|
|
304
318
|
* choose to insert the after article before or after the given element.
|
|
305
319
|
*/
|
|
306
|
-
export function insertAfterArticlePlaylinks(elements: HTMLElement[], injections: LinkInjection[]
|
|
320
|
+
export function insertAfterArticlePlaylinks(elements: HTMLElement[], injections: LinkInjection[]): void {
|
|
321
|
+
if (afterArticlePlaylinkInsertedComponent) return
|
|
307
322
|
if (!injections.length) return
|
|
308
323
|
|
|
309
324
|
const target = document.createElement('div')
|
|
@@ -314,7 +329,7 @@ export function insertAfterArticlePlaylinks(elements: HTMLElement[], injections:
|
|
|
314
329
|
target.dataset.playpilotAfterArticlePlaylinks = 'true'
|
|
315
330
|
insertElement.insertAdjacentElement(insertPosition, target)
|
|
316
331
|
|
|
317
|
-
afterArticlePlaylinkInsertedComponent = mount(AfterArticlePlaylinks, { target, props: { linkInjections: injections, onclickmodal } })
|
|
332
|
+
afterArticlePlaylinkInsertedComponent = mount(AfterArticlePlaylinks, { target, props: { linkInjections: injections, onclickmodal: (event, injection) => openLinkModal(event, injection) } })
|
|
318
333
|
}
|
|
319
334
|
|
|
320
335
|
function clearAfterArticlePlaylinks(): void {
|
|
@@ -337,6 +352,7 @@ export function clearLinkInjections(): void {
|
|
|
337
352
|
Object.values(activePopovers).forEach(({ injection }) => destroyLinkPopover(injection, false))
|
|
338
353
|
|
|
339
354
|
clearAfterArticlePlaylinks()
|
|
355
|
+
destroyLinkModal()
|
|
340
356
|
}
|
|
341
357
|
|
|
342
358
|
/**
|
package/src/routes/+page.svelte
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { isCrawler } from '$lib/crawler'
|
|
8
8
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
9
9
|
import { authorize, getAuthToken, isEditorialModeEnabled, removeAuthCookie, setEditorialParamInUrl } from '$lib/auth'
|
|
10
|
-
import TitleModal from './components/TitleModal.svelte'
|
|
11
10
|
import Editor from './components/Editorial/Editor.svelte'
|
|
12
11
|
import EditorTrigger from './components/Editorial/EditorTrigger.svelte'
|
|
13
12
|
import type { LinkInjectionResponse, LinkInjection, LinkInjectionTypes } from '$lib/types/injection'
|
|
@@ -17,7 +16,6 @@
|
|
|
17
16
|
const htmlString = elements.map(p => p.outerHTML).join('')
|
|
18
17
|
|
|
19
18
|
let response: LinkInjectionResponse | null = $state(null)
|
|
20
|
-
let activeInjection: LinkInjection | null = $state(null)
|
|
21
19
|
let isEditorialMode = $state(isEditorialModeEnabled())
|
|
22
20
|
let hasAuthToken = $state(!!getAuthToken())
|
|
23
21
|
let authorized = $state(false)
|
|
@@ -91,14 +89,13 @@
|
|
|
91
89
|
}
|
|
92
90
|
|
|
93
91
|
function rerender(): void {
|
|
94
|
-
clearLinkInjections()
|
|
95
92
|
inject(separateLinkInjectionTypes(linkInjections))
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
function inject(injections: LinkInjectionTypes = { aiInjections, manualInjections }): void {
|
|
99
96
|
// Get filtered injections as they are shown on the page.
|
|
100
97
|
// Only update state if it they are different from current injections.
|
|
101
|
-
const filteredInjections = injectLinksInDocument(elements,
|
|
98
|
+
const filteredInjections = injectLinksInDocument(elements, injections)
|
|
102
99
|
if (JSON.stringify(filteredInjections) !== JSON.stringify(linkInjections)) linkInjections = filteredInjections
|
|
103
100
|
|
|
104
101
|
const successfulInjections = filteredInjections.filter(i => !i.failed)
|
|
@@ -112,10 +109,6 @@
|
|
|
112
109
|
})
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
function setTarget(injection: LinkInjection): void {
|
|
116
|
-
activeInjection = injection
|
|
117
|
-
}
|
|
118
|
-
|
|
119
112
|
function openEditorialMode() {
|
|
120
113
|
isEditorialMode = true
|
|
121
114
|
setEditorialParamInUrl()
|
|
@@ -145,10 +138,6 @@
|
|
|
145
138
|
percentage: response?.ai_progress_percentage,
|
|
146
139
|
}} />
|
|
147
140
|
{/if}
|
|
148
|
-
|
|
149
|
-
{#if activeInjection && activeInjection.title_details}
|
|
150
|
-
<TitleModal title={activeInjection.title_details} onclose={() => activeInjection = null} />
|
|
151
|
-
{/if}
|
|
152
141
|
</div>
|
|
153
142
|
|
|
154
143
|
<style lang="scss">
|
|
@@ -160,16 +149,5 @@
|
|
|
160
149
|
:global(*) {
|
|
161
150
|
box-sizing: border-box;
|
|
162
151
|
}
|
|
163
|
-
|
|
164
|
-
:global(.playpilot-link-injections button),
|
|
165
|
-
:global(.playpilot-link-injections input) {
|
|
166
|
-
transition: outline-offset 100ms;
|
|
167
|
-
|
|
168
|
-
&:focus-visible,
|
|
169
|
-
&:focus-visible {
|
|
170
|
-
outline: 2px solid white;
|
|
171
|
-
outline-offset: 2px;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
152
|
}
|
|
175
153
|
</style>
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
interface Props {
|
|
9
9
|
linkInjections: LinkInjection[],
|
|
10
10
|
// eslint-disable-next-line no-unused-vars
|
|
11
|
-
onclickmodal?: (linkInjection: LinkInjection) => void
|
|
11
|
+
onclickmodal?: (event: MouseEvent, linkInjection: LinkInjection) => void
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
const { linkInjections, onclickmodal = (linkInjection) => null }: Props = $props()
|
|
14
|
+
const { linkInjections, onclickmodal = () => null }: Props = $props()
|
|
16
15
|
|
|
17
16
|
function onclick(title: TitleData, playlink: string): void {
|
|
18
17
|
track(TrackingEvent.AfterArticlePlaylinkClick, title, { playlink })
|
|
@@ -21,9 +20,9 @@
|
|
|
21
20
|
/**
|
|
22
21
|
* Open a modal for the given injection and track the click
|
|
23
22
|
*/
|
|
24
|
-
function openModal(title: TitleData, linkInjection: LinkInjection): void {
|
|
23
|
+
function openModal(event: MouseEvent, title: TitleData, linkInjection: LinkInjection): void {
|
|
25
24
|
track(TrackingEvent.AfterArticleModalButtonClick, title)
|
|
26
|
-
onclickmodal(linkInjection)
|
|
25
|
+
onclickmodal(event, linkInjection)
|
|
27
26
|
}
|
|
28
27
|
</script>
|
|
29
28
|
|
|
@@ -39,7 +38,7 @@
|
|
|
39
38
|
"{title}" {t('Is Available To Stream')}
|
|
40
39
|
|
|
41
40
|
<span>
|
|
42
|
-
<button onclick={() => openModal(title_details as TitleData, linkInjection)}>
|
|
41
|
+
<button onclick={(event) => openModal(event, title_details as TitleData, linkInjection)}>
|
|
43
42
|
{t('View Streaming Options')}
|
|
44
43
|
</button>
|
|
45
44
|
</span>
|