|
1 | 1 | import type { RegistryScriptInput, UseScriptContext } from '#nuxt-scripts/types' |
2 | 2 | import { useHead } from '@unhead/vue' |
3 | 3 | import { useNuxtApp } from 'nuxt/app' |
4 | | -import { logger } from '../logger' |
5 | 4 | import { useRegistryScript } from '../utils' |
6 | | -import { createAbortablePromise, createAbortError } from '../utils/abortable-promise' |
| 5 | +import { attachUsercentricsConsent } from '../utils/usercentrics-consent' |
7 | 6 | import { UsercentricsOptions } from './schemas' |
8 | 7 |
|
9 | 8 | export { UsercentricsOptions } |
@@ -81,95 +80,6 @@ export interface UsercentricsConsent { |
81 | 80 | denyAll: () => Promise<void> | void |
82 | 81 | } |
83 | 82 |
|
84 | | -interface UsercentricsEventTarget { |
85 | | - __ucCmp?: UsercentricsCmp |
86 | | - addEventListener: Window['addEventListener'] |
87 | | - removeEventListener: Window['removeEventListener'] |
88 | | -} |
89 | | - |
90 | | -type HookAppUnmount = (callback: () => void) => () => void |
91 | | - |
92 | | -export function attachUsercentricsConsent<T extends UsercentricsApi>( |
93 | | - instance: UseScriptContext<T, UsercentricsConsent>, |
94 | | - target: UsercentricsEventTarget, |
95 | | - hookAppUnmount: HookAppUnmount, |
96 | | -): void { |
97 | | - if (instance.consent) |
98 | | - return |
99 | | - |
100 | | - let readyApi: UsercentricsCmp | undefined |
101 | | - let readyPromise: Promise<UsercentricsCmp> | undefined |
102 | | - const readyController = new AbortController() |
103 | | - let disposed = false |
104 | | - const cleanupReadyListener = () => { |
105 | | - if (disposed) |
106 | | - return |
107 | | - disposed = true |
108 | | - readyController.abort() |
109 | | - } |
110 | | - const whenReady = (): Promise<UsercentricsCmp> => { |
111 | | - if (disposed) |
112 | | - return Promise.reject(createAbortError('Usercentrics readiness wait was aborted')) |
113 | | - if (readyApi) |
114 | | - return Promise.resolve(readyApi) |
115 | | - if (!readyPromise) { |
116 | | - // Install the event listener before checking isInitialized() so an |
117 | | - // event fired during that async check cannot be missed. |
118 | | - readyPromise = createAbortablePromise<UsercentricsCmp>((resolve) => { |
119 | | - const onReady = () => { |
120 | | - const api = target.__ucCmp |
121 | | - if (!api) |
122 | | - return |
123 | | - readyApi = api |
124 | | - resolve(api) |
125 | | - } |
126 | | - target.addEventListener('UC_CMP_API_READY', onReady) |
127 | | - const api = target.__ucCmp |
128 | | - if (api?.isInitialized) { |
129 | | - Promise.resolve() |
130 | | - .then(() => api.isInitialized()) |
131 | | - .then((initialized) => { |
132 | | - if (initialized && !readyController.signal.aborted) |
133 | | - onReady() |
134 | | - }) |
135 | | - .catch((error) => { |
136 | | - // Some bootstrap stubs throw until the ready event; the event |
137 | | - // listener remains the authoritative readiness signal. |
138 | | - if (!readyController.signal.aborted) |
139 | | - logger.debug('[usercentrics] Waiting for UC_CMP_API_READY after isInitialized() failed', error) |
140 | | - }) |
141 | | - } |
142 | | - return () => target.removeEventListener('UC_CMP_API_READY', onReady) |
143 | | - }, { |
144 | | - signal: readyController.signal, |
145 | | - abortMessage: 'Usercentrics readiness wait was aborted', |
146 | | - }) |
147 | | - } |
148 | | - return readyPromise |
149 | | - } |
150 | | - |
151 | | - const stopAppUnmount = hookAppUnmount(cleanupReadyListener) |
152 | | - const originalRemove = instance.remove |
153 | | - instance.remove = () => { |
154 | | - cleanupReadyListener() |
155 | | - stopAppUnmount() |
156 | | - return originalRemove() |
157 | | - } |
158 | | - |
159 | | - instance.consent = { |
160 | | - whenReady, |
161 | | - onConsentChange(cb) { |
162 | | - const handler = (event: Event) => cb((event as CustomEvent).detail, event) |
163 | | - target.addEventListener('UC_UI_CMP_EVENT', handler) |
164 | | - return () => target.removeEventListener('UC_UI_CMP_EVENT', handler) |
165 | | - }, |
166 | | - showFirstLayer: () => target.__ucCmp?.showFirstLayer?.(), |
167 | | - showSecondLayer: () => target.__ucCmp?.showSecondLayer?.(), |
168 | | - acceptAll: () => target.__ucCmp?.acceptAllConsents?.(), |
169 | | - denyAll: () => target.__ucCmp?.denyAllConsents?.(), |
170 | | - } |
171 | | -} |
172 | | - |
173 | 83 | /** |
174 | 84 | * Load the Usercentrics CMP v3 ("Web CMP") loader and expose typed access to |
175 | 85 | * the `window.__ucCmp` programmatic API plus a `consent` helper with |
|
0 commit comments