payload-subscribers-plugin 0.0.9 → 0.0.11
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/README.md +233 -26
- package/dist/components/app/RequestMagicLink.d.ts +21 -4
- package/dist/components/app/RequestMagicLink.js +11 -56
- package/dist/components/app/RequestMagicLink.js.map +1 -1
- package/dist/components/app/RequestOrSubscribe.d.ts +21 -4
- package/dist/components/app/RequestOrSubscribe.js +5 -8
- package/dist/components/app/RequestOrSubscribe.js.map +1 -1
- package/dist/components/app/SelectOptInChannels.d.ts +26 -3
- package/dist/components/app/SelectOptInChannels.js +4 -1
- package/dist/components/app/SelectOptInChannels.js.map +1 -1
- package/dist/components/app/Subscribe.d.ts +26 -5
- package/dist/components/app/Subscribe.js +40 -73
- package/dist/components/app/Subscribe.js.map +1 -1
- package/dist/components/app/SubscriberMenu.d.ts +18 -6
- package/dist/components/app/SubscriberMenu.js +9 -8
- package/dist/components/app/SubscriberMenu.js.map +1 -1
- package/dist/components/app/Unsubscribe.d.ts +26 -12
- package/dist/components/app/Unsubscribe.js +63 -127
- package/dist/components/app/Unsubscribe.js.map +1 -1
- package/dist/components/app/VerifyMagicLink.d.ts +32 -14
- package/dist/components/app/VerifyMagicLink.js +60 -110
- package/dist/components/app/VerifyMagicLink.js.map +1 -1
- package/dist/contexts/SubscriberProvider.js +1 -1
- package/dist/contexts/SubscriberProvider.js.map +1 -1
- package/dist/endpoints/requestMagicLink.d.ts +6 -4
- package/dist/endpoints/requestMagicLink.js +16 -12
- package/dist/endpoints/requestMagicLink.js.map +1 -1
- package/dist/endpoints/subscribe.d.ts +6 -2
- package/dist/endpoints/subscribe.js +19 -17
- package/dist/endpoints/subscribe.js.map +1 -1
- package/dist/endpoints/unsubscribe.js +17 -15
- package/dist/endpoints/unsubscribe.js.map +1 -1
- package/dist/exports/ui.d.ts +4 -0
- package/dist/exports/ui.js +4 -0
- package/dist/exports/ui.js.map +1 -1
- package/dist/helpers/utilities.d.ts +1 -0
- package/dist/helpers/utilities.js +6 -0
- package/dist/helpers/utilities.js.map +1 -0
- package/dist/hooks/useRequestMagicLink.d.ts +35 -0
- package/dist/hooks/useRequestMagicLink.js +61 -0
- package/dist/hooks/useRequestMagicLink.js.map +1 -0
- package/dist/hooks/useSubscribe.d.ts +38 -0
- package/dist/hooks/useSubscribe.js +62 -0
- package/dist/hooks/useSubscribe.js.map +1 -0
- package/dist/hooks/useUnsubscribe.d.ts +43 -0
- package/dist/hooks/useUnsubscribe.js +86 -0
- package/dist/hooks/useUnsubscribe.js.map +1 -0
- package/dist/hooks/useVerifyMagicLink.d.ts +31 -0
- package/dist/hooks/useVerifyMagicLink.js +74 -0
- package/dist/hooks/useVerifyMagicLink.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +12 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,10 +50,13 @@ export default buildConfig({
|
|
|
50
50
|
|
|
51
51
|
// Provide a custom expiration for magic link tokens. The default is 30 minutes.
|
|
52
52
|
tokenExpiration: 60 * 60,
|
|
53
|
-
|
|
54
|
-
// Provide your unsubscribe route. This route should include the Unsubscribe component.
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
|
|
54
|
+
// Provide your unsubscribe route. This route should include the Unsubscribe component. If not provided, your payload config must have serverURL defined, and the default will be serverURL+'/unsubscribe'
|
|
55
|
+
unsubscribeURL?: string
|
|
56
|
+
|
|
57
|
+
// Provide your verify route. This route should include the Unsubscribe component. If not provided, your payload config must have serverURL defined, and the default will be serverURL+'/verify'
|
|
58
|
+
verifyURL?: string
|
|
59
|
+
}),
|
|
57
60
|
],
|
|
58
61
|
})
|
|
59
62
|
```
|
|
@@ -127,7 +130,7 @@ You can specify collections in the plugin options which will be amended to inclu
|
|
|
127
130
|
|
|
128
131
|
#### **tokenExpiration**
|
|
129
132
|
|
|
130
|
-
###
|
|
133
|
+
### 🔵 Collections
|
|
131
134
|
|
|
132
135
|
#### **optInChannels**
|
|
133
136
|
|
|
@@ -153,7 +156,7 @@ Seeded when plugin inits.
|
|
|
153
156
|
|
|
154
157
|
---
|
|
155
158
|
|
|
156
|
-
###
|
|
159
|
+
### 🔴 Fields
|
|
157
160
|
|
|
158
161
|
#### **OptedInChannels**
|
|
159
162
|
|
|
@@ -163,7 +166,7 @@ This is the same field used by the plugin **collections** to amended a relationT
|
|
|
163
166
|
|
|
164
167
|
---
|
|
165
168
|
|
|
166
|
-
###
|
|
169
|
+
### 🟢 Payload endpoints
|
|
167
170
|
|
|
168
171
|
#### **requestMagicLink**
|
|
169
172
|
|
|
@@ -181,19 +184,193 @@ Returns all active optInChannels data.
|
|
|
181
184
|
|
|
182
185
|
Takes an email and list of optInChannel IDs, verifies them, and if the authenticated subscriber matches the email will update the channels that subscriber is opted into.
|
|
183
186
|
|
|
184
|
-
####
|
|
187
|
+
#### **unsubscribe**
|
|
185
188
|
|
|
186
|
-
The **
|
|
189
|
+
The **unsubscribe** endpoint sets the subscriber status to "unsubscribed".
|
|
187
190
|
|
|
188
191
|
---
|
|
189
192
|
|
|
190
|
-
###
|
|
193
|
+
### 🔵 SubscriberProvider provider with useSubscriber context
|
|
194
|
+
|
|
195
|
+
**SubscriberProvider** fetches and holds the current subscriber auth state (via POST /api/subscriberAuth) and exposes it to the tree. Any component that uses **useSubscriber()**, or the plugin’s client components (RequestOrSubscribe, RequestMagicLink, Subscribe, etc.), or the plugin's client hooks (useRequestMagicLink, useSubscribe, etc.) must be a descendant of this provider.
|
|
196
|
+
|
|
197
|
+
The context value includes:
|
|
198
|
+
|
|
199
|
+
- **subscriber** — The current authenticated subscriber (or `null` if not logged in).
|
|
200
|
+
- **isLoaded** — `true` once the initial auth check has completed.
|
|
201
|
+
- **permissions** — Permissions returned from the auth endpoint (if any).
|
|
202
|
+
- **refreshSubscriber** — Call to refetch the current subscriber (e.g. after verifying a magic link or updating preferences).
|
|
203
|
+
- **logOut** — Calls POST /api/logout and clears subscriber state.
|
|
204
|
+
|
|
205
|
+
Wrap your app (or the part that uses subscriber features) with **SubscriberProvider**, then use **useSubscriber** in child components:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// layout.tsx (or root layout)
|
|
209
|
+
|
|
210
|
+
import { SubscriberProvider } from 'payload-subscribers-plugin/ui'
|
|
211
|
+
|
|
212
|
+
export default function Layout({ children }: { children: React.ReactNode }) {
|
|
213
|
+
return (
|
|
214
|
+
<html lang="en">
|
|
215
|
+
<body>
|
|
216
|
+
<SubscriberProvider>
|
|
217
|
+
{children}
|
|
218
|
+
</SubscriberProvider>
|
|
219
|
+
</body>
|
|
220
|
+
</html>
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// Any descendant component
|
|
227
|
+
|
|
228
|
+
import { useSubscriber } from 'payload-subscribers-plugin/ui'
|
|
229
|
+
|
|
230
|
+
function MyComponent() {
|
|
231
|
+
const { isLoaded, logOut, refreshSubscriber, subscriber } = useSubscriber()
|
|
232
|
+
|
|
233
|
+
if (!isLoaded) return <p>Loading…</p>
|
|
234
|
+
if (!subscriber) return <p>Not signed in.</p>
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div>
|
|
238
|
+
<p>Signed in as {subscriber.email}</p>
|
|
239
|
+
<button type="button" onClick={() => void refreshSubscriber()}>
|
|
240
|
+
Refresh
|
|
241
|
+
</button>
|
|
242
|
+
<button type="button" onClick={() => void logOut()}>
|
|
243
|
+
Log out
|
|
244
|
+
</button>
|
|
245
|
+
</div>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Note:** `useSubscriber` throws if used outside a `SubscriberProvider`. Ensure the provider is mounted above any component that calls it.
|
|
191
251
|
|
|
192
252
|
---
|
|
193
253
|
|
|
194
|
-
###
|
|
254
|
+
### 🔴 Client hooks
|
|
195
255
|
|
|
196
|
-
|
|
256
|
+
Use these hooks inside components that are descendants of **SubscriberProvider**. They call the plugin endpoints and expose state and callbacks for building custom UI.
|
|
257
|
+
|
|
258
|
+
#### **useRequestMagicLink**
|
|
259
|
+
|
|
260
|
+
Requests a magic-login link by email (POST /api/emailToken). Exposes `sendMagicLink`, plus `result` and `status` for rendering messages and loading state.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { useRequestMagicLink } from 'payload-subscribers-plugin/ui'
|
|
264
|
+
|
|
265
|
+
function MySignInForm() {
|
|
266
|
+
const { result, sendMagicLink, status } = useRequestMagicLink({
|
|
267
|
+
handleMagicLinkRequested: (response) => console.log('Link sent', response),
|
|
268
|
+
verifyData: `forwardURL=${typeof window !== 'undefined' ? window.location.href : ''}`,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
272
|
+
e.preventDefault()
|
|
273
|
+
const email = new FormData(e.currentTarget).get('email') as string
|
|
274
|
+
void sendMagicLink(email)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<form onSubmit={handleSubmit}>
|
|
279
|
+
<input name="email" type="email" required />
|
|
280
|
+
<button type="submit" disabled={status === 'sending'}>
|
|
281
|
+
{status === 'sending' ? 'Sending…' : 'Request magic link'}
|
|
282
|
+
</button>
|
|
283
|
+
{result && <p className={status === 'error' ? 'error' : ''}>{result}</p>}
|
|
284
|
+
</form>
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### **useVerifyMagicLink**
|
|
290
|
+
|
|
291
|
+
Handles the verify step of the magic-link flow: reads `email` and `token` from URL search params, calls POST /api/verifyToken, and refreshes the subscriber on success. Takes no options.
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { useEffect } from 'react'
|
|
295
|
+
import { useVerifyMagicLink } from 'payload-subscribers-plugin/ui'
|
|
296
|
+
|
|
297
|
+
function VerifyPage() {
|
|
298
|
+
const { isError, isLoading, result, verify } = useVerifyMagicLink()
|
|
299
|
+
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
void verify()
|
|
302
|
+
}, [verify])
|
|
303
|
+
|
|
304
|
+
if (isLoading) return <p>Verifying…</p>
|
|
305
|
+
return <p className={isError ? 'error' : ''}>{result || 'Done.'}</p>
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### **useSubscribe**
|
|
310
|
+
|
|
311
|
+
Updates the current subscriber’s opt-in channels (POST /api/subscribe). Exposes `updateSubscriptions`, plus `subscriber`, `result`, and `status`. Use with **SubscriberProvider** so `subscriber` and refresh are available.
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import { useSubscribe } from 'payload-subscribers-plugin/ui'
|
|
315
|
+
|
|
316
|
+
function MyPreferencesForm() {
|
|
317
|
+
const { result, status, subscriber, updateSubscriptions } = useSubscribe({
|
|
318
|
+
handleSubscribe: (response) => console.log('Updated', response),
|
|
319
|
+
verifyData: `forwardURL=${typeof window !== 'undefined' ? window.location.href : ''}`,
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
const handleSave = (e: React.FormEvent) => {
|
|
323
|
+
e.preventDefault()
|
|
324
|
+
const channelIds = ['channel-id-1', 'channel-id-2'] // from your form state
|
|
325
|
+
void updateSubscriptions(channelIds)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<form onSubmit={handleSave}>
|
|
330
|
+
{/* your channel checkboxes, etc. */}
|
|
331
|
+
<button type="submit" disabled={status === 'updating'}>
|
|
332
|
+
{status === 'updating' ? 'Saving…' : 'Save choices'}
|
|
333
|
+
</button>
|
|
334
|
+
{result && <p>{result}</p>}
|
|
335
|
+
</form>
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
#### **useUnsubscribe**
|
|
341
|
+
|
|
342
|
+
Calls POST /api/unsubscribe with email and token (from the hook args or from subscriber context). For use on unsubscribe pages linked from emails.
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { useEffect } from 'react'
|
|
346
|
+
import { useSearchParams } from 'next/navigation'
|
|
347
|
+
import { useUnsubscribe } from 'payload-subscribers-plugin/ui'
|
|
348
|
+
|
|
349
|
+
function UnsubscribePage() {
|
|
350
|
+
const searchParams = useSearchParams()
|
|
351
|
+
const { isError, isLoading, result, unsubscribe } = useUnsubscribe({
|
|
352
|
+
handleUnsubscribe: (response) => console.log('Unsubscribed', response),
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// With email/hash from URL (e.g. from email link)
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
const email = searchParams.get('email')
|
|
358
|
+
const hash = searchParams.get('hash')
|
|
359
|
+
if (email && hash) void unsubscribe({ email, hash })
|
|
360
|
+
}, [searchParams, unsubscribe])
|
|
361
|
+
|
|
362
|
+
if (isLoading) return <p>Unsubscribing…</p>
|
|
363
|
+
return <p className={isError ? 'error' : ''}>{result}</p>
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
### 🟢 Client components
|
|
370
|
+
|
|
371
|
+
The plugin provides several NextJS client components ready for use in a frontend app
|
|
372
|
+
|
|
373
|
+
- All App Components are client components that consume hooks, server components, server functions. Including the useSubscriber context, and so they must be used within the children descendent tree of the SubscriberProvider provider.
|
|
197
374
|
|
|
198
375
|
- All App Components accept a **classNames** prop to specify CSS class names to add to the different parts of the component
|
|
199
376
|
|
|
@@ -223,14 +400,14 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
|
|
|
223
400
|
handleMagicLinkRequested={async (result: RequestMagicLinkResponse) => {}}
|
|
224
401
|
// Called after a subscribers opt-ins have been updated. Optional
|
|
225
402
|
handleSubscribe={async (result: SubscribeResponse) => {}}
|
|
226
|
-
//
|
|
403
|
+
// Provide your own button component. Optional
|
|
227
404
|
renderButton={({ name, onClick, text }) =>
|
|
228
405
|
<button name={name} onClick={onClick} type="button">
|
|
229
406
|
{text}
|
|
230
407
|
</button>
|
|
231
408
|
}
|
|
232
|
-
// Provide
|
|
233
|
-
|
|
409
|
+
// Provide a payload of data to put on any verify link sent by either Request or Subscribe components
|
|
410
|
+
verifyData={`forwardURL=${window.location.href}`}
|
|
234
411
|
/>
|
|
235
412
|
```
|
|
236
413
|
|
|
@@ -261,8 +438,8 @@ Form to input email address and get a magic link email sent.
|
|
|
261
438
|
{text}
|
|
262
439
|
</button>
|
|
263
440
|
}
|
|
264
|
-
// Provide
|
|
265
|
-
|
|
441
|
+
// Provide a payload of data to put on any verify link sent
|
|
442
|
+
verifyData={`forwardURL=${window.location.href}`}
|
|
266
443
|
/>
|
|
267
444
|
```
|
|
268
445
|
|
|
@@ -304,13 +481,11 @@ Component that verifies a magic link using expected url parameters.
|
|
|
304
481
|
{text}
|
|
305
482
|
</button>
|
|
306
483
|
}
|
|
307
|
-
// Provide
|
|
308
|
-
|
|
309
|
-
// when verifying the current one fails.
|
|
310
|
-
verifyUrl={verifyUrl}
|
|
484
|
+
// Provide a payload of data to put on "request another" link sent
|
|
485
|
+
verifyData={`forwardURL=${window.location.href}`}
|
|
311
486
|
>
|
|
312
487
|
// Provide children to render after link is verified. Optional
|
|
313
|
-
// Since you provide the
|
|
488
|
+
// Since you provide the verifyURL to any of the plugin components, you can include a forwardUrl
|
|
314
489
|
// as a search param, which your route can then use here.
|
|
315
490
|
<a href={forwardUrl}>
|
|
316
491
|
<button className={'customCss'} name={'continue'} type="button">
|
|
@@ -361,8 +536,8 @@ Allows a subscriber to select from among all active optInChannels.
|
|
|
361
536
|
{text}
|
|
362
537
|
</button>
|
|
363
538
|
}
|
|
364
|
-
// Provide
|
|
365
|
-
|
|
539
|
+
// Provide a payload of data to put on any verify link sent
|
|
540
|
+
verifyData={`forwardURL=${window.location.href}`}
|
|
366
541
|
/>
|
|
367
542
|
```
|
|
368
543
|
|
|
@@ -410,7 +585,7 @@ A simple user menu, most useful for testing. Seen in the screenshots above. Incl
|
|
|
410
585
|
button: 'customCssClassNames',
|
|
411
586
|
container: 'customCssClassNames',
|
|
412
587
|
}}
|
|
413
|
-
|
|
588
|
+
subscribeURL={new URL('/subscribe', serverURL)}
|
|
414
589
|
/>
|
|
415
590
|
|
|
416
591
|
```
|
|
@@ -421,7 +596,7 @@ A simple user menu, most useful for testing. Seen in the screenshots above. Incl
|
|
|
421
596
|
<div class="subscribers-group">
|
|
422
597
|
<div class="subscribers-welcome">Welcome, {subscriber email}</div>
|
|
423
598
|
<div class="subscribers-subs-link">
|
|
424
|
-
<a href="{
|
|
599
|
+
<a href="{subscribeURL}">Manage subscriptions</a>
|
|
425
600
|
</div>
|
|
426
601
|
<div class="subscribers-logout">
|
|
427
602
|
<button class="subscribers-button" type="button">Log out</button>
|
|
@@ -430,6 +605,38 @@ A simple user menu, most useful for testing. Seen in the screenshots above. Incl
|
|
|
430
605
|
</div>
|
|
431
606
|
```
|
|
432
607
|
|
|
608
|
+
#### **Unsubscribe**
|
|
609
|
+
|
|
610
|
+
A component that uses URL parameters to execute the /api/unsubscribe end point. Should be used on your own route, as specified in the **unsubscribeURL** plugin option.
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// Unsubscribe with a custom "resubscribe" button
|
|
614
|
+
|
|
615
|
+
<Unsubscribe
|
|
616
|
+
classNames={{ button: 'customCss', container: 'customCss', emailInput: 'customCss' }}
|
|
617
|
+
handleUnsubscribe={handleUnsubscribe}
|
|
618
|
+
>
|
|
619
|
+
// <!-- children are rendered after unsubscribe is successful -->
|
|
620
|
+
<a href={'/subscribe'}>
|
|
621
|
+
<button name={'resubscribe'} type="button">
|
|
622
|
+
Resubscribe
|
|
623
|
+
</button>
|
|
624
|
+
</a>
|
|
625
|
+
</Unsubscribe>
|
|
626
|
+
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
```html
|
|
630
|
+
<!-- The HTML scaffolding of the built-in render layout, with global CSS classes you can use -->
|
|
631
|
+
<div class="subscribers-container">
|
|
632
|
+
<!-- While loading -->
|
|
633
|
+
<p class="subscribers-loading">unsubscribing...</p>
|
|
634
|
+
<!-- After loading -->
|
|
635
|
+
<p class="subscribers-message">{result}</p>
|
|
636
|
+
<div class="subscribers-form">{children}</div>
|
|
637
|
+
</div>
|
|
638
|
+
```
|
|
639
|
+
|
|
433
640
|
## Contributing
|
|
434
641
|
|
|
435
642
|
Community contributions are welcome! I haven't organized around that yet, so let me know if you're interested by opening an issue on the GitHub repo.
|
|
@@ -2,14 +2,28 @@ import type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.
|
|
|
2
2
|
export { RequestMagicLinkResponse };
|
|
3
3
|
/**
|
|
4
4
|
* Props for the RequestMagicLink component.
|
|
5
|
+
*
|
|
6
|
+
* @property classNames - Optional CSS class overrides for the component elements
|
|
7
|
+
* @property handleMagicLinkRequested - Callback when a magic link is requested
|
|
8
|
+
* @property props - Optional passthrough props (reserved for future use)
|
|
9
|
+
* @property verifyData - Optional data for magic-link verification (e.g. passed from URL)
|
|
5
10
|
*/
|
|
6
11
|
export interface IRequestMagicLink {
|
|
7
12
|
classNames?: RequestMagicLinkClasses;
|
|
8
13
|
handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
|
|
9
14
|
props?: any;
|
|
10
|
-
|
|
15
|
+
verifyData?: string;
|
|
11
16
|
}
|
|
12
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Optional CSS class overrides for RequestMagicLink elements.
|
|
19
|
+
*
|
|
20
|
+
* @property button - Class for the submit button
|
|
21
|
+
* @property container - Class for the main container
|
|
22
|
+
* @property emailInput - Class for the email input field
|
|
23
|
+
* @property error - Class for error messages
|
|
24
|
+
* @property form - Class for the form
|
|
25
|
+
* @property message - Class for success/error message text
|
|
26
|
+
*/
|
|
13
27
|
export type RequestMagicLinkClasses = {
|
|
14
28
|
button?: string;
|
|
15
29
|
container?: string;
|
|
@@ -22,7 +36,10 @@ export type RequestMagicLinkClasses = {
|
|
|
22
36
|
* Form component that lets users request a magic-login link by email. Submits to POST /api/emailToken
|
|
23
37
|
* and shows success or error message. Uses SubscriberProvider for pre-filling email when available.
|
|
24
38
|
*
|
|
25
|
-
* @param props -
|
|
39
|
+
* @param props - Component props (see IRequestMagicLink)
|
|
40
|
+
* @param props.classNames - Optional class overrides for the component elements
|
|
41
|
+
* @param props.handleMagicLinkRequested - Callback when a magic link is requested
|
|
42
|
+
* @param props.verifyData - Optional data to send to the magic-link verification (e.g. passed from URL)
|
|
26
43
|
* @returns Form UI with email input and "Request magic link" button
|
|
27
44
|
*/
|
|
28
|
-
export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested,
|
|
45
|
+
export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested, verifyData, }: IRequestMagicLink) => import("react").JSX.Element;
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
4
|
import { useSubscriber } from '../../contexts/SubscriberProvider.js';
|
|
5
|
-
import {
|
|
5
|
+
import { useRequestMagicLink } from '../../hooks/useRequestMagicLink.js';
|
|
6
6
|
import { mergeClassNames } from './helpers.js';
|
|
7
7
|
import styles from './shared.module.css';
|
|
8
8
|
/**
|
|
9
9
|
* Form component that lets users request a magic-login link by email. Submits to POST /api/emailToken
|
|
10
10
|
* and shows success or error message. Uses SubscriberProvider for pre-filling email when available.
|
|
11
11
|
*
|
|
12
|
-
* @param props -
|
|
12
|
+
* @param props - Component props (see IRequestMagicLink)
|
|
13
|
+
* @param props.classNames - Optional class overrides for the component elements
|
|
14
|
+
* @param props.handleMagicLinkRequested - Callback when a magic link is requested
|
|
15
|
+
* @param props.verifyData - Optional data to send to the magic-link verification (e.g. passed from URL)
|
|
13
16
|
* @returns Form UI with email input and "Request magic link" button
|
|
14
17
|
*/ export const RequestMagicLink = ({ classNames = {
|
|
15
18
|
button: '',
|
|
@@ -18,19 +21,12 @@ import styles from './shared.module.css';
|
|
|
18
21
|
error: '',
|
|
19
22
|
form: '',
|
|
20
23
|
message: ''
|
|
21
|
-
}, handleMagicLinkRequested,
|
|
24
|
+
}, handleMagicLinkRequested, verifyData })=>{
|
|
22
25
|
const { subscriber } = useSubscriber();
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (typeof verifyUrl == 'string') {
|
|
29
|
-
console.log('verifyUrl STRING: ', verifyUrl);
|
|
30
|
-
verifyUrl = new URL(verifyUrl);
|
|
31
|
-
}
|
|
32
|
-
const [status, setStatus] = useState('default');
|
|
33
|
-
const [result, setResult] = useState();
|
|
26
|
+
const { result, sendMagicLink, status } = useRequestMagicLink({
|
|
27
|
+
handleMagicLinkRequested,
|
|
28
|
+
verifyData
|
|
29
|
+
});
|
|
34
30
|
const [email, setEmail] = useState(subscriber?.email || '');
|
|
35
31
|
useEffect(()=>{
|
|
36
32
|
setEmail(subscriber?.email || '');
|
|
@@ -39,48 +35,7 @@ import styles from './shared.module.css';
|
|
|
39
35
|
]);
|
|
40
36
|
const handleSubmit = async (e)=>{
|
|
41
37
|
e.preventDefault();
|
|
42
|
-
|
|
43
|
-
setStatus('error');
|
|
44
|
-
setResult(`An error occured. Please try again. \n(No verify URL available.)`);
|
|
45
|
-
} else {
|
|
46
|
-
// const emailTokenResponse = await sdk.request({
|
|
47
|
-
// json: {
|
|
48
|
-
// email,
|
|
49
|
-
// verifyUrl: verifyUrl?.href,
|
|
50
|
-
// },
|
|
51
|
-
// method: 'POST',
|
|
52
|
-
// path: '/api/emailToken',
|
|
53
|
-
// })
|
|
54
|
-
const emailTokenResponse = await fetch(`${serverURL ? serverURL : ''}/api/emailToken`, {
|
|
55
|
-
body: JSON.stringify({
|
|
56
|
-
email,
|
|
57
|
-
verifyUrl: verifyUrl?.href
|
|
58
|
-
}),
|
|
59
|
-
method: 'POST'
|
|
60
|
-
});
|
|
61
|
-
if (emailTokenResponse.ok) {
|
|
62
|
-
const emailTokenResponseJson = await emailTokenResponse.json();
|
|
63
|
-
if (handleMagicLinkRequested) {
|
|
64
|
-
handleMagicLinkRequested(emailTokenResponseJson);
|
|
65
|
-
}
|
|
66
|
-
// @ts-expect-error One or the other exists
|
|
67
|
-
const { emailResult, error } = emailTokenResponseJson;
|
|
68
|
-
if (error) {
|
|
69
|
-
setStatus('error');
|
|
70
|
-
setResult(`An error occured. Please try again. \n ${error}`);
|
|
71
|
-
} else if (emailResult) {
|
|
72
|
-
setStatus('sent');
|
|
73
|
-
setResult('An email has been sent containing your magic link.');
|
|
74
|
-
} else {
|
|
75
|
-
setStatus('error');
|
|
76
|
-
setResult(`An error occured. Please try again. \nResult unknown`);
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
const emailTokenResponseText = await emailTokenResponse.text();
|
|
80
|
-
setStatus('error');
|
|
81
|
-
setResult(`An error occured. Please try again. \n${emailTokenResponseText}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
38
|
+
await sendMagicLink(email);
|
|
84
39
|
};
|
|
85
40
|
return /*#__PURE__*/ _jsxs("div", {
|
|
86
41
|
className: mergeClassNames([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/app/RequestMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/components/app/RequestMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { type ChangeEvent, type SubmitEvent, useEffect, useState } from 'react'\n\nimport type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useRequestMagicLink } from '../../hooks/useRequestMagicLink.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\nexport { RequestMagicLinkResponse }\n\n/**\n * Props for the RequestMagicLink component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property handleMagicLinkRequested - Callback when a magic link is requested\n * @property props - Optional passthrough props (reserved for future use)\n * @property verifyData - Optional data for magic-link verification (e.g. passed from URL)\n */\nexport interface IRequestMagicLink {\n classNames?: RequestMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n props?: any\n verifyData?: string\n}\n\n/**\n * Optional CSS class overrides for RequestMagicLink elements.\n *\n * @property button - Class for the submit button\n * @property container - Class for the main container\n * @property emailInput - Class for the email input field\n * @property error - Class for error messages\n * @property form - Class for the form\n * @property message - Class for success/error message text\n */\nexport type RequestMagicLinkClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n message?: string\n}\n\n/**\n * Form component that lets users request a magic-login link by email. Submits to POST /api/emailToken\n * and shows success or error message. Uses SubscriberProvider for pre-filling email when available.\n *\n * @param props - Component props (see IRequestMagicLink)\n * @param props.classNames - Optional class overrides for the component elements\n * @param props.handleMagicLinkRequested - Callback when a magic link is requested\n * @param props.verifyData - Optional data to send to the magic-link verification (e.g. passed from URL)\n * @returns Form UI with email input and \"Request magic link\" button\n */\nexport const RequestMagicLink = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n message: '',\n },\n handleMagicLinkRequested,\n verifyData,\n}: IRequestMagicLink) => {\n const { subscriber } = useSubscriber()\n const { result, sendMagicLink, status } = useRequestMagicLink({\n handleMagicLinkRequested,\n verifyData,\n })\n\n const [email, setEmail] = useState(subscriber?.email || '')\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n }, [subscriber])\n\n const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault()\n\n await sendMagicLink(email)\n }\n return (\n <div\n className={mergeClassNames([\n 'subscribers-request subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n {result ? (\n <p\n className={mergeClassNames([\n 'subscribers-message',\n styles.message,\n classNames.message,\n status == 'error' ? ['subscribers-error', styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n ) : (\n <></>\n )}\n <form\n className={mergeClassNames(['subscribers-form', styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={handleSubmit}\n >\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([\n 'subscribers-emailInput',\n styles.emailInput,\n classNames.emailInput,\n ])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n type=\"submit\"\n >\n Request magic link\n </button>\n </form>\n </div>\n )\n}\n"],"names":["useEffect","useState","useSubscriber","useRequestMagicLink","mergeClassNames","styles","RequestMagicLink","classNames","button","container","emailInput","error","form","message","handleMagicLinkRequested","verifyData","subscriber","result","sendMagicLink","status","email","setEmail","handleSubmit","e","preventDefault","div","className","p","method","onSubmit","input","aria-label","onChange","target","value","placeholder","type"],"mappings":"AAAA;;AAEA,SAA6CA,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAI/E,SAASC,aAAa,QAAQ,uCAAsC;AACpE,SAASC,mBAAmB,QAAQ,qCAAoC;AACxE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAsCxC;;;;;;;;;CASC,GACD,OAAO,MAAMC,mBAAmB,CAAC,EAC/BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,UAAU,EACQ;IAClB,MAAM,EAAEC,UAAU,EAAE,GAAGd;IACvB,MAAM,EAAEe,MAAM,EAAEC,aAAa,EAAEC,MAAM,EAAE,GAAGhB,oBAAoB;QAC5DW;QACAC;IACF;IAEA,MAAM,CAACK,OAAOC,SAAS,GAAGpB,SAASe,YAAYI,SAAS;IAExDpB,UAAU;QACRqB,SAASL,YAAYI,SAAS;IAChC,GAAG;QAACJ;KAAW;IAEf,MAAMM,eAAe,OAAOC;QAC1BA,EAAEC,cAAc;QAEhB,MAAMN,cAAcE;IACtB;IACA,qBACE,MAACK;QACCC,WAAWtB,gBAAgB;YACzB;YACAC,OAAOI,SAAS;YAChBF,WAAWE,SAAS;SACrB;;YAEAQ,uBACC,KAACU;gBACCD,WAAWtB,gBAAgB;oBACzB;oBACAC,OAAOQ,OAAO;oBACdN,WAAWM,OAAO;oBAClBM,UAAU,UAAU;wBAAC;wBAAqBd,OAAOM,KAAK;wBAAEJ,WAAWI,KAAK;qBAAC,GAAG,EAAE;iBAC/E;0BAEAM;+BAGH;0BAEF,MAACL;gBACCc,WAAWtB,gBAAgB;oBAAC;oBAAoBC,OAAOO,IAAI;oBAAEL,WAAWK,IAAI;iBAAC;gBAC7EgB,QAAO;gBACPC,UAAUP;;kCAEV,KAACQ;wBACCC,cAAW;wBACXL,WAAWtB,gBAAgB;4BACzB;4BACAC,OAAOK,UAAU;4BACjBH,WAAWG,UAAU;yBACtB;wBACDsB,UAAU,CAACT,IAAqCF,SAASE,EAAEU,MAAM,CAACC,KAAK;wBACvEC,aAAY;wBACZC,MAAK;wBACLF,OAAOd;;kCAET,KAACZ;wBACCkB,WAAWtB,gBAAgB;4BAAC;4BAAsBC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBACnF4B,MAAK;kCACN;;;;;;AAMT,EAAC"}
|
|
@@ -2,14 +2,30 @@ import { type RequestMagicLinkResponse, type SubscribeResponse } from '../../exp
|
|
|
2
2
|
export type { RequestMagicLinkResponse, SubscribeResponse };
|
|
3
3
|
/**
|
|
4
4
|
* Props for the RequestOrSubscribe component.
|
|
5
|
+
*
|
|
6
|
+
* @property classNames - Optional CSS class overrides for the component and its children
|
|
7
|
+
* @property handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)
|
|
8
|
+
* @property handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)
|
|
9
|
+
* @property verifyData - Optional data passed to child components (e.g. for magic-link verification)
|
|
5
10
|
*/
|
|
6
11
|
export interface IRequestOrSubscribe {
|
|
7
12
|
classNames?: RequestOrSubscribeClasses;
|
|
8
13
|
handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
|
|
9
14
|
handleSubscribe?: (result: SubscribeResponse) => void;
|
|
10
|
-
|
|
15
|
+
verifyData?: string;
|
|
11
16
|
}
|
|
12
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Optional CSS class overrides for RequestOrSubscribe and its child components.
|
|
19
|
+
*
|
|
20
|
+
* @property button - Class for buttons
|
|
21
|
+
* @property container - Class for the main container
|
|
22
|
+
* @property emailInput - Class for the email input field
|
|
23
|
+
* @property error - Class for error messages
|
|
24
|
+
* @property form - Class for forms
|
|
25
|
+
* @property loading - Class for loading state
|
|
26
|
+
* @property message - Class for message text
|
|
27
|
+
* @property section - Class for section wrappers
|
|
28
|
+
*/
|
|
13
29
|
export type RequestOrSubscribeClasses = {
|
|
14
30
|
button?: string;
|
|
15
31
|
container?: string;
|
|
@@ -24,10 +40,11 @@ export type RequestOrSubscribeClasses = {
|
|
|
24
40
|
* Composite component that shows Subscribe when a subscriber is authenticated, otherwise
|
|
25
41
|
* RequestMagicLink. Used as a single entry point for "sign in or manage subscriptions."
|
|
26
42
|
*
|
|
43
|
+
* @param props - Component props (see IRequestOrSubscribe)
|
|
27
44
|
* @param props.classNames - Optional class overrides passed to child components
|
|
28
45
|
* @param props.handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)
|
|
29
46
|
* @param props.handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)
|
|
30
|
-
* @param props.
|
|
47
|
+
* @param props.verifyData - Optional data passed to child components (e.g. for magic-link verification)
|
|
31
48
|
* @returns Either Subscribe or RequestMagicLink based on subscriber context
|
|
32
49
|
*/
|
|
33
|
-
export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe,
|
|
50
|
+
export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe, verifyData, }: IRequestOrSubscribe): import("react").JSX.Element;
|
|
@@ -6,10 +6,11 @@ import { RequestMagicLink, Subscribe } from '../../exports/ui.js';
|
|
|
6
6
|
* Composite component that shows Subscribe when a subscriber is authenticated, otherwise
|
|
7
7
|
* RequestMagicLink. Used as a single entry point for "sign in or manage subscriptions."
|
|
8
8
|
*
|
|
9
|
+
* @param props - Component props (see IRequestOrSubscribe)
|
|
9
10
|
* @param props.classNames - Optional class overrides passed to child components
|
|
10
11
|
* @param props.handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)
|
|
11
12
|
* @param props.handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)
|
|
12
|
-
* @param props.
|
|
13
|
+
* @param props.verifyData - Optional data passed to child components (e.g. for magic-link verification)
|
|
13
14
|
* @returns Either Subscribe or RequestMagicLink based on subscriber context
|
|
14
15
|
*/ export function RequestOrSubscribe({ classNames = {
|
|
15
16
|
button: '',
|
|
@@ -20,21 +21,17 @@ import { RequestMagicLink, Subscribe } from '../../exports/ui.js';
|
|
|
20
21
|
loading: '',
|
|
21
22
|
message: '',
|
|
22
23
|
section: ''
|
|
23
|
-
}, handleMagicLinkRequested, handleSubscribe,
|
|
24
|
-
if (typeof verifyUrl == 'string') {
|
|
25
|
-
verifyUrl = new URL(verifyUrl);
|
|
26
|
-
}
|
|
24
|
+
}, handleMagicLinkRequested, handleSubscribe, verifyData }) {
|
|
27
25
|
const { subscriber } = useSubscriber();
|
|
28
|
-
// Example: Conditionally render something or pass the state to children
|
|
29
26
|
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
30
27
|
children: subscriber ? /*#__PURE__*/ _jsx(Subscribe, {
|
|
31
28
|
classNames: classNames,
|
|
32
29
|
handleSubscribe: handleSubscribe,
|
|
33
|
-
|
|
30
|
+
verifyData: verifyData
|
|
34
31
|
}) : /*#__PURE__*/ _jsx(RequestMagicLink, {
|
|
35
32
|
classNames: classNames,
|
|
36
33
|
handleMagicLinkRequested: handleMagicLinkRequested,
|
|
37
|
-
|
|
34
|
+
verifyData: verifyData
|
|
38
35
|
})
|
|
39
36
|
});
|
|
40
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport {\n RequestMagicLink,\n type RequestMagicLinkResponse,\n Subscribe,\n type SubscribeResponse,\n} from '../../exports/ui.js'\n\nexport type { RequestMagicLinkResponse, SubscribeResponse }\n\n/**\n * Props for the RequestOrSubscribe component.\n */\nexport interface IRequestOrSubscribe {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n
|
|
1
|
+
{"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport {\n RequestMagicLink,\n type RequestMagicLinkResponse,\n Subscribe,\n type SubscribeResponse,\n} from '../../exports/ui.js'\n\nexport type { RequestMagicLinkResponse, SubscribeResponse }\n\n/**\n * Props for the RequestOrSubscribe component.\n *\n * @property classNames - Optional CSS class overrides for the component and its children\n * @property handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)\n * @property handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)\n * @property verifyData - Optional data passed to child components (e.g. for magic-link verification)\n */\nexport interface IRequestOrSubscribe {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n verifyData?: string\n}\n\n/**\n * Optional CSS class overrides for RequestOrSubscribe and its child components.\n *\n * @property button - Class for buttons\n * @property container - Class for the main container\n * @property emailInput - Class for the email input field\n * @property error - Class for error messages\n * @property form - Class for forms\n * @property loading - Class for loading state\n * @property message - Class for message text\n * @property section - Class for section wrappers\n */\nexport type RequestOrSubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\n/**\n * Composite component that shows Subscribe when a subscriber is authenticated, otherwise\n * RequestMagicLink. Used as a single entry point for \"sign in or manage subscriptions.\"\n *\n * @param props - Component props (see IRequestOrSubscribe)\n * @param props.classNames - Optional class overrides passed to child components\n * @param props.handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)\n * @param props.handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)\n * @param props.verifyData - Optional data passed to child components (e.g. for magic-link verification)\n * @returns Either Subscribe or RequestMagicLink based on subscriber context\n */\nexport function RequestOrSubscribe({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleMagicLinkRequested,\n handleSubscribe,\n verifyData,\n}: IRequestOrSubscribe) {\n const { subscriber } = useSubscriber()\n\n return (\n <>\n {subscriber ? (\n <Subscribe\n classNames={classNames}\n handleSubscribe={handleSubscribe}\n verifyData={verifyData}\n />\n ) : (\n <RequestMagicLink\n classNames={classNames}\n handleMagicLinkRequested={handleMagicLinkRequested}\n verifyData={verifyData}\n />\n )}\n {/* <div>subscriber = {JSON.stringify(subscriber)}</div> */}\n </>\n )\n}\n"],"names":["useSubscriber","RequestMagicLink","Subscribe","RequestOrSubscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleMagicLinkRequested","handleSubscribe","verifyData","subscriber"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SACEC,gBAAgB,EAEhBC,SAAS,QAEJ,sBAAqB;AA0C5B;;;;;;;;;;CAUC,GACD,OAAO,SAASC,mBAAmB,EACjCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,eAAe,EACfC,UAAU,EACU;IACpB,MAAM,EAAEC,UAAU,EAAE,GAAGhB;IAEvB,qBACE;kBACGgB,2BACC,KAACd;YACCE,YAAYA;YACZU,iBAAiBA;YACjBC,YAAYA;2BAGd,KAACd;YACCG,YAAYA;YACZS,0BAA0BA;YAC1BE,YAAYA;;;AAMtB"}
|