payload-subscribers-plugin 0.0.11 → 0.0.13
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 +2 -31
- package/dist/components/app/SelectOptInChannels.d.ts +3 -2
- package/dist/components/app/SelectOptInChannels.js +11 -32
- package/dist/components/app/SelectOptInChannels.js.map +1 -1
- package/dist/components/app/Subscribe.js +2 -1
- package/dist/components/app/Subscribe.js.map +1 -1
- package/dist/contexts/SubscriberProvider.d.ts +4 -2
- package/dist/contexts/SubscriberProvider.js +3 -1
- package/dist/contexts/SubscriberProvider.js.map +1 -1
- package/dist/endpoints/getOptInChannels.d.ts +1 -1
- package/dist/endpoints/getOptInChannels.js.map +1 -1
- package/dist/endpoints/subscriberAuth.js +22 -2
- package/dist/endpoints/subscriberAuth.js.map +1 -1
- package/dist/exports/ui.d.ts +1 -0
- package/dist/exports/ui.js.map +1 -1
- package/dist/hooks/useSubscribe.d.ts +5 -2
- package/dist/hooks/useSubscribe.js +27 -1
- package/dist/hooks/useSubscribe.js.map +1 -1
- package/dist/hooks/useUnsubscribe.js +2 -1
- package/dist/hooks/useUnsubscribe.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -383,8 +383,6 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
|
|
|
383
383
|
|
|
384
384
|
```typescript
|
|
385
385
|
<RequestOrSubscribe
|
|
386
|
-
// Provide the URL the user should go to after clicking the link in the email and having it verified
|
|
387
|
-
afterVerifyUrl={new URL(window.href)}
|
|
388
386
|
// Provide your own global class names to add to the component elements. Optional
|
|
389
387
|
classNames={{
|
|
390
388
|
button: 'customCssClassNames',
|
|
@@ -400,11 +398,6 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
|
|
|
400
398
|
handleMagicLinkRequested={async (result: RequestMagicLinkResponse) => {}}
|
|
401
399
|
// Called after a subscribers opt-ins have been updated. Optional
|
|
402
400
|
handleSubscribe={async (result: SubscribeResponse) => {}}
|
|
403
|
-
// Provide your own button component. Optional
|
|
404
|
-
renderButton={({ name, onClick, text }) =>
|
|
405
|
-
<button name={name} onClick={onClick} type="button">
|
|
406
|
-
{text}
|
|
407
|
-
</button>
|
|
408
401
|
}
|
|
409
402
|
// Provide a payload of data to put on any verify link sent by either Request or Subscribe components
|
|
410
403
|
verifyData={`forwardURL=${window.location.href}`}
|
|
@@ -419,8 +412,6 @@ Form to input email address and get a magic link email sent.
|
|
|
419
412
|
|
|
420
413
|
```typescript
|
|
421
414
|
<RequestMagicLink
|
|
422
|
-
// Provide the URL the user should go to after clicking the link in the email and having it verified
|
|
423
|
-
afterVerifyUrl={new URL(window.href)}
|
|
424
415
|
// Provide your own global class names to add to the component elements. Optional
|
|
425
416
|
classNames={{
|
|
426
417
|
button: 'customCssClassNames',
|
|
@@ -432,12 +423,6 @@ Form to input email address and get a magic link email sent.
|
|
|
432
423
|
}}
|
|
433
424
|
// Called after a subscribers opt-ins have been updated. Optional
|
|
434
425
|
handleMagicLinkRequested={async (result: RequestMagicLinkResponse) => {}}
|
|
435
|
-
// Provided your own button component. Optional
|
|
436
|
-
renderButton={({ name, onClick, text }) =>
|
|
437
|
-
<button name={name} onClick={onClick} type="button">
|
|
438
|
-
{text}
|
|
439
|
-
</button>
|
|
440
|
-
}
|
|
441
426
|
// Provide a payload of data to put on any verify link sent
|
|
442
427
|
verifyData={`forwardURL=${window.location.href}`}
|
|
443
428
|
/>
|
|
@@ -475,12 +460,6 @@ Component that verifies a magic link using expected url parameters.
|
|
|
475
460
|
handleMagicLinkRequested={async (result: RequestMagicLinkResponse) => {}}
|
|
476
461
|
// Called after a magic link has been verified. Optional
|
|
477
462
|
handleMagicLinkVerified={async (result: RequestMagicLinkResponse) => {}}
|
|
478
|
-
// Provided your own button component. Optional
|
|
479
|
-
renderButton={({ name, onClick, text }) =>
|
|
480
|
-
<button name={name} onClick={onClick} type="button">
|
|
481
|
-
{text}
|
|
482
|
-
</button>
|
|
483
|
-
}
|
|
484
463
|
// Provide a payload of data to put on "request another" link sent
|
|
485
464
|
verifyData={`forwardURL=${window.location.href}`}
|
|
486
465
|
>
|
|
@@ -501,8 +480,8 @@ Component that verifies a magic link using expected url parameters.
|
|
|
501
480
|
<p class="subscribers-loading">verifying...</p>
|
|
502
481
|
<p class="subscribers-message">{result}</p>
|
|
503
482
|
<div class="subscribers-form">
|
|
504
|
-
|
|
505
|
-
|
|
483
|
+
<!-- Form elements render here, before the component children provided -->
|
|
484
|
+
{children}
|
|
506
485
|
</div>
|
|
507
486
|
</div>
|
|
508
487
|
```
|
|
@@ -515,8 +494,6 @@ Allows a subscriber to select from among all active optInChannels.
|
|
|
515
494
|
|
|
516
495
|
```typescript
|
|
517
496
|
<Subscribe
|
|
518
|
-
// Provide the URL the user should go to after clicking the link in the email and having it verified
|
|
519
|
-
afterVerifyUrl={new URL(window.href)}
|
|
520
497
|
// Provide your own global class names to add to the component elements. Optional
|
|
521
498
|
classNames={{
|
|
522
499
|
button: 'customCssClassNames',
|
|
@@ -530,12 +507,6 @@ Allows a subscriber to select from among all active optInChannels.
|
|
|
530
507
|
}}
|
|
531
508
|
// Called after a subscribers opt-ins have been updated. Optional
|
|
532
509
|
handleSubscribe={async (result: SubscribeResponse) => {}}
|
|
533
|
-
// Provided your own button component. Optional
|
|
534
|
-
renderButton={({ name, onClick, text }) =>
|
|
535
|
-
<button name={name} onClick={onClick} type="button">
|
|
536
|
-
{text}
|
|
537
|
-
</button>
|
|
538
|
-
}
|
|
539
510
|
// Provide a payload of data to put on any verify link sent
|
|
540
511
|
verifyData={`forwardURL=${window.location.href}`}
|
|
541
512
|
/>
|
|
@@ -9,7 +9,8 @@ import type { OptInChannel } from '../../copied/payload-types.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export interface ISelectOptInChannels {
|
|
11
11
|
classNames?: SelectOptInChannelsClasses;
|
|
12
|
-
handleOptInChannelsSelected?: (
|
|
12
|
+
handleOptInChannelsSelected?: (isLoaded: OptInChannel[]) => void;
|
|
13
|
+
optInChannels?: OptInChannel[];
|
|
13
14
|
props?: any;
|
|
14
15
|
selectedOptInChannelIDs?: string[];
|
|
15
16
|
}
|
|
@@ -49,4 +50,4 @@ export type SelectOptInChannelsClasses = {
|
|
|
49
50
|
* @param props.selectedOptInChannelIDs - Optional channel IDs to pre-select
|
|
50
51
|
* @returns Section titled "Opt-in Channels" with checkboxes and loading/error state
|
|
51
52
|
*/
|
|
52
|
-
export declare const SelectOptInChannels: ({ classNames, handleOptInChannelsSelected, selectedOptInChannelIDs, }: ISelectOptInChannels) => import("react").JSX.Element;
|
|
53
|
+
export declare const SelectOptInChannels: ({ classNames, handleOptInChannelsSelected, optInChannels, selectedOptInChannelIDs, }: ISelectOptInChannels) => import("react").JSX.Element;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { PayloadSDK } from '@payloadcms/sdk';
|
|
4
3
|
import { useEffect, useState } from 'react';
|
|
5
|
-
import { useServerUrl } from '../../react-hooks/useServerUrl.js';
|
|
6
4
|
import { mergeClassNames } from './helpers.js';
|
|
7
5
|
import styles from './shared.module.css';
|
|
8
6
|
/**
|
|
@@ -25,40 +23,21 @@ import styles from './shared.module.css';
|
|
|
25
23
|
optInCheckboxItem: '',
|
|
26
24
|
optInCheckboxLabel: '',
|
|
27
25
|
optionsGroup: ''
|
|
28
|
-
}, handleOptInChannelsSelected, selectedOptInChannelIDs })=>{
|
|
29
|
-
const
|
|
30
|
-
const [result, setResult] = useState();
|
|
26
|
+
}, handleOptInChannelsSelected, optInChannels, selectedOptInChannelIDs })=>{
|
|
27
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
31
28
|
const [allOptInChannels, setAllOptInChannels] = useState([]);
|
|
32
29
|
useEffect(()=>{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
baseURL: serverURL || ''
|
|
36
|
-
});
|
|
37
|
-
console.log('calling optinchannels endpoint');
|
|
38
|
-
const result = await sdk.request({
|
|
39
|
-
method: 'GET',
|
|
40
|
-
path: '/api/optinchannels'
|
|
41
|
-
});
|
|
42
|
-
if (result.ok) {
|
|
43
|
-
const resultJson = await result.json();
|
|
44
|
-
setResult(resultJson);
|
|
45
|
-
} else {
|
|
46
|
-
const resultText = await result.text();
|
|
47
|
-
setResult(resultText);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
void verify();
|
|
51
|
-
}, [
|
|
52
|
-
serverURL
|
|
53
|
-
]);
|
|
54
|
-
useEffect(()=>{
|
|
55
|
-
const channels = result?.optInChannels?.map((channel)=>({
|
|
30
|
+
setIsLoaded(false);
|
|
31
|
+
const channels = optInChannels?.map((channel)=>({
|
|
56
32
|
...channel,
|
|
57
33
|
isChecked: selectedOptInChannelIDs?.includes(channel.id)
|
|
58
34
|
}));
|
|
59
|
-
|
|
35
|
+
if (channels) {
|
|
36
|
+
setAllOptInChannels(channels);
|
|
37
|
+
}
|
|
38
|
+
setIsLoaded(true);
|
|
60
39
|
}, [
|
|
61
|
-
|
|
40
|
+
optInChannels,
|
|
62
41
|
selectedOptInChannelIDs
|
|
63
42
|
]);
|
|
64
43
|
return /*#__PURE__*/ _jsxs("div", {
|
|
@@ -70,12 +49,12 @@ import styles from './shared.module.css';
|
|
|
70
49
|
/*#__PURE__*/ _jsx("h3", {
|
|
71
50
|
children: "Opt-in Channels"
|
|
72
51
|
}),
|
|
73
|
-
!
|
|
52
|
+
!isLoaded ? /*#__PURE__*/ _jsx("p", {
|
|
74
53
|
className: mergeClassNames([
|
|
75
54
|
styles.loading,
|
|
76
55
|
classNames.loading
|
|
77
56
|
]),
|
|
78
|
-
children: "
|
|
57
|
+
children: "loading..."
|
|
79
58
|
}) : /*#__PURE__*/ _jsx("div", {
|
|
80
59
|
className: mergeClassNames([
|
|
81
60
|
styles.optionsGroup,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/app/SelectOptInChannels.tsx"],"sourcesContent":["'use client'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/components/app/SelectOptInChannels.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport type { OptInChannel } from '../../copied/payload-types.js'\n\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n/**\n * Props for the SelectOptInChannels component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property handleOptInChannelsSelected - Callback with the list of selected opt-in channels\n * @property props - Optional passthrough props (reserved for future use)\n * @property selectedOptInChannelIDs - Optional channel IDs to pre-select\n */\nexport interface ISelectOptInChannels {\n classNames?: SelectOptInChannelsClasses\n handleOptInChannelsSelected?: (isLoaded: OptInChannel[]) => void\n optInChannels?: OptInChannel[]\n props?: any\n selectedOptInChannelIDs?: string[]\n}\n\n/**\n * Optional CSS class overrides for SelectOptInChannels elements.\n *\n * @property button - Class for buttons\n * @property container - Class for the main container\n * @property error - Class for error messages\n * @property form - Class for the form\n * @property loading - Class for loading state\n * @property message - Class for message text\n * @property optInCheckbox - Class for each checkbox input\n * @property optInCheckboxItem - Class for each checkbox row/item\n * @property optInCheckboxLabel - Class for checkbox labels\n * @property optionsGroup - Class for the group wrapping all checkboxes\n */\nexport type SelectOptInChannelsClasses = {\n button?: string\n container?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n optInCheckbox?: string\n optInCheckboxItem?: string\n optInCheckboxLabel?: string\n optionsGroup?: string\n}\n\n/**\n * Fetches active opt-in channels from GET /api/optinchannels and renders a list of checkboxes.\n * Reports selected channels via handleOptInChannelsSelected. Supports pre-selection via selectedOptInChannelIDs.\n *\n * @param props - Component props (see ISelectOptInChannels)\n * @param props.classNames - Optional class overrides for the component elements\n * @param props.handleOptInChannelsSelected - Callback with the list of selected opt-in channels\n * @param props.selectedOptInChannelIDs - Optional channel IDs to pre-select\n * @returns Section titled \"Opt-in Channels\" with checkboxes and loading/error state\n */\nexport const SelectOptInChannels = ({\n classNames = {\n button: '',\n container: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n optInCheckbox: '',\n optInCheckboxItem: '',\n optInCheckboxLabel: '',\n optionsGroup: '',\n },\n handleOptInChannelsSelected,\n optInChannels,\n selectedOptInChannelIDs,\n}: ISelectOptInChannels) => {\n // const { serverURL } = { serverURL: 'http://localhost:3001' }\n type OptInChannelCheckbox = {\n isChecked?: boolean\n } & OptInChannel\n const [isLoaded, setIsLoaded] = useState<boolean>(false)\n const [allOptInChannels, setAllOptInChannels] = useState<OptInChannelCheckbox[]>([])\n\n useEffect(() => {\n setIsLoaded(false)\n const channels = optInChannels?.map((channel: OptInChannel) => ({\n ...channel,\n isChecked: selectedOptInChannelIDs?.includes(channel.id),\n }))\n if (channels) {\n setAllOptInChannels(channels)\n }\n setIsLoaded(true)\n }, [optInChannels, selectedOptInChannelIDs])\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h3>Opt-in Channels</h3>\n {!isLoaded ? (\n <p className={mergeClassNames([styles.loading, classNames.loading])}>loading...</p>\n ) : (\n <div className={mergeClassNames([styles.optionsGroup, classNames.optionsGroup])}>\n {// Map over the tasks array to render each checkbox\n allOptInChannels?.map((channel) => (\n <div\n className={mergeClassNames([styles.optInCheckboxItem, classNames.optInCheckboxItem])}\n key={channel.id}\n >\n <label\n className={mergeClassNames([\n styles.optInCheckboxLabel,\n classNames.optInCheckboxLabel,\n ])}\n >\n <input\n aria-label={channel.title}\n // The checked prop is controlled by the state\n checked={channel.isChecked}\n className={mergeClassNames([styles.optInCheckbox, classNames.optInCheckbox])}\n // The onChange handler calls the update function with the item's ID\n onChange={(event) => {\n event.preventDefault()\n\n const checked = event.target.checked\n\n if (handleOptInChannelsSelected) {\n handleOptInChannelsSelected(\n allOptInChannels\n .map((channel) => ({\n ...channel,\n isChecked:\n channel.title == event.target.value ? checked : channel.isChecked,\n }))\n .filter((c) => c.isChecked)\n .map((channel) => ({ ...channel, isChecked: undefined })),\n )\n }\n }}\n type=\"checkbox\"\n value={channel.title}\n />\n {channel.title}\n </label>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"names":["useEffect","useState","mergeClassNames","styles","SelectOptInChannels","classNames","button","container","error","form","loading","message","optInCheckbox","optInCheckboxItem","optInCheckboxLabel","optionsGroup","handleOptInChannelsSelected","optInChannels","selectedOptInChannelIDs","isLoaded","setIsLoaded","allOptInChannels","setAllOptInChannels","channels","map","channel","isChecked","includes","id","div","className","h3","p","label","input","aria-label","title","checked","onChange","event","preventDefault","target","value","filter","c","undefined","type"],"mappings":"AAAA;;AAEA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAI3C,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AA6CxC;;;;;;;;;CASC,GACD,OAAO,MAAMC,sBAAsB,CAAC,EAClCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,eAAe;IACfC,mBAAmB;IACnBC,oBAAoB;IACpBC,cAAc;AAChB,CAAC,EACDC,2BAA2B,EAC3BC,aAAa,EACbC,uBAAuB,EACF;IAKrB,MAAM,CAACC,UAAUC,YAAY,GAAGnB,SAAkB;IAClD,MAAM,CAACoB,kBAAkBC,oBAAoB,GAAGrB,SAAiC,EAAE;IAEnFD,UAAU;QACRoB,YAAY;QACZ,MAAMG,WAAWN,eAAeO,IAAI,CAACC,UAA2B,CAAA;gBAC9D,GAAGA,OAAO;gBACVC,WAAWR,yBAAyBS,SAASF,QAAQG,EAAE;YACzD,CAAA;QACA,IAAIL,UAAU;YACZD,oBAAoBC;QACtB;QACAH,YAAY;IACd,GAAG;QAACH;QAAeC;KAAwB;IAE3C,qBACE,MAACW;QAAIC,WAAW5B,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;;0BACtE,KAACwB;0BAAG;;YACH,CAACZ,yBACA,KAACa;gBAAEF,WAAW5B,gBAAgB;oBAACC,OAAOO,OAAO;oBAAEL,WAAWK,OAAO;iBAAC;0BAAG;+BAErE,KAACmB;gBAAIC,WAAW5B,gBAAgB;oBAACC,OAAOY,YAAY;oBAAEV,WAAWU,YAAY;iBAAC;0BAE5EM,kBAAkBG,IAAI,CAACC,wBACrB,KAACI;wBACCC,WAAW5B,gBAAgB;4BAACC,OAAOU,iBAAiB;4BAAER,WAAWQ,iBAAiB;yBAAC;kCAGnF,cAAA,MAACoB;4BACCH,WAAW5B,gBAAgB;gCACzBC,OAAOW,kBAAkB;gCACzBT,WAAWS,kBAAkB;6BAC9B;;8CAED,KAACoB;oCACCC,cAAYV,QAAQW,KAAK;oCACzB,8CAA8C;oCAC9CC,SAASZ,QAAQC,SAAS;oCAC1BI,WAAW5B,gBAAgB;wCAACC,OAAOS,aAAa;wCAAEP,WAAWO,aAAa;qCAAC;oCAC3E,oEAAoE;oCACpE0B,UAAU,CAACC;wCACTA,MAAMC,cAAc;wCAEpB,MAAMH,UAAUE,MAAME,MAAM,CAACJ,OAAO;wCAEpC,IAAIrB,6BAA6B;4CAC/BA,4BACEK,iBACGG,GAAG,CAAC,CAACC,UAAa,CAAA;oDACjB,GAAGA,OAAO;oDACVC,WACED,QAAQW,KAAK,IAAIG,MAAME,MAAM,CAACC,KAAK,GAAGL,UAAUZ,QAAQC,SAAS;gDACrE,CAAA,GACCiB,MAAM,CAAC,CAACC,IAAMA,EAAElB,SAAS,EACzBF,GAAG,CAAC,CAACC,UAAa,CAAA;oDAAE,GAAGA,OAAO;oDAAEC,WAAWmB;gDAAU,CAAA;wCAE5D;oCACF;oCACAC,MAAK;oCACLJ,OAAOjB,QAAQW,KAAK;;gCAErBX,QAAQW,KAAK;;;uBAnCXX,QAAQG,EAAE;;;;AA2C7B,EAAC"}
|
|
@@ -36,7 +36,7 @@ import styles from './shared.module.css';
|
|
|
36
36
|
handleMagicLinkRequested,
|
|
37
37
|
verifyData
|
|
38
38
|
});
|
|
39
|
-
const { result: subscribeResult, status: subscribeStatus, subscriber, updateSubscriptions } = useSubscribe({
|
|
39
|
+
const { optInChannels, result: subscribeResult, status: subscribeStatus, subscriber, updateSubscriptions } = useSubscribe({
|
|
40
40
|
handleSubscribe,
|
|
41
41
|
verifyData
|
|
42
42
|
});
|
|
@@ -77,6 +77,7 @@ import styles from './shared.module.css';
|
|
|
77
77
|
}),
|
|
78
78
|
/*#__PURE__*/ _jsx(SelectOptInChannels, {
|
|
79
79
|
handleOptInChannelsSelected: handleOptInChannelsSelected,
|
|
80
|
+
optInChannels: optInChannels,
|
|
80
81
|
selectedOptInChannelIDs: selectedChannelIDs
|
|
81
82
|
})
|
|
82
83
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { OptInChannel, Subscriber } from '../../copied/payload-types.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport type { RequestMagicLinkStatusValue } from '../../hooks/useRequestMagicLink.js'\nimport type { UpdateSubscriptionStatusValue } from '../../hooks/useSubscribe.js'\n\nimport { useRequestMagicLink } from '../../hooks/useRequestMagicLink.js'\nimport { useSubscribe } from '../../hooks/useSubscribe.js'\nimport { useUnsubscribe } from '../../hooks/useUnsubscribe.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.js'\nimport styles from './shared.module.css'\n\n/**\n * Props for the Subscribe component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property handleSubscribe - Callback when subscription/opt-ins are updated\n * @property props - Optional passthrough props (reserved for future use)\n * @property verifyData - Optional data for verification (e.g. passed from magic-link URL)\n */\nexport interface ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n verifyData?: string\n}\n\n/**\n * Optional CSS class overrides for Subscribe elements.\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 the form\n * @property loading - Class for loading state\n * @property message - Class for status message text\n * @property section - Class for section wrappers\n */\nexport type SubscribeClasses = {\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 * Subscribe/preferences form for authenticated subscribers. Shows SelectOptInChannels and an email\n * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger\n * verification email. Calls refreshSubscriber and handleSubscribe on success.\n *\n * @param props - Component props (see ISubscribe)\n * @param props.classNames - Optional class overrides for the component elements\n * @param props.handleSubscribe - Callback when subscription/opt-ins are updated\n * @param props.verifyData - Optional data for verification (e.g. passed from magic-link URL)\n * @returns Form with channel checkboxes, optional email field, \"Save choices\" button, and status message\n */\nexport const Subscribe = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleSubscribe,\n verifyData,\n}: ISubscribe) => {\n const { unsubscribe } = useUnsubscribe({ handleUnsubscribe: () => {} })\n\n const [email, setEmail] = useState<string>('')\n\n const handleMagicLinkRequested = () => {}\n const {\n result: requestResult,\n sendMagicLink,\n status: requestStatus,\n } = useRequestMagicLink({\n handleMagicLinkRequested,\n verifyData,\n })\n const {\n result: subscribeResult,\n status: subscribeStatus,\n subscriber,\n updateSubscriptions,\n } = useSubscribe({\n handleSubscribe,\n verifyData,\n })\n\n const flattenChannels = (channels: (OptInChannel | string)[] | null | undefined) => {\n if (!channels) {\n return []\n }\n return channels.map((channel: OptInChannel | string) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n\n const [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\n }\n\n return (\n <div\n className={mergeClassNames([\n 'subscribers-subscribe subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n <h2>Subscribe</h2>\n <div className={mergeClassNames(['subscribers-section', styles.section, classNames.section])}>\n {subscriber?.status == 'unsubscribed' && <p>You are unsubscribed</p>}\n <SelectOptInChannels\n handleOptInChannelsSelected={handleOptInChannelsSelected}\n selectedOptInChannelIDs={selectedChannelIDs}\n />\n </div>\n <form\n className={mergeClassNames(['subscribers-form', styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={async (e) => {\n e.preventDefault()\n if (subscriber) {\n await updateSubscriptions(selectedChannelIDs)\n } else {\n await sendMagicLink(email)\n }\n }}\n >\n <div\n className={mergeClassNames(['subscribers-section', styles.section, classNames.section])}\n >\n {!subscriber && (\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 )}\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n type=\"submit\"\n >\n {!subscriber && <>Subscribe</>}\n {subscriber && subscriber?.status != 'unsubscribed' && <>Save choices</>}\n {subscriber?.status == 'unsubscribed' && <>Subscribe and save choices</>}\n </button>\n {subscriber && subscriber?.status != 'unsubscribed' && (\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n onClick={async () => {\n await unsubscribe()\n }}\n type=\"button\"\n >\n Unsubscribe from all\n </button>\n )}\n </div>\n </form>\n {(!!requestResult || !!subscribeResult) && (\n <p\n className={mergeClassNames([\n 'subscribers-message',\n styles.message,\n classNames.message,\n requestStatus == 'error' || subscribeStatus == 'error'\n ? ['subscribers-error', styles.error, classNames.error]\n : [],\n ])}\n >\n {requestResult || subscribeResult}\n </p>\n )}\n </div>\n )\n}\n"],"names":["useEffect","useState","useRequestMagicLink","useSubscribe","useUnsubscribe","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","verifyData","unsubscribe","handleUnsubscribe","email","setEmail","handleMagicLinkRequested","result","requestResult","sendMagicLink","status","requestStatus","subscribeResult","subscribeStatus","subscriber","updateSubscriptions","flattenChannels","channels","map","channel","id","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","div","className","h2","p","selectedOptInChannelIDs","method","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","onClick"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { OptInChannel, Subscriber } from '../../copied/payload-types.js'\nimport type { GetOptInChannelsResponse } from '../../endpoints/getOptInChannels.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport type { RequestMagicLinkStatusValue } from '../../hooks/useRequestMagicLink.js'\nimport type { UpdateSubscriptionStatusValue } from '../../hooks/useSubscribe.js'\n\nimport { useRequestMagicLink } from '../../hooks/useRequestMagicLink.js'\nimport { useSubscribe } from '../../hooks/useSubscribe.js'\nimport { useUnsubscribe } from '../../hooks/useUnsubscribe.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.js'\nimport styles from './shared.module.css'\n\n/**\n * Props for the Subscribe component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property handleSubscribe - Callback when subscription/opt-ins are updated\n * @property props - Optional passthrough props (reserved for future use)\n * @property verifyData - Optional data for verification (e.g. passed from magic-link URL)\n */\nexport interface ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n verifyData?: string\n}\n\n/**\n * Optional CSS class overrides for Subscribe elements.\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 the form\n * @property loading - Class for loading state\n * @property message - Class for status message text\n * @property section - Class for section wrappers\n */\nexport type SubscribeClasses = {\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 * Subscribe/preferences form for authenticated subscribers. Shows SelectOptInChannels and an email\n * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger\n * verification email. Calls refreshSubscriber and handleSubscribe on success.\n *\n * @param props - Component props (see ISubscribe)\n * @param props.classNames - Optional class overrides for the component elements\n * @param props.handleSubscribe - Callback when subscription/opt-ins are updated\n * @param props.verifyData - Optional data for verification (e.g. passed from magic-link URL)\n * @returns Form with channel checkboxes, optional email field, \"Save choices\" button, and status message\n */\nexport const Subscribe = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleSubscribe,\n verifyData,\n}: ISubscribe) => {\n const { unsubscribe } = useUnsubscribe({ handleUnsubscribe: () => {} })\n\n const [email, setEmail] = useState<string>('')\n\n const handleMagicLinkRequested = () => {}\n const {\n result: requestResult,\n sendMagicLink,\n status: requestStatus,\n } = useRequestMagicLink({\n handleMagicLinkRequested,\n verifyData,\n })\n const {\n optInChannels,\n result: subscribeResult,\n status: subscribeStatus,\n subscriber,\n updateSubscriptions,\n } = useSubscribe({\n handleSubscribe,\n verifyData,\n })\n\n const flattenChannels = (channels: (OptInChannel | string)[] | null | undefined) => {\n if (!channels) {\n return []\n }\n return channels.map((channel: OptInChannel | string) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n\n const [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\n }\n\n return (\n <div\n className={mergeClassNames([\n 'subscribers-subscribe subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n <h2>Subscribe</h2>\n <div className={mergeClassNames(['subscribers-section', styles.section, classNames.section])}>\n {subscriber?.status == 'unsubscribed' && <p>You are unsubscribed</p>}\n <SelectOptInChannels\n handleOptInChannelsSelected={handleOptInChannelsSelected}\n optInChannels={optInChannels}\n selectedOptInChannelIDs={selectedChannelIDs}\n />\n </div>\n <form\n className={mergeClassNames(['subscribers-form', styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={async (e) => {\n e.preventDefault()\n if (subscriber) {\n await updateSubscriptions(selectedChannelIDs)\n } else {\n await sendMagicLink(email)\n }\n }}\n >\n <div\n className={mergeClassNames(['subscribers-section', styles.section, classNames.section])}\n >\n {!subscriber && (\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 )}\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n type=\"submit\"\n >\n {!subscriber && <>Subscribe</>}\n {subscriber && subscriber?.status != 'unsubscribed' && <>Save choices</>}\n {subscriber?.status == 'unsubscribed' && <>Subscribe and save choices</>}\n </button>\n {subscriber && subscriber?.status != 'unsubscribed' && (\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n onClick={async () => {\n await unsubscribe()\n }}\n type=\"button\"\n >\n Unsubscribe from all\n </button>\n )}\n </div>\n </form>\n {(!!requestResult || !!subscribeResult) && (\n <p\n className={mergeClassNames([\n 'subscribers-message',\n styles.message,\n classNames.message,\n requestStatus == 'error' || subscribeStatus == 'error'\n ? ['subscribers-error', styles.error, classNames.error]\n : [],\n ])}\n >\n {requestResult || subscribeResult}\n </p>\n )}\n </div>\n )\n}\n"],"names":["useEffect","useState","useRequestMagicLink","useSubscribe","useUnsubscribe","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","verifyData","unsubscribe","handleUnsubscribe","email","setEmail","handleMagicLinkRequested","result","requestResult","sendMagicLink","status","requestStatus","optInChannels","subscribeResult","subscribeStatus","subscriber","updateSubscriptions","flattenChannels","channels","map","channel","id","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","div","className","h2","p","selectedOptInChannelIDs","method","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","onClick"],"mappings":"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"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
import type { Subscriber } from '../copied/payload-types.js';
|
|
2
|
+
import type { OptInChannel, Subscriber } from '../copied/payload-types.js';
|
|
3
3
|
/** Value provided by SubscriberProvider: current subscriber, auth state, and actions. */
|
|
4
4
|
export type SubscriberContextType = {
|
|
5
5
|
isLoaded: boolean;
|
|
6
6
|
logOut: () => void;
|
|
7
7
|
permissions: any;
|
|
8
8
|
refreshSubscriber: () => void;
|
|
9
|
-
subscriber:
|
|
9
|
+
subscriber: ({
|
|
10
|
+
optIns?: null | OptInChannel[];
|
|
11
|
+
} & Omit<Subscriber, 'optIns'>) | null;
|
|
10
12
|
};
|
|
11
13
|
/** Props for SubscriberProvider. */
|
|
12
14
|
interface ProviderProps {
|
|
@@ -28,7 +28,9 @@ const SubscriberContext = /*#__PURE__*/ createContext(undefined);
|
|
|
28
28
|
});
|
|
29
29
|
if (authResponse.ok) {
|
|
30
30
|
// Call the server function to get the user data
|
|
31
|
-
const
|
|
31
|
+
const authResponseJson = await authResponse.json();
|
|
32
|
+
// console.log('authResponseJson', JSON.stringify(authResponseJson, undefined, 2))
|
|
33
|
+
const { permissions, subscriber } = authResponseJson;
|
|
32
34
|
// console.log(`subscriber = `, subscriber)
|
|
33
35
|
// console.log(`permissions = `, permissions)
|
|
34
36
|
setPermissions(permissions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/contexts/SubscriberProvider.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ReactNode, useCallback, useEffect } from 'react'\nimport { createContext, useContext, useMemo, useState } from 'react'\n\nimport type { Config, Subscriber } from '../copied/payload-types.js'\n\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\n/** Value provided by SubscriberProvider: current subscriber, auth state, and actions. */\nexport type SubscriberContextType = {\n isLoaded: boolean\n logOut: () => void\n permissions: any\n refreshSubscriber: () => void\n subscriber: null | Subscriber\n}\n\nconst SubscriberContext = createContext<SubscriberContextType | undefined>(undefined)\n\n/** Props for SubscriberProvider. */\ninterface ProviderProps {\n children?: ReactNode\n}\n\n/**\n * Provider that fetches and holds the current subscriber auth state (via POST /api/subscriberAuth).\n * Exposes subscriber, permissions, refreshSubscriber, and logOut to descendants. Must wrap any\n * component that uses useSubscriber().\n *\n * @param props.children - React tree to wrap\n * @returns SubscriberContext.Provider with current auth state and actions\n */\nexport function SubscriberProvider({ children }: ProviderProps) {\n // eslint-disable-next-line\n const [subscriber, setSubscriber] = useState<null | (Subscriber & { optIns:
|
|
1
|
+
{"version":3,"sources":["../../src/contexts/SubscriberProvider.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ReactNode, useCallback, useEffect } from 'react'\nimport { createContext, useContext, useMemo, useState } from 'react'\n\nimport type { Config, OptInChannel, Subscriber } from '../copied/payload-types.js'\n\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\n/** Value provided by SubscriberProvider: current subscriber, auth state, and actions. */\nexport type SubscriberContextType = {\n isLoaded: boolean\n logOut: () => void\n permissions: any\n refreshSubscriber: () => void\n subscriber: ({ optIns?: null | OptInChannel[] } & Omit<Subscriber, 'optIns'>) | null\n}\n\nconst SubscriberContext = createContext<SubscriberContextType | undefined>(undefined)\n\n/** Props for SubscriberProvider. */\ninterface ProviderProps {\n children?: ReactNode\n}\n\n/**\n * Provider that fetches and holds the current subscriber auth state (via POST /api/subscriberAuth).\n * Exposes subscriber, permissions, refreshSubscriber, and logOut to descendants. Must wrap any\n * component that uses useSubscriber().\n *\n * @param props.children - React tree to wrap\n * @returns SubscriberContext.Provider with current auth state and actions\n */\nexport function SubscriberProvider({ children }: ProviderProps) {\n // eslint-disable-next-line\n const [subscriber, setSubscriber] = useState<null | (Subscriber & { optIns: OptInChannel[] })>(\n null,\n )\n\n const { serverURL } = useServerUrl()\n\n // Keep track of if the selection content is loaded yet\n const [isLoaded, setIsLoaded] = useState(false)\n\n const [permissions, setPermissions] = useState<any>()\n\n const refreshSubscriber = useCallback(async () => {\n const initSubscriber = async () => {\n setIsLoaded(false)\n try {\n const authResponse = await fetch('/api/subscriberAuth', {\n // body: JSON.stringify({}),\n method: 'POST',\n })\n\n if (authResponse.ok) {\n // Call the server function to get the user data\n const authResponseJson = await authResponse.json()\n // console.log('authResponseJson', JSON.stringify(authResponseJson, undefined, 2))\n const { permissions, subscriber } = authResponseJson\n // console.log(`subscriber = `, subscriber)\n // console.log(`permissions = `, permissions)\n setPermissions(permissions)\n setSubscriber(subscriber)\n } else {\n setPermissions(null)\n setSubscriber(null)\n }\n } catch (error: unknown) {\n console.log(`authResponse error`, error)\n }\n setIsLoaded(true)\n }\n await initSubscriber()\n }, [serverURL])\n\n const logOut = useCallback(async () => {\n setIsLoaded(false)\n try {\n // const sdk = new PayloadSDK<Config>({\n // baseURL: serverURL || '',\n // })\n // const logoutResponse = await sdk.request({\n // json: {},\n // method: 'POST',\n // path: '/api/logout',\n // })\n // Unsure why sdk isn't working here\n const logoutResponse = await fetch('/api/logout', {\n method: 'POST',\n })\n\n // console.log(`logoutResponse`, logoutResponse)\n\n if (logoutResponse.ok) {\n setSubscriber(null)\n setPermissions(null)\n }\n } catch (error: unknown) {\n console.log(`logoutResponse error`, error)\n }\n setIsLoaded(true)\n }, [])\n\n useEffect(() => {\n void refreshSubscriber()\n }, [refreshSubscriber])\n\n // Memoize the value to prevent unnecessary re-renders in consumers\n const contextValue: SubscriberContextType = useMemo(\n () => ({\n isLoaded,\n logOut,\n permissions,\n refreshSubscriber,\n subscriber,\n }),\n [isLoaded, logOut, permissions, refreshSubscriber, subscriber],\n )\n\n return <SubscriberContext.Provider value={contextValue}>{children}</SubscriberContext.Provider>\n}\n\n/**\n * Consumes SubscriberContext. Use only inside a SubscriberProvider.\n *\n * @returns Current subscriber (or null), permissions, isLoaded, refreshSubscriber, and logOut\n * @throws Error if used outside SubscriberProvider\n */\nexport function useSubscriber() {\n const context = useContext(SubscriberContext)\n if (context === undefined) {\n throw new Error('useSubscriber must be used within a SubscriberProvider')\n }\n return context\n}\n"],"names":["useCallback","useEffect","createContext","useContext","useMemo","useState","useServerUrl","SubscriberContext","undefined","SubscriberProvider","children","subscriber","setSubscriber","serverURL","isLoaded","setIsLoaded","permissions","setPermissions","refreshSubscriber","initSubscriber","authResponse","fetch","method","ok","authResponseJson","json","error","console","log","logOut","logoutResponse","contextValue","Provider","value","useSubscriber","context","Error"],"mappings":"AAAA;;AAGA,SAAyBA,WAAW,EAAEC,SAAS,QAAQ,QAAO;AAC9D,SAASC,aAAa,EAAEC,UAAU,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAIpE,SAASC,YAAY,QAAQ,iCAAgC;AAW7D,MAAMC,kCAAoBL,cAAiDM;AAO3E;;;;;;;CAOC,GACD,OAAO,SAASC,mBAAmB,EAAEC,QAAQ,EAAiB;IAC5D,2BAA2B;IAC3B,MAAM,CAACC,YAAYC,cAAc,GAAGP,SAClC;IAGF,MAAM,EAAEQ,SAAS,EAAE,GAAGP;IAEtB,uDAAuD;IACvD,MAAM,CAACQ,UAAUC,YAAY,GAAGV,SAAS;IAEzC,MAAM,CAACW,aAAaC,eAAe,GAAGZ;IAEtC,MAAMa,oBAAoBlB,YAAY;QACpC,MAAMmB,iBAAiB;YACrBJ,YAAY;YACZ,IAAI;gBACF,MAAMK,eAAe,MAAMC,MAAM,uBAAuB;oBACtD,4BAA4B;oBAC5BC,QAAQ;gBACV;gBAEA,IAAIF,aAAaG,EAAE,EAAE;oBACnB,gDAAgD;oBAChD,MAAMC,mBAAmB,MAAMJ,aAAaK,IAAI;oBAChD,kFAAkF;oBAClF,MAAM,EAAET,WAAW,EAAEL,UAAU,EAAE,GAAGa;oBACpC,2CAA2C;oBAC3C,6CAA6C;oBAC7CP,eAAeD;oBACfJ,cAAcD;gBAChB,OAAO;oBACLM,eAAe;oBACfL,cAAc;gBAChB;YACF,EAAE,OAAOc,OAAgB;gBACvBC,QAAQC,GAAG,CAAC,CAAC,kBAAkB,CAAC,EAAEF;YACpC;YACAX,YAAY;QACd;QACA,MAAMI;IACR,GAAG;QAACN;KAAU;IAEd,MAAMgB,SAAS7B,YAAY;QACzBe,YAAY;QACZ,IAAI;YACF,uCAAuC;YACvC,8BAA8B;YAC9B,KAAK;YACL,6CAA6C;YAC7C,cAAc;YACd,oBAAoB;YACpB,yBAAyB;YACzB,KAAK;YACL,oCAAoC;YACpC,MAAMe,iBAAiB,MAAMT,MAAM,eAAe;gBAChDC,QAAQ;YACV;YAEA,gDAAgD;YAEhD,IAAIQ,eAAeP,EAAE,EAAE;gBACrBX,cAAc;gBACdK,eAAe;YACjB;QACF,EAAE,OAAOS,OAAgB;YACvBC,QAAQC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAEF;QACtC;QACAX,YAAY;IACd,GAAG,EAAE;IAELd,UAAU;QACR,KAAKiB;IACP,GAAG;QAACA;KAAkB;IAEtB,mEAAmE;IACnE,MAAMa,eAAsC3B,QAC1C,IAAO,CAAA;YACLU;YACAe;YACAb;YACAE;YACAP;QACF,CAAA,GACA;QAACG;QAAUe;QAAQb;QAAaE;QAAmBP;KAAW;IAGhE,qBAAO,KAACJ,kBAAkByB,QAAQ;QAACC,OAAOF;kBAAerB;;AAC3D;AAEA;;;;;CAKC,GACD,OAAO,SAASwB;IACd,MAAMC,UAAUhC,WAAWI;IAC3B,IAAI4B,YAAY3B,WAAW;QACzB,MAAM,IAAI4B,MAAM;IAClB;IACA,OAAOD;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/getOptInChannels.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/getOptInChannels.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'\n\nimport type { OptInChannel } from '../copied/payload-types.js'\n\nimport { OptInChannels as OptInChannelCollection } from '../collections/OptInChannels.js'\n\nexport type GetOptInChannelsResponse =\n | {\n error: string\n now: string\n }\n | {\n now: string\n optInChannels: OptInChannel[]\n }\n\n/**\n * Payload handler for GET /optinchannels. Returns all active opt-in channels\n * for subscription preferences.\n *\n * @param req - Payload request object\n * @returns Response with `optInChannels` array on success, or `error` and `now` on failure (400)\n */\nexport const getOptInChannelsHandler: PayloadHandler = async (req) => {\n const findResults = await req.payload.find({\n collection: OptInChannelCollection.slug as CollectionSlug,\n depth: 2,\n where: {\n active: { equals: true },\n },\n })\n // .catch((error) => {\n // return Response.json({ error, now: new Date().toISOString() } as GetOptInChannelsResponse, {\n // status: 400,\n // })\n // })\n\n if (!findResults) {\n return Response.json(\n { error: 'Unknown find result', now: new Date().toISOString() } as GetOptInChannelsResponse,\n { status: 400 },\n )\n }\n\n return Response.json({\n now: new Date().toISOString(),\n optInChannels: findResults.docs,\n } as GetOptInChannelsResponse)\n}\n\n/**\n * Endpoint config for listing active opt-in channels. Mount as GET /optinchannels.\n * Used by the subscribe UI to fetch available subscription channels.\n */\nconst getOptInChannelsEndpoint: Endpoint = {\n handler: getOptInChannelsHandler,\n method: 'get',\n path: '/optinchannels',\n}\n\nexport default getOptInChannelsEndpoint\n"],"names":["OptInChannels","OptInChannelCollection","getOptInChannelsHandler","req","findResults","payload","find","collection","slug","depth","where","active","equals","Response","json","error","now","Date","toISOString","status","optInChannels","docs","getOptInChannelsEndpoint","handler","method","path"],"mappings":"AAIA,SAASA,iBAAiBC,sBAAsB,QAAQ,kCAAiC;AAYzF;;;;;;CAMC,GACD,OAAO,MAAMC,0BAA0C,OAAOC;IAC5D,MAAMC,cAAc,MAAMD,IAAIE,OAAO,CAACC,IAAI,CAAC;QACzCC,YAAYN,uBAAuBO,IAAI;QACvCC,OAAO;QACPC,OAAO;YACLC,QAAQ;gBAAEC,QAAQ;YAAK;QACzB;IACF;IACA,sBAAsB;IACtB,iGAAiG;IACjG,mBAAmB;IACnB,OAAO;IACP,KAAK;IAEL,IAAI,CAACR,aAAa;QAChB,OAAOS,SAASC,IAAI,CAClB;YAAEC,OAAO;YAAuBC,KAAK,IAAIC,OAAOC,WAAW;QAAG,GAC9D;YAAEC,QAAQ;QAAI;IAElB;IAEA,OAAON,SAASC,IAAI,CAAC;QACnBE,KAAK,IAAIC,OAAOC,WAAW;QAC3BE,eAAehB,YAAYiB,IAAI;IACjC;AACF,EAAC;AAED;;;CAGC,GACD,MAAMC,2BAAqC;IACzCC,SAASrB;IACTsB,QAAQ;IACRC,MAAM;AACR;AAEA,eAAeH,yBAAwB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { headers as nextHeaders } from 'next/headers.js';
|
|
2
|
+
import { OptInChannels as OptInChannelCollection } from '../collections/OptInChannels.js';
|
|
2
3
|
import { defaultCollectionSlug } from '../collections/Subscribers.js';
|
|
3
4
|
/**
|
|
4
5
|
* Factory that creates the subscriber-auth endpoint config and handler.
|
|
@@ -27,7 +28,26 @@ import { defaultCollectionSlug } from '../collections/Subscribers.js';
|
|
|
27
28
|
if (user && user.collection == subscribersCollectionSlug) {
|
|
28
29
|
const subscriber = user;
|
|
29
30
|
if (subscriber.optIns) {
|
|
30
|
-
subscriber.optIns = subscriber.optIns.map((channel)=>
|
|
31
|
+
// subscriber.optIns = subscriber.optIns.map((channel) =>
|
|
32
|
+
// typeof channel == 'string' ? channel : channel.id,
|
|
33
|
+
// )
|
|
34
|
+
const channelsToGet = subscriber.optIns.map((channel)=>typeof channel == 'string' ? channel : channel.id);
|
|
35
|
+
const findResults = await req.payload.find({
|
|
36
|
+
collection: OptInChannelCollection.slug,
|
|
37
|
+
depth: 2,
|
|
38
|
+
where: {
|
|
39
|
+
id: {
|
|
40
|
+
in: channelsToGet
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
if (!findResults) {
|
|
45
|
+
req.payload.logger.info(`Error getting optIns data: ${JSON.stringify({
|
|
46
|
+
error: 'Unknown find result'
|
|
47
|
+
})}`);
|
|
48
|
+
} else {
|
|
49
|
+
subscriber.optIns = findResults.docs.map((c)=>c);
|
|
50
|
+
}
|
|
31
51
|
}
|
|
32
52
|
return Response.json({
|
|
33
53
|
now: new Date().toISOString(),
|
|
@@ -43,7 +63,7 @@ import { defaultCollectionSlug } from '../collections/Subscribers.js';
|
|
|
43
63
|
subscriber: null
|
|
44
64
|
}, {
|
|
45
65
|
headers,
|
|
46
|
-
status:
|
|
66
|
+
status: 200
|
|
47
67
|
});
|
|
48
68
|
} catch (error) {
|
|
49
69
|
// req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/subscriberAuth.ts"],"sourcesContent":["import { headers as nextHeaders } from 'next/headers.js'\n\nimport type { Subscriber } from '../copied/payload-types.js'\n\n// If you're using Next.js, you'll have to import headers from next/headers, like so:\nimport type { CollectionSlug, Endpoint, PayloadHandler, Permissions } from 'payload'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nexport type SubscriberAuthResponse =\n | {\n error: string\n now: string\n }\n | {\n now: string\n permissions: Permissions\n subscriber: null | Subscriber\n }\n\n/**\n * Factory that creates the subscriber-auth endpoint config and handler.\n * Authenticates the current request via Payload auth and returns the subscriber and permissions if from the subscribers collection.\n *\n * @param options - Config options for the endpoint\n * @param options.subscribersCollectionSlug - Collection slug for subscribers (default from Subscribers collection)\n * @returns Payload Endpoint config for POST /subscriberAuth\n */\nfunction createEndpointSubscriberAuth({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * Handler for POST /subscriberAuth. Uses Payload auth (e.g. cookies) to resolve the current user;\n * if the user belongs to the subscribers collection, returns subscriber and permissions.\n *\n * @param req - Payload request (auth via headers)\n * @returns 200 with `subscriber`, `permissions`, `now` when a subscriber is authed; 400 with `subscriber: null` or `error` otherwise\n */\n const subscriberAuthHandler: PayloadHandler = async (req) => {\n // req.payload.logger.info('subscriberAuthHandler')\n // Log the user in via Payload headers\n const headers = await nextHeaders()\n\n try {\n const { permissions, user } = await req.payload.auth({\n headers,\n })\n\n // req.payload.logger.info(`user = ${JSON.stringify(user)}`)\n // req.payload.logger.info(`permissions = ${JSON.stringify(permissions)}`)\n\n if (user && user.collection == subscribersCollectionSlug) {\n const subscriber: Subscriber = user as Subscriber\n if (subscriber.optIns) {\n subscriber.optIns = subscriber.optIns.map((channel) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n return Response.json({\n now: new Date().toISOString(),\n permissions,\n subscriber,\n } as SubscriberAuthResponse)\n }\n\n // req.payload.logger.info('subscriberAuthHandler: No subscriber authed')\n return Response.json(\n {\n // error: 'No subscriber authed',\n now: new Date().toISOString(),\n permissions,\n subscriber: null,\n } as SubscriberAuthResponse,\n { headers, status:
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/subscriberAuth.ts"],"sourcesContent":["import { headers as nextHeaders } from 'next/headers.js'\n\nimport type { OptInChannel, Subscriber } from '../copied/payload-types.js'\n\nimport { OptInChannels as OptInChannelCollection } from '../collections/OptInChannels.js'\n\n// If you're using Next.js, you'll have to import headers from next/headers, like so:\nimport type { CollectionSlug, Endpoint, PayloadHandler, Permissions } from 'payload'\n\nimport type { GetOptInChannelsResponse } from './getOptInChannels.js'\n\nimport { defaultCollectionSlug } from '../collections/Subscribers.js'\n\nexport type SubscriberAuthResponse =\n | {\n error: string\n now: string\n }\n | {\n now: string\n permissions: Permissions\n subscriber: null | Subscriber\n }\n\n/**\n * Factory that creates the subscriber-auth endpoint config and handler.\n * Authenticates the current request via Payload auth and returns the subscriber and permissions if from the subscribers collection.\n *\n * @param options - Config options for the endpoint\n * @param options.subscribersCollectionSlug - Collection slug for subscribers (default from Subscribers collection)\n * @returns Payload Endpoint config for POST /subscriberAuth\n */\nfunction createEndpointSubscriberAuth({\n subscribersCollectionSlug = defaultCollectionSlug,\n}: {\n subscribersCollectionSlug: CollectionSlug\n}): Endpoint {\n /**\n * Handler for POST /subscriberAuth. Uses Payload auth (e.g. cookies) to resolve the current user;\n * if the user belongs to the subscribers collection, returns subscriber and permissions.\n *\n * @param req - Payload request (auth via headers)\n * @returns 200 with `subscriber`, `permissions`, `now` when a subscriber is authed; 400 with `subscriber: null` or `error` otherwise\n */\n const subscriberAuthHandler: PayloadHandler = async (req) => {\n // req.payload.logger.info('subscriberAuthHandler')\n // Log the user in via Payload headers\n const headers = await nextHeaders()\n\n try {\n const { permissions, user } = await req.payload.auth({\n headers,\n })\n\n // req.payload.logger.info(`user = ${JSON.stringify(user)}`)\n // req.payload.logger.info(`permissions = ${JSON.stringify(permissions)}`)\n\n if (user && user.collection == subscribersCollectionSlug) {\n const subscriber: Subscriber = user as Subscriber\n if (subscriber.optIns) {\n // subscriber.optIns = subscriber.optIns.map((channel) =>\n // typeof channel == 'string' ? channel : channel.id,\n // )\n const channelsToGet = subscriber.optIns.map((channel) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n const findResults = await req.payload.find({\n collection: OptInChannelCollection.slug as CollectionSlug,\n depth: 2,\n where: {\n id: { in: channelsToGet },\n },\n })\n\n if (!findResults) {\n req.payload.logger.info(\n `Error getting optIns data: ${JSON.stringify({\n error: 'Unknown find result',\n // now: new Date().toISOString(),\n } as GetOptInChannelsResponse)}`,\n )\n } else {\n subscriber.optIns = findResults.docs.map((c) => c as OptInChannel)\n }\n }\n return Response.json({\n now: new Date().toISOString(),\n permissions,\n subscriber,\n } as SubscriberAuthResponse)\n }\n\n // req.payload.logger.info('subscriberAuthHandler: No subscriber authed')\n return Response.json(\n {\n // error: 'No subscriber authed',\n now: new Date().toISOString(),\n permissions,\n subscriber: null,\n } as SubscriberAuthResponse,\n { headers, status: 200 },\n )\n } catch (error: unknown) {\n // req.payload.logger.info(`subscriberAuth error: ${JSON.stringify(error)}`)\n return Response.json(\n {\n error,\n now: new Date().toISOString(),\n } as SubscriberAuthResponse,\n { headers, status: 400 },\n )\n }\n }\n\n /** Endpoint config for checking current subscriber auth. Mount as POST /subscriberAuth. */\n const subscriberAuthEndpoint: Endpoint = {\n handler: subscriberAuthHandler,\n method: 'post',\n path: '/subscriberAuth',\n }\n return subscriberAuthEndpoint\n}\n\nexport default createEndpointSubscriberAuth\n"],"names":["headers","nextHeaders","OptInChannels","OptInChannelCollection","defaultCollectionSlug","createEndpointSubscriberAuth","subscribersCollectionSlug","subscriberAuthHandler","req","permissions","user","payload","auth","collection","subscriber","optIns","channelsToGet","map","channel","id","findResults","find","slug","depth","where","in","logger","info","JSON","stringify","error","docs","c","Response","json","now","Date","toISOString","status","subscriberAuthEndpoint","handler","method","path"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,kBAAiB;AAIxD,SAASC,iBAAiBC,sBAAsB,QAAQ,kCAAiC;AAOzF,SAASC,qBAAqB,QAAQ,gCAA+B;AAarE;;;;;;;CAOC,GACD,SAASC,6BAA6B,EACpCC,4BAA4BF,qBAAqB,EAGlD;IACC;;;;;;GAMC,GACD,MAAMG,wBAAwC,OAAOC;QACnD,mDAAmD;QACnD,sCAAsC;QACtC,MAAMR,UAAU,MAAMC;QAEtB,IAAI;YACF,MAAM,EAAEQ,WAAW,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,IAAI,CAAC;gBACnDZ;YACF;YAEA,4DAA4D;YAC5D,0EAA0E;YAE1E,IAAIU,QAAQA,KAAKG,UAAU,IAAIP,2BAA2B;gBACxD,MAAMQ,aAAyBJ;gBAC/B,IAAII,WAAWC,MAAM,EAAE;oBACrB,yDAAyD;oBACzD,uDAAuD;oBACvD,IAAI;oBACJ,MAAMC,gBAAgBF,WAAWC,MAAM,CAACE,GAAG,CAAC,CAACC,UAC3C,OAAOA,WAAW,WAAWA,UAAUA,QAAQC,EAAE;oBAEnD,MAAMC,cAAc,MAAMZ,IAAIG,OAAO,CAACU,IAAI,CAAC;wBACzCR,YAAYV,uBAAuBmB,IAAI;wBACvCC,OAAO;wBACPC,OAAO;4BACLL,IAAI;gCAAEM,IAAIT;4BAAc;wBAC1B;oBACF;oBAEA,IAAI,CAACI,aAAa;wBAChBZ,IAAIG,OAAO,CAACe,MAAM,CAACC,IAAI,CACrB,CAAC,2BAA2B,EAAEC,KAAKC,SAAS,CAAC;4BAC3CC,OAAO;wBAET,IAAgC;oBAEpC,OAAO;wBACLhB,WAAWC,MAAM,GAAGK,YAAYW,IAAI,CAACd,GAAG,CAAC,CAACe,IAAMA;oBAClD;gBACF;gBACA,OAAOC,SAASC,IAAI,CAAC;oBACnBC,KAAK,IAAIC,OAAOC,WAAW;oBAC3B5B;oBACAK;gBACF;YACF;YAEA,yEAAyE;YACzE,OAAOmB,SAASC,IAAI,CAClB;gBACE,iCAAiC;gBACjCC,KAAK,IAAIC,OAAOC,WAAW;gBAC3B5B;gBACAK,YAAY;YACd,GACA;gBAAEd;gBAASsC,QAAQ;YAAI;QAE3B,EAAE,OAAOR,OAAgB;YACvB,4EAA4E;YAC5E,OAAOG,SAASC,IAAI,CAClB;gBACEJ;gBACAK,KAAK,IAAIC,OAAOC,WAAW;YAC7B,GACA;gBAAErC;gBAASsC,QAAQ;YAAI;QAE3B;IACF;IAEA,yFAAyF,GACzF,MAAMC,yBAAmC;QACvCC,SAASjC;QACTkC,QAAQ;QACRC,MAAM;IACR;IACA,OAAOH;AACT;AAEA,eAAelC,6BAA4B"}
|
package/dist/exports/ui.d.ts
CHANGED
|
@@ -15,3 +15,4 @@ export { useSubscribe } from '../hooks/useSubscribe.js';
|
|
|
15
15
|
export { useUnsubscribe } from '../hooks/useUnsubscribe.js';
|
|
16
16
|
export { useVerifyMagicLink } from '../hooks/useVerifyMagicLink.js';
|
|
17
17
|
export { getServerUrl } from '../server-functions/serverUrl.js';
|
|
18
|
+
export type { OptInChannel } from 'src/copied/payload-types.js';
|
package/dist/exports/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/ui.ts"],"sourcesContent":["export type { RequestMagicLinkResponse } from '../components/app/RequestMagicLink.js'\nexport { RequestMagicLink } from '../components/app/RequestMagicLink.js'\n\nexport { RequestOrSubscribe } from '../components/app/RequestOrSubscribe.js'\n\nexport type { SubscribeResponse } from '../components/app/Subscribe.js'\nexport { Subscribe } from '../components/app/Subscribe.js'\n\nexport { SubscriberMenu } from '../components/app/SubscriberMenu.js'\n\nexport type { UnsubscribeResponse } from '../components/app/Unsubscribe.js'\nexport { Unsubscribe } from '../components/app/Unsubscribe.js'\n\nexport type { VerifyMagicLinkResponse } from '../components/app/VerifyMagicLink.js'\nexport { VerifyMagicLink } from '../components/app/VerifyMagicLink.js'\n\nexport type { SubscriberContextType } from '../contexts/SubscriberProvider.js'\nexport { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js'\n\nexport { useRequestMagicLink } from '../hooks/useRequestMagicLink.js'\nexport { useSubscribe } from '../hooks/useSubscribe.js'\nexport { useUnsubscribe } from '../hooks/useUnsubscribe.js'\nexport { useVerifyMagicLink } from '../hooks/useVerifyMagicLink.js'\n\nexport { getServerUrl } from '../server-functions/serverUrl.js'\n"],"names":["RequestMagicLink","RequestOrSubscribe","Subscribe","SubscriberMenu","Unsubscribe","VerifyMagicLink","SubscriberProvider","useSubscriber","useRequestMagicLink","useSubscribe","useUnsubscribe","useVerifyMagicLink","getServerUrl"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,wCAAuC;AAExE,SAASC,kBAAkB,QAAQ,0CAAyC;AAG5E,SAASC,SAAS,QAAQ,iCAAgC;AAE1D,SAASC,cAAc,QAAQ,sCAAqC;AAGpE,SAASC,WAAW,QAAQ,mCAAkC;AAG9D,SAASC,eAAe,QAAQ,uCAAsC;AAGtE,SAASC,kBAAkB,EAAEC,aAAa,QAAQ,oCAAmC;AAErF,SAASC,mBAAmB,QAAQ,kCAAiC;AACrE,SAASC,YAAY,QAAQ,2BAA0B;AACvD,SAASC,cAAc,QAAQ,6BAA4B;AAC3D,SAASC,kBAAkB,QAAQ,iCAAgC;AAEnE,SAASC,YAAY,QAAQ,mCAAkC"}
|
|
1
|
+
{"version":3,"sources":["../../src/exports/ui.ts"],"sourcesContent":["export type { RequestMagicLinkResponse } from '../components/app/RequestMagicLink.js'\nexport { RequestMagicLink } from '../components/app/RequestMagicLink.js'\n\nexport { RequestOrSubscribe } from '../components/app/RequestOrSubscribe.js'\n\nexport type { SubscribeResponse } from '../components/app/Subscribe.js'\nexport { Subscribe } from '../components/app/Subscribe.js'\n\nexport { SubscriberMenu } from '../components/app/SubscriberMenu.js'\n\nexport type { UnsubscribeResponse } from '../components/app/Unsubscribe.js'\nexport { Unsubscribe } from '../components/app/Unsubscribe.js'\n\nexport type { VerifyMagicLinkResponse } from '../components/app/VerifyMagicLink.js'\nexport { VerifyMagicLink } from '../components/app/VerifyMagicLink.js'\n\nexport type { SubscriberContextType } from '../contexts/SubscriberProvider.js'\nexport { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js'\n\nexport { useRequestMagicLink } from '../hooks/useRequestMagicLink.js'\nexport { useSubscribe } from '../hooks/useSubscribe.js'\nexport { useUnsubscribe } from '../hooks/useUnsubscribe.js'\nexport { useVerifyMagicLink } from '../hooks/useVerifyMagicLink.js'\n\nexport { getServerUrl } from '../server-functions/serverUrl.js'\n\nexport type { OptInChannel } from 'src/copied/payload-types.js'\n"],"names":["RequestMagicLink","RequestOrSubscribe","Subscribe","SubscriberMenu","Unsubscribe","VerifyMagicLink","SubscriberProvider","useSubscriber","useRequestMagicLink","useSubscribe","useUnsubscribe","useVerifyMagicLink","getServerUrl"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,wCAAuC;AAExE,SAASC,kBAAkB,QAAQ,0CAAyC;AAG5E,SAASC,SAAS,QAAQ,iCAAgC;AAE1D,SAASC,cAAc,QAAQ,sCAAqC;AAGpE,SAASC,WAAW,QAAQ,mCAAkC;AAG9D,SAASC,eAAe,QAAQ,uCAAsC;AAGtE,SAASC,kBAAkB,EAAEC,aAAa,QAAQ,oCAAmC;AAErF,SAASC,mBAAmB,QAAQ,kCAAiC;AACrE,SAASC,YAAY,QAAQ,2BAA0B;AACvD,SAASC,cAAc,QAAQ,6BAA4B;AAC3D,SAASC,kBAAkB,QAAQ,iCAAgC;AAEnE,SAASC,YAAY,QAAQ,mCAAkC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Subscriber } from '../copied/payload-types.js';
|
|
1
|
+
import type { OptInChannel, Subscriber } from '../copied/payload-types.js';
|
|
2
2
|
import type { SubscribeResponse } from '../endpoints/subscribe.js';
|
|
3
3
|
export { SubscribeResponse };
|
|
4
4
|
/**
|
|
@@ -20,9 +20,12 @@ export interface IUseSubscribeOptions {
|
|
|
20
20
|
* @property updateSubscriptions - Updates opt-in channels for the current subscriber
|
|
21
21
|
*/
|
|
22
22
|
export interface IUseSubscribe {
|
|
23
|
+
optInChannels: OptInChannel[];
|
|
23
24
|
result?: string;
|
|
24
25
|
status?: UpdateSubscriptionStatusValue;
|
|
25
|
-
subscriber:
|
|
26
|
+
subscriber: ({
|
|
27
|
+
optIns?: null | OptInChannel[];
|
|
28
|
+
} & Omit<Subscriber, 'optIns'>) | null;
|
|
26
29
|
updateSubscriptions: (selectedChannelIDs: string[]) => Promise<void>;
|
|
27
30
|
}
|
|
28
31
|
export type UpdateSubscriptionStatusValue = 'default' | 'error' | 'sent' | 'updated' | 'updating';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { useSubscriber } from '../contexts/SubscriberProvider.js';
|
|
4
|
+
import { useServerUrl } from '../react-hooks/useServerUrl.js';
|
|
4
5
|
/**
|
|
5
6
|
* Hook to update subscriber opt-in channels. Calls POST /api/subscribe, refreshes subscriber
|
|
6
7
|
* from context on success, and optionally invokes handleSubscribe.
|
|
@@ -11,6 +12,30 @@ import { useSubscriber } from '../contexts/SubscriberProvider.js';
|
|
|
11
12
|
* @returns updateSubscriptions, subscriber, result message, and status (see IUseSubscribe)
|
|
12
13
|
*/ export const useSubscribe = ({ handleSubscribe, verifyData })=>{
|
|
13
14
|
const { refreshSubscriber, subscriber } = useSubscriber();
|
|
15
|
+
const { serverURL } = useServerUrl();
|
|
16
|
+
const [optInChannels, setOptInChannels] = useState([]);
|
|
17
|
+
useEffect(()=>{
|
|
18
|
+
async function getOptInChannels() {
|
|
19
|
+
const result = await fetch(`${serverURL ? serverURL : ''}/api/optinchannels`, {
|
|
20
|
+
method: 'GET'
|
|
21
|
+
});
|
|
22
|
+
if (result.ok) {
|
|
23
|
+
const resultJson = await result.json();
|
|
24
|
+
// @ts-expect-error OR type union not recognized
|
|
25
|
+
setOptInChannels(resultJson?.optInChannels);
|
|
26
|
+
} else {
|
|
27
|
+
const resultText = await result.text();
|
|
28
|
+
console.log('Error getting opt-in channels: ', [
|
|
29
|
+
{
|
|
30
|
+
resultText
|
|
31
|
+
}
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
void getOptInChannels();
|
|
36
|
+
}, [
|
|
37
|
+
serverURL
|
|
38
|
+
]);
|
|
14
39
|
const [status, setStatus] = useState('default');
|
|
15
40
|
const [result, setResult] = useState();
|
|
16
41
|
const updateSubscriptions = async (selectedChannelIDs)=>{
|
|
@@ -52,6 +77,7 @@ import { useSubscriber } from '../contexts/SubscriberProvider.js';
|
|
|
52
77
|
}
|
|
53
78
|
};
|
|
54
79
|
return {
|
|
80
|
+
optInChannels,
|
|
55
81
|
result,
|
|
56
82
|
status,
|
|
57
83
|
subscriber,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/useSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\n\nimport type { Subscriber } from '../copied/payload-types.js'\nimport type { SubscribeResponse } from '../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport { useSubscriber } from '../contexts/SubscriberProvider.js'\n\n/**\n * Options for the useSubscribe hook.\n *\n * @property handleSubscribe - Callback when subscription is updated or magic link is sent\n * @property verifyData - Optional data sent with subscribe requests (e.g. for verification)\n */\nexport interface IUseSubscribeOptions {\n handleSubscribe?: (result: SubscribeResponse) => void\n verifyData?: string\n}\n\n/**\n * Return value of useSubscribe.\n *\n * @property result - Success or error message from the last update\n * @property status - Current status: 'default' | 'updating' | 'updated' | 'sent' | 'error'\n * @property subscriber - Current subscriber from context, or null\n * @property updateSubscriptions - Updates opt-in channels for the current subscriber\n */\nexport interface IUseSubscribe {\n result?: string\n status?: UpdateSubscriptionStatusValue\n subscriber: null | Subscriber\n updateSubscriptions: (selectedChannelIDs: string[]) => Promise<void>\n}\n\nexport type UpdateSubscriptionStatusValue = 'default' | 'error' | 'sent' | 'updated' | 'updating'\n\n/**\n * Hook to update subscriber opt-in channels. Calls POST /api/subscribe, refreshes subscriber\n * from context on success, and optionally invokes handleSubscribe.\n *\n * @param options - Hook options (see IUseSubscribeOptions)\n * @param options.handleSubscribe - Callback when subscription is updated or magic link is sent\n * @param options.verifyData - Optional data sent with subscribe requests (e.g. for verification)\n * @returns updateSubscriptions, subscriber, result message, and status (see IUseSubscribe)\n */\nexport const useSubscribe = ({\n handleSubscribe,\n verifyData,\n}: IUseSubscribeOptions): IUseSubscribe => {\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const [status, setStatus] = useState<UpdateSubscriptionStatusValue>('default')\n const [result, setResult] = useState<string>()\n\n const updateSubscriptions = async (selectedChannelIDs: string[]) => {\n setStatus('updating')\n setResult(`Updating...`)\n const subscribeResult = await fetch('/api/subscribe', {\n body: JSON.stringify({\n email: subscriber?.email,\n optIns: selectedChannelIDs,\n verifyData,\n }),\n method: 'POST',\n })\n if (subscribeResult.ok) {\n const resultJson: SubscribeResponse = await subscribeResult.json()\n // @ts-expect-error Silly type confusion\n const { emailResult, error } = resultJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else if (subscriber) {\n setStatus('updated')\n setResult(`You're subscriptions have been updated.`)\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n\n refreshSubscriber()\n\n if (handleSubscribe) {\n handleSubscribe(resultJson)\n }\n } else {\n // const resultText = await subscribeResult.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n }\n\n return {\n result,\n status,\n subscriber,\n updateSubscriptions,\n }\n}\n"],"names":["useState","useSubscriber","useSubscribe","handleSubscribe","verifyData","refreshSubscriber","subscriber","
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\n\nimport type { OptInChannel, Subscriber } from '../copied/payload-types.js'\nimport type { SubscribeResponse } from '../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport type { GetOptInChannelsResponse } from '../endpoints/getOptInChannels.js'\n\nimport { useSubscriber } from '../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\n/**\n * Options for the useSubscribe hook.\n *\n * @property handleSubscribe - Callback when subscription is updated or magic link is sent\n * @property verifyData - Optional data sent with subscribe requests (e.g. for verification)\n */\nexport interface IUseSubscribeOptions {\n handleSubscribe?: (result: SubscribeResponse) => void\n verifyData?: string\n}\n\n/**\n * Return value of useSubscribe.\n *\n * @property result - Success or error message from the last update\n * @property status - Current status: 'default' | 'updating' | 'updated' | 'sent' | 'error'\n * @property subscriber - Current subscriber from context, or null\n * @property updateSubscriptions - Updates opt-in channels for the current subscriber\n */\nexport interface IUseSubscribe {\n optInChannels: OptInChannel[]\n result?: string\n status?: UpdateSubscriptionStatusValue\n subscriber: ({ optIns?: null | OptInChannel[] } & Omit<Subscriber, 'optIns'>) | null\n updateSubscriptions: (selectedChannelIDs: string[]) => Promise<void>\n}\n\nexport type UpdateSubscriptionStatusValue = 'default' | 'error' | 'sent' | 'updated' | 'updating'\n\n/**\n * Hook to update subscriber opt-in channels. Calls POST /api/subscribe, refreshes subscriber\n * from context on success, and optionally invokes handleSubscribe.\n *\n * @param options - Hook options (see IUseSubscribeOptions)\n * @param options.handleSubscribe - Callback when subscription is updated or magic link is sent\n * @param options.verifyData - Optional data sent with subscribe requests (e.g. for verification)\n * @returns updateSubscriptions, subscriber, result message, and status (see IUseSubscribe)\n */\nexport const useSubscribe = ({\n handleSubscribe,\n verifyData,\n}: IUseSubscribeOptions): IUseSubscribe => {\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const { serverURL } = useServerUrl()\n const [optInChannels, setOptInChannels] = useState<OptInChannel[]>([])\n\n useEffect(() => {\n async function getOptInChannels() {\n const result = await fetch(`${serverURL ? serverURL : ''}/api/optinchannels`, {\n method: 'GET',\n })\n if (result.ok) {\n const resultJson: GetOptInChannelsResponse = await result.json()\n // @ts-expect-error OR type union not recognized\n setOptInChannels(resultJson?.optInChannels)\n } else {\n const resultText = await result.text()\n console.log('Error getting opt-in channels: ', [{ resultText }])\n }\n }\n void getOptInChannels()\n }, [serverURL])\n\n const [status, setStatus] = useState<UpdateSubscriptionStatusValue>('default')\n const [result, setResult] = useState<string>()\n\n const updateSubscriptions = async (selectedChannelIDs: string[]) => {\n setStatus('updating')\n setResult(`Updating...`)\n const subscribeResult = await fetch('/api/subscribe', {\n body: JSON.stringify({\n email: subscriber?.email,\n optIns: selectedChannelIDs,\n verifyData,\n }),\n method: 'POST',\n })\n if (subscribeResult.ok) {\n const resultJson: SubscribeResponse = await subscribeResult.json()\n // @ts-expect-error Silly type confusion\n const { emailResult, error } = resultJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else if (subscriber) {\n setStatus('updated')\n setResult(`You're subscriptions have been updated.`)\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n\n refreshSubscriber()\n\n if (handleSubscribe) {\n handleSubscribe(resultJson)\n }\n } else {\n // const resultText = await subscribeResult.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n }\n\n return {\n optInChannels,\n result,\n status,\n subscriber,\n updateSubscriptions,\n }\n}\n"],"names":["useEffect","useState","useSubscriber","useServerUrl","useSubscribe","handleSubscribe","verifyData","refreshSubscriber","subscriber","serverURL","optInChannels","setOptInChannels","getOptInChannels","result","fetch","method","ok","resultJson","json","resultText","text","console","log","status","setStatus","setResult","updateSubscriptions","selectedChannelIDs","subscribeResult","body","JSON","stringify","email","optIns","emailResult","error"],"mappings":"AAAA;AAEA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAS3C,SAASC,aAAa,QAAQ,oCAAmC;AACjE,SAASC,YAAY,QAAQ,iCAAgC;AA+B7D;;;;;;;;CAQC,GACD,OAAO,MAAMC,eAAe,CAAC,EAC3BC,eAAe,EACfC,UAAU,EACW;IACrB,MAAM,EAAEC,iBAAiB,EAAEC,UAAU,EAAE,GAAGN;IAE1C,MAAM,EAAEO,SAAS,EAAE,GAAGN;IACtB,MAAM,CAACO,eAAeC,iBAAiB,GAAGV,SAAyB,EAAE;IAErED,UAAU;QACR,eAAeY;YACb,MAAMC,SAAS,MAAMC,MAAM,GAAGL,YAAYA,YAAY,GAAG,kBAAkB,CAAC,EAAE;gBAC5EM,QAAQ;YACV;YACA,IAAIF,OAAOG,EAAE,EAAE;gBACb,MAAMC,aAAuC,MAAMJ,OAAOK,IAAI;gBAC9D,gDAAgD;gBAChDP,iBAAiBM,YAAYP;YAC/B,OAAO;gBACL,MAAMS,aAAa,MAAMN,OAAOO,IAAI;gBACpCC,QAAQC,GAAG,CAAC,mCAAmC;oBAAC;wBAAEH;oBAAW;iBAAE;YACjE;QACF;QACA,KAAKP;IACP,GAAG;QAACH;KAAU;IAEd,MAAM,CAACc,QAAQC,UAAU,GAAGvB,SAAwC;IACpE,MAAM,CAACY,QAAQY,UAAU,GAAGxB;IAE5B,MAAMyB,sBAAsB,OAAOC;QACjCH,UAAU;QACVC,UAAU,CAAC,WAAW,CAAC;QACvB,MAAMG,kBAAkB,MAAMd,MAAM,kBAAkB;YACpDe,MAAMC,KAAKC,SAAS,CAAC;gBACnBC,OAAOxB,YAAYwB;gBACnBC,QAAQN;gBACRrB;YACF;YACAS,QAAQ;QACV;QACA,IAAIa,gBAAgBZ,EAAE,EAAE;YACtB,MAAMC,aAAgC,MAAMW,gBAAgBV,IAAI;YAChE,wCAAwC;YACxC,MAAM,EAAEgB,WAAW,EAAEC,KAAK,EAAE,GAAGlB;YAC/B,IAAIkB,OAAO;gBACTX,UAAU;gBACVC,UAAU,CAAC,uCAAuC,EAAEU,OAAO;YAC7D,OAAO,IAAID,aAAa;gBACtBV,UAAU;gBACVC,UAAU;YACZ,OAAO,IAAIjB,YAAY;gBACrBgB,UAAU;gBACVC,UAAU,CAAC,uCAAuC,CAAC;YACrD,OAAO;gBACLD,UAAU;gBACVC,UAAU,CAAC,oDAAoD,CAAC;YAClE;YAEAlB;YAEA,IAAIF,iBAAiB;gBACnBA,gBAAgBY;YAClB;QACF,OAAO;YACL,kDAAkD;YAClDO,UAAU;YACVC,UAAU,CAAC,oDAAoD,CAAC;QAClE;IACF;IAEA,OAAO;QACLf;QACAG;QACAU;QACAf;QACAkB;IACF;AACF,EAAC"}
|
|
@@ -11,7 +11,7 @@ import { useServerUrl } from '../react-hooks/useServerUrl.js';
|
|
|
11
11
|
* @returns unsubscribe function plus isLoading, isError, and result (see IUseUnsubscribe)
|
|
12
12
|
*/ export const useUnsubscribe = ({ handleUnsubscribe })=>{
|
|
13
13
|
const { serverURL } = useServerUrl();
|
|
14
|
-
const { subscriber } = useSubscriber();
|
|
14
|
+
const { refreshSubscriber, subscriber } = useSubscriber();
|
|
15
15
|
const [result, setResult] = useState('');
|
|
16
16
|
const [isError, setIsError] = useState(false);
|
|
17
17
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -67,6 +67,7 @@ import { useServerUrl } from '../react-hooks/useServerUrl.js';
|
|
|
67
67
|
setResult(message || `An error occured. Please try again. (${error})`);
|
|
68
68
|
setIsError(error && !message);
|
|
69
69
|
setIsLoading(false);
|
|
70
|
+
refreshSubscriber();
|
|
70
71
|
if (handleUnsubscribe) {
|
|
71
72
|
handleUnsubscribe(resultJson);
|
|
72
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/useUnsubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useCallback, useState } from 'react'\n\nimport type { UnsubscribeResponse } from '../endpoints/unsubscribe.js'\n\nexport { UnsubscribeResponse }\nimport { useSubscriber } from '../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\n/**\n * Options for the useUnsubscribe hook.\n *\n * @property handleUnsubscribe - Callback when unsubscribe is attempted (success or error)\n */\nexport interface IUseUnsubscribeOptions {\n handleUnsubscribe?: (result: UnsubscribeResponse) => void\n}\n\n/**\n * Arguments for the unsubscribe function when calling it with email/hash explicitly.\n *\n * @property email - Subscriber email\n * @property hash - Unsubscribe token (from email link)\n */\nexport interface IUnsubscribeProps {\n email: string\n hash: string\n}\n\n/**\n * Return value of useUnsubscribe.\n *\n * @property isError - True if the last unsubscribe attempt failed\n * @property isLoading - True while an unsubscribe request is in progress\n * @property result - Result message from the last attempt\n * @property unsubscribe - Calls POST /api/unsubscribe; optional props or uses subscriber from context\n */\nexport interface IUseUnsubscribe {\n isError: boolean\n isLoading: boolean\n result: string\n unsubscribe: (props?: IUnsubscribeProps) => Promise<void>\n}\n\n/**\n * Hook to perform unsubscribe. Calls POST /api/unsubscribe with email and token (from args or\n * subscriber context). For use with unsubscribe URLs in emails, etc.\n *\n * @param options - Hook options (see IUseUnsubscribeOptions)\n * @param options.handleUnsubscribe - Callback when unsubscribe is attempted (success or error)\n * @returns unsubscribe function plus isLoading, isError, and result (see IUseUnsubscribe)\n */\nexport const useUnsubscribe = ({ handleUnsubscribe }: IUseUnsubscribeOptions): IUseUnsubscribe => {\n const { serverURL } = useServerUrl()\n const { subscriber } = useSubscriber()\n\n const [result, setResult] = useState<string>('')\n const [isError, setIsError] = useState<boolean>(false)\n const [isLoading, setIsLoading] = useState<boolean>(false)\n\n const unsubscribe = useCallback(\n async (props?: IUnsubscribeProps) => {\n let email: string | undefined\n let hash: string | undefined\n let resultJson: UnsubscribeResponse\n\n if (!props) {\n if (subscriber?.email) {\n email = subscriber?.email\n }\n } else {\n email = props.email\n hash = props.hash\n }\n if (!email || !hash) {\n resultJson = { error: 'Invalid input', now: new Date().toISOString() }\n }\n setIsLoading(true)\n\n try {\n const unsubscribeEndpointResult = await fetch(\n `${serverURL ? serverURL : ''}/api/unsubscribe`,\n {\n body: JSON.stringify({\n email,\n unsubscribeToken: hash,\n }),\n method: 'POST',\n },\n )\n\n if (unsubscribeEndpointResult && unsubscribeEndpointResult.json) {\n resultJson = await unsubscribeEndpointResult.json()\n } else if (unsubscribeEndpointResult && unsubscribeEndpointResult.text) {\n const resultText = await unsubscribeEndpointResult.text()\n resultJson = { error: resultText, now: new Date().toISOString() }\n } else {\n resultJson = {\n error: `${unsubscribeEndpointResult.status}`,\n now: new Date().toISOString(),\n }\n }\n } catch (error: any) {\n resultJson = { error, now: new Date().toISOString() }\n }\n\n // @ts-expect-error Linter doesn't recognize the OR typing\n const { error, message } = resultJson\n setResult(message || `An error occured. Please try again. (${error})`)\n setIsError(error && !message)\n setIsLoading(false)\n\n if (handleUnsubscribe) {\n handleUnsubscribe(resultJson)\n }\n },\n [serverURL, handleUnsubscribe, subscriber],\n )\n\n return { isError, isLoading, result, unsubscribe }\n}\n"],"names":["useCallback","useState","useSubscriber","useServerUrl","useUnsubscribe","handleUnsubscribe","serverURL","subscriber","result","setResult","isError","setIsError","isLoading","setIsLoading","unsubscribe","props","email","hash","resultJson","error","now","Date","toISOString","unsubscribeEndpointResult","fetch","body","JSON","stringify","unsubscribeToken","method","json","text","resultText","status","message"],"mappings":"AAAA;AAEA,SAASA,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAK7C,SAASC,aAAa,QAAQ,oCAAmC;AACjE,SAASC,YAAY,QAAQ,iCAAgC;AAqC7D;;;;;;;CAOC,GACD,OAAO,MAAMC,iBAAiB,CAAC,EAAEC,iBAAiB,EAA0B;IAC1E,MAAM,EAAEC,SAAS,EAAE,GAAGH;IACtB,MAAM,EAAEI,UAAU,EAAE,
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useUnsubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useCallback, useState } from 'react'\n\nimport type { UnsubscribeResponse } from '../endpoints/unsubscribe.js'\n\nexport { UnsubscribeResponse }\nimport { useSubscriber } from '../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../react-hooks/useServerUrl.js'\n\n/**\n * Options for the useUnsubscribe hook.\n *\n * @property handleUnsubscribe - Callback when unsubscribe is attempted (success or error)\n */\nexport interface IUseUnsubscribeOptions {\n handleUnsubscribe?: (result: UnsubscribeResponse) => void\n}\n\n/**\n * Arguments for the unsubscribe function when calling it with email/hash explicitly.\n *\n * @property email - Subscriber email\n * @property hash - Unsubscribe token (from email link)\n */\nexport interface IUnsubscribeProps {\n email: string\n hash: string\n}\n\n/**\n * Return value of useUnsubscribe.\n *\n * @property isError - True if the last unsubscribe attempt failed\n * @property isLoading - True while an unsubscribe request is in progress\n * @property result - Result message from the last attempt\n * @property unsubscribe - Calls POST /api/unsubscribe; optional props or uses subscriber from context\n */\nexport interface IUseUnsubscribe {\n isError: boolean\n isLoading: boolean\n result: string\n unsubscribe: (props?: IUnsubscribeProps) => Promise<void>\n}\n\n/**\n * Hook to perform unsubscribe. Calls POST /api/unsubscribe with email and token (from args or\n * subscriber context). For use with unsubscribe URLs in emails, etc.\n *\n * @param options - Hook options (see IUseUnsubscribeOptions)\n * @param options.handleUnsubscribe - Callback when unsubscribe is attempted (success or error)\n * @returns unsubscribe function plus isLoading, isError, and result (see IUseUnsubscribe)\n */\nexport const useUnsubscribe = ({ handleUnsubscribe }: IUseUnsubscribeOptions): IUseUnsubscribe => {\n const { serverURL } = useServerUrl()\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const [result, setResult] = useState<string>('')\n const [isError, setIsError] = useState<boolean>(false)\n const [isLoading, setIsLoading] = useState<boolean>(false)\n\n const unsubscribe = useCallback(\n async (props?: IUnsubscribeProps) => {\n let email: string | undefined\n let hash: string | undefined\n let resultJson: UnsubscribeResponse\n\n if (!props) {\n if (subscriber?.email) {\n email = subscriber?.email\n }\n } else {\n email = props.email\n hash = props.hash\n }\n if (!email || !hash) {\n resultJson = { error: 'Invalid input', now: new Date().toISOString() }\n }\n setIsLoading(true)\n\n try {\n const unsubscribeEndpointResult = await fetch(\n `${serverURL ? serverURL : ''}/api/unsubscribe`,\n {\n body: JSON.stringify({\n email,\n unsubscribeToken: hash,\n }),\n method: 'POST',\n },\n )\n\n if (unsubscribeEndpointResult && unsubscribeEndpointResult.json) {\n resultJson = await unsubscribeEndpointResult.json()\n } else if (unsubscribeEndpointResult && unsubscribeEndpointResult.text) {\n const resultText = await unsubscribeEndpointResult.text()\n resultJson = { error: resultText, now: new Date().toISOString() }\n } else {\n resultJson = {\n error: `${unsubscribeEndpointResult.status}`,\n now: new Date().toISOString(),\n }\n }\n } catch (error: any) {\n resultJson = { error, now: new Date().toISOString() }\n }\n\n // @ts-expect-error Linter doesn't recognize the OR typing\n const { error, message } = resultJson\n setResult(message || `An error occured. Please try again. (${error})`)\n setIsError(error && !message)\n setIsLoading(false)\n\n refreshSubscriber()\n\n if (handleUnsubscribe) {\n handleUnsubscribe(resultJson)\n }\n },\n [serverURL, handleUnsubscribe, subscriber],\n )\n\n return { isError, isLoading, result, unsubscribe }\n}\n"],"names":["useCallback","useState","useSubscriber","useServerUrl","useUnsubscribe","handleUnsubscribe","serverURL","refreshSubscriber","subscriber","result","setResult","isError","setIsError","isLoading","setIsLoading","unsubscribe","props","email","hash","resultJson","error","now","Date","toISOString","unsubscribeEndpointResult","fetch","body","JSON","stringify","unsubscribeToken","method","json","text","resultText","status","message"],"mappings":"AAAA;AAEA,SAASA,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAK7C,SAASC,aAAa,QAAQ,oCAAmC;AACjE,SAASC,YAAY,QAAQ,iCAAgC;AAqC7D;;;;;;;CAOC,GACD,OAAO,MAAMC,iBAAiB,CAAC,EAAEC,iBAAiB,EAA0B;IAC1E,MAAM,EAAEC,SAAS,EAAE,GAAGH;IACtB,MAAM,EAAEI,iBAAiB,EAAEC,UAAU,EAAE,GAAGN;IAE1C,MAAM,CAACO,QAAQC,UAAU,GAAGT,SAAiB;IAC7C,MAAM,CAACU,SAASC,WAAW,GAAGX,SAAkB;IAChD,MAAM,CAACY,WAAWC,aAAa,GAAGb,SAAkB;IAEpD,MAAMc,cAAcf,YAClB,OAAOgB;QACL,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QAEJ,IAAI,CAACH,OAAO;YACV,IAAIR,YAAYS,OAAO;gBACrBA,QAAQT,YAAYS;YACtB;QACF,OAAO;YACLA,QAAQD,MAAMC,KAAK;YACnBC,OAAOF,MAAME,IAAI;QACnB;QACA,IAAI,CAACD,SAAS,CAACC,MAAM;YACnBC,aAAa;gBAAEC,OAAO;gBAAiBC,KAAK,IAAIC,OAAOC,WAAW;YAAG;QACvE;QACAT,aAAa;QAEb,IAAI;YACF,MAAMU,4BAA4B,MAAMC,MACtC,GAAGnB,YAAYA,YAAY,GAAG,gBAAgB,CAAC,EAC/C;gBACEoB,MAAMC,KAAKC,SAAS,CAAC;oBACnBX;oBACAY,kBAAkBX;gBACpB;gBACAY,QAAQ;YACV;YAGF,IAAIN,6BAA6BA,0BAA0BO,IAAI,EAAE;gBAC/DZ,aAAa,MAAMK,0BAA0BO,IAAI;YACnD,OAAO,IAAIP,6BAA6BA,0BAA0BQ,IAAI,EAAE;gBACtE,MAAMC,aAAa,MAAMT,0BAA0BQ,IAAI;gBACvDb,aAAa;oBAAEC,OAAOa;oBAAYZ,KAAK,IAAIC,OAAOC,WAAW;gBAAG;YAClE,OAAO;gBACLJ,aAAa;oBACXC,OAAO,GAAGI,0BAA0BU,MAAM,EAAE;oBAC5Cb,KAAK,IAAIC,OAAOC,WAAW;gBAC7B;YACF;QACF,EAAE,OAAOH,OAAY;YACnBD,aAAa;gBAAEC;gBAAOC,KAAK,IAAIC,OAAOC,WAAW;YAAG;QACtD;QAEA,0DAA0D;QAC1D,MAAM,EAAEH,KAAK,EAAEe,OAAO,EAAE,GAAGhB;QAC3BT,UAAUyB,WAAW,CAAC,qCAAqC,EAAEf,MAAM,CAAC,CAAC;QACrER,WAAWQ,SAAS,CAACe;QACrBrB,aAAa;QAEbP;QAEA,IAAIF,mBAAmB;YACrBA,kBAAkBc;QACpB;IACF,GACA;QAACb;QAAWD;QAAmBG;KAAW;IAG5C,OAAO;QAAEG;QAASE;QAAWJ;QAAQM;IAAY;AACnD,EAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,4 +32,16 @@ export type PayloadSubscribersConfig = {
|
|
|
32
32
|
*/
|
|
33
33
|
verifyURL?: string;
|
|
34
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Adds the payload-subscribers-plugin to your payload config
|
|
37
|
+
*
|
|
38
|
+
* @param pluginOptions - Plugin options
|
|
39
|
+
* @param pluginOptions.collections - (optional) An array of existing collection slugs to add an optIns relationship field to
|
|
40
|
+
* @param pluginOptions.disabled - (optional) A convenience option to disable the plugin
|
|
41
|
+
* @param pluginOptions.subscribersCollectionSlug - (optional) The slug of an existing collection to use for subscribers. If omitted, the plugin will create the 'subscribers' collection
|
|
42
|
+
* @param pluginOptions.tokenExpiration - (optional) The expiration time for a token, in milliseconds. Defaults to 30 minutes
|
|
43
|
+
* @param pluginOptions.unsubscribeURL - (optional) The route or full URL for unsubscribe links
|
|
44
|
+
* @param pluginOptions.verifyURL - (optional) The route or full URL for verify links
|
|
45
|
+
* @returns Payload config modified to include the plugin
|
|
46
|
+
*/
|
|
35
47
|
export declare const payloadSubscribersPlugin: (pluginOptions: PayloadSubscribersConfig) => (config: Config) => Config;
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,18 @@ import createEndpointVerifyMagicLink from './endpoints/verifyMagicLink.js';
|
|
|
11
11
|
import { getTestEmail } from './helpers/testData.js';
|
|
12
12
|
import { getTokenAndHash } from './helpers/token.js';
|
|
13
13
|
import { isAbsoluteURL } from './helpers/utilities.js';
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Adds the payload-subscribers-plugin to your payload config
|
|
16
|
+
*
|
|
17
|
+
* @param pluginOptions - Plugin options
|
|
18
|
+
* @param pluginOptions.collections - (optional) An array of existing collection slugs to add an optIns relationship field to
|
|
19
|
+
* @param pluginOptions.disabled - (optional) A convenience option to disable the plugin
|
|
20
|
+
* @param pluginOptions.subscribersCollectionSlug - (optional) The slug of an existing collection to use for subscribers. If omitted, the plugin will create the 'subscribers' collection
|
|
21
|
+
* @param pluginOptions.tokenExpiration - (optional) The expiration time for a token, in milliseconds. Defaults to 30 minutes
|
|
22
|
+
* @param pluginOptions.unsubscribeURL - (optional) The route or full URL for unsubscribe links
|
|
23
|
+
* @param pluginOptions.verifyURL - (optional) The route or full URL for verify links
|
|
24
|
+
* @returns Payload config modified to include the plugin
|
|
25
|
+
*/ export const payloadSubscribersPlugin = (pluginOptions)=>(config)=>{
|
|
15
26
|
if (!config.serverURL && !(pluginOptions.unsubscribeURL && pluginOptions.verifyURL)) {
|
|
16
27
|
throw new Error('payloadSubscribersPlugin requires config.serverURL OR valid values for all URL options: unsubscribeURL, verifyURL');
|
|
17
28
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { BasePayload, CollectionSlug, Config } from 'payload'\n\nimport { OptedInChannels } from './collections/fields/OptedInChannels.js'\nimport OptInChannels from './collections/OptInChannels.js'\nimport {\n defaultTokenExpiration,\n SubscribersCollectionFactory,\n subscribersCollectionFields,\n} from './collections/Subscribers.js'\nimport getOptInChannelsEndpoint from './endpoints/getOptInChannels.js'\nimport createEndpointLogout from './endpoints/logout.js'\nimport createEndpointRequestMagicLink from './endpoints/requestMagicLink.js'\nimport createEndpointSubscribe from './endpoints/subscribe.js'\nimport createEndpointSubscriberAuth from './endpoints/subscriberAuth.js'\nimport createEndpointUnsubscribe from './endpoints/unsubscribe.js'\nimport createEndpointVerifyMagicLink from './endpoints/verifyMagicLink.js'\nimport { getTestEmail } from './helpers/testData.js'\nimport { getTokenAndHash } from './helpers/token.js'\nimport { isAbsoluteURL } from './helpers/utilities.js'\n\nexport type PayloadSubscribersConfig = {\n /**\n * List of collections to add a custom field\n */\n collections?: Partial<Record<CollectionSlug, true>>\n /**\n * Defaults to false-y. When true:\n * - Database schema changes are still made and seeded\n * - APIs return null or undefined success\n * - Admin components are not added\n * - App components return nothing\n */\n disabled?: boolean\n /**\n * The collection to use as the subscribers collection\n * - Optional. If not specified, the plugin will add a 'subscribers' collection.\n * - Sets the collection auth if not already.\n * - Adds (or overrides) fields: email, firstName, status, optIns, verificationToken, verificationTokenExpires.\n */\n subscribersCollectionSlug?: CollectionSlug\n /**\n * Defaults to 30 minutes\n */\n tokenExpiration?: number\n /**\n * The route or full URL for unsubscribe links\n */\n unsubscribeURL?: string\n /**\n * The route or full URL for verify links\n */\n verifyURL?: string\n}\n\nexport const payloadSubscribersPlugin =\n (pluginOptions: PayloadSubscribersConfig) =>\n (config: Config): Config => {\n if (!config.serverURL && !(pluginOptions.unsubscribeURL && pluginOptions.verifyURL)) {\n throw new Error(\n 'payloadSubscribersPlugin requires config.serverURL OR valid values for all URL options: unsubscribeURL, verifyURL',\n )\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(OptInChannels)\n\n const unsubscribeURL = !pluginOptions.unsubscribeURL\n ? new URL('/unsubscribe', config.serverURL)\n : isAbsoluteURL(pluginOptions.unsubscribeURL)\n ? new URL(pluginOptions.unsubscribeURL)\n : new URL(pluginOptions.unsubscribeURL, config.serverURL)\n\n // Get a URL object from the verifyURL option\n const verifyURL = !pluginOptions.verifyURL\n ? new URL('/verify', config.serverURL)\n : isAbsoluteURL(pluginOptions.verifyURL)\n ? new URL(pluginOptions.verifyURL)\n : new URL(pluginOptions.verifyURL, config.serverURL)\n\n let subscribersCollection = pluginOptions.subscribersCollectionSlug\n ? config.collections.find(\n (collection) => collection.slug == pluginOptions.subscribersCollectionSlug,\n )\n : undefined\n\n if (subscribersCollection) {\n // Configure the input collection to be the subscribers collection\n config.collections = config.collections.filter(\n (collection) => collection.slug != subscribersCollection?.slug,\n )\n subscribersCollection.fields.push(...subscribersCollectionFields)\n if (!subscribersCollection.auth) {\n subscribersCollection = {\n ...subscribersCollection,\n auth: { tokenExpiration: defaultTokenExpiration },\n }\n }\n if (!subscribersCollection.admin?.useAsTitle) {\n if (!subscribersCollection.admin) {\n subscribersCollection.admin = { useAsTitle: 'email' }\n } else {\n // Throw error? Or override?\n subscribersCollection.admin.useAsTitle = 'email'\n }\n }\n config.collections.push(subscribersCollection)\n } else {\n // Configure the default built-in subscribers collection\n subscribersCollection = SubscribersCollectionFactory({\n slug: pluginOptions.subscribersCollectionSlug,\n tokenExpiration: pluginOptions.tokenExpiration,\n })\n config.collections.push(subscribersCollection)\n }\n\n if (pluginOptions.collections) {\n for (const collectionSlug in pluginOptions.collections) {\n const collection = config.collections.find(\n (collection) => collection.slug === collectionSlug,\n )\n\n if (collection) {\n collection.fields.push(OptedInChannels)\n }\n }\n }\n\n /**\n * If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.\n * If your plugin heavily modifies the database schema, you may want to remove this property.\n */\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.admin) {\n config.admin = {}\n }\n\n if (!config.admin.components) {\n config.admin.components = {}\n }\n\n if (!config.admin.components.beforeDashboard) {\n config.admin.components.beforeDashboard = []\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n config.endpoints.push(\n getOptInChannelsEndpoint,\n createEndpointLogout({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointRequestMagicLink({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n unsubscribeURL,\n verifyURL,\n }),\n createEndpointSubscribe({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n unsubscribeURL,\n verifyURL,\n }),\n createEndpointSubscriberAuth({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointUnsubscribe({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointVerifyMagicLink({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n )\n\n const incomingOnInit = config.onInit\n\n const genInit = (testData: { testEmail: string }) => async (payload: BasePayload) => {\n // Ensure we are executing any existing onInit functions before running our own.\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n\n // console.log('Object.keys(payload.collections)', Object.keys(payload.collections))\n const { totalDocs: totalOptIns } = await payload.count({\n collection: 'opt-in-channels',\n where: {\n title: {\n equals: 'seeded-by-plugin',\n },\n },\n })\n\n if (totalOptIns === 0) {\n await payload.create({\n collection: 'opt-in-channels',\n data: {\n active: true,\n title: 'seeded-by-plugin',\n },\n })\n }\n\n // const { seededChannel } = await payload.find({\n // collection: 'opt-in-channels',\n // where: {\n // title: {\n // equals: 'seeded-by-plugin',\n // },\n // },\n // })\n\n const { totalDocs: totalSubscribers } = await payload.count({\n collection: subscribersCollection.slug as CollectionSlug,\n where: {\n email: {\n equals: testData.testEmail,\n },\n },\n })\n\n const { tokenHash } = getTokenAndHash() // Unknowable\n // payload.logger.info(`testData.testEmail == '${testData.testEmail}'`)\n if (totalSubscribers === 0) {\n await payload.create({\n collection: subscribersCollection.slug as CollectionSlug,\n data: {\n email: testData.testEmail,\n password: tokenHash,\n status: 'pending',\n },\n })\n }\n }\n\n // console.log(`getTestEmail == '${getTestEmail()}'`)\n config.onInit = genInit({ testEmail: getTestEmail() })\n\n return config\n }\n"],"names":["OptedInChannels","OptInChannels","defaultTokenExpiration","SubscribersCollectionFactory","subscribersCollectionFields","getOptInChannelsEndpoint","createEndpointLogout","createEndpointRequestMagicLink","createEndpointSubscribe","createEndpointSubscriberAuth","createEndpointUnsubscribe","createEndpointVerifyMagicLink","getTestEmail","getTokenAndHash","isAbsoluteURL","payloadSubscribersPlugin","pluginOptions","config","serverURL","unsubscribeURL","verifyURL","Error","collections","push","URL","subscribersCollection","subscribersCollectionSlug","find","collection","slug","undefined","filter","fields","auth","tokenExpiration","admin","useAsTitle","collectionSlug","disabled","components","beforeDashboard","endpoints","incomingOnInit","onInit","genInit","testData","payload","totalDocs","totalOptIns","count","where","title","equals","create","data","active","totalSubscribers","email","testEmail","tokenHash","password","status"],"mappings":"AAEA,SAASA,eAAe,QAAQ,0CAAyC;AACzE,OAAOC,mBAAmB,iCAAgC;AAC1D,SACEC,sBAAsB,EACtBC,4BAA4B,EAC5BC,2BAA2B,QACtB,+BAA8B;AACrC,OAAOC,8BAA8B,kCAAiC;AACtE,OAAOC,0BAA0B,wBAAuB;AACxD,OAAOC,oCAAoC,kCAAiC;AAC5E,OAAOC,6BAA6B,2BAA0B;AAC9D,OAAOC,kCAAkC,gCAA+B;AACxE,OAAOC,+BAA+B,6BAA4B;AAClE,OAAOC,mCAAmC,iCAAgC;AAC1E,SAASC,YAAY,QAAQ,wBAAuB;AACpD,SAASC,eAAe,QAAQ,qBAAoB;AACpD,SAASC,aAAa,QAAQ,yBAAwB;AAoCtD,OAAO,MAAMC,2BACX,CAACC,gBACD,CAACC;QACC,IAAI,CAACA,OAAOC,SAAS,IAAI,CAAEF,CAAAA,cAAcG,cAAc,IAAIH,cAAcI,SAAS,AAAD,GAAI;YACnF,MAAM,IAAIC,MACR;QAEJ;QAEA,IAAI,CAACJ,OAAOK,WAAW,EAAE;YACvBL,OAAOK,WAAW,GAAG,EAAE;QACzB;QAEAL,OAAOK,WAAW,CAACC,IAAI,CAACtB;QAExB,MAAMkB,iBAAiB,CAACH,cAAcG,cAAc,GAChD,IAAIK,IAAI,gBAAgBP,OAAOC,SAAS,IACxCJ,cAAcE,cAAcG,cAAc,IACxC,IAAIK,IAAIR,cAAcG,cAAc,IACpC,IAAIK,IAAIR,cAAcG,cAAc,EAAEF,OAAOC,SAAS;QAE5D,6CAA6C;QAC7C,MAAME,YAAY,CAACJ,cAAcI,SAAS,GACtC,IAAII,IAAI,WAAWP,OAAOC,SAAS,IACnCJ,cAAcE,cAAcI,SAAS,IACnC,IAAII,IAAIR,cAAcI,SAAS,IAC/B,IAAII,IAAIR,cAAcI,SAAS,EAAEH,OAAOC,SAAS;QAEvD,IAAIO,wBAAwBT,cAAcU,yBAAyB,GAC/DT,OAAOK,WAAW,CAACK,IAAI,CACrB,CAACC,aAAeA,WAAWC,IAAI,IAAIb,cAAcU,yBAAyB,IAE5EI;QAEJ,IAAIL,uBAAuB;YACzB,kEAAkE;YAClER,OAAOK,WAAW,GAAGL,OAAOK,WAAW,CAACS,MAAM,CAC5C,CAACH,aAAeA,WAAWC,IAAI,IAAIJ,uBAAuBI;YAE5DJ,sBAAsBO,MAAM,CAACT,IAAI,IAAInB;YACrC,IAAI,CAACqB,sBAAsBQ,IAAI,EAAE;gBAC/BR,wBAAwB;oBACtB,GAAGA,qBAAqB;oBACxBQ,MAAM;wBAAEC,iBAAiBhC;oBAAuB;gBAClD;YACF;YACA,IAAI,CAACuB,sBAAsBU,KAAK,EAAEC,YAAY;gBAC5C,IAAI,CAACX,sBAAsBU,KAAK,EAAE;oBAChCV,sBAAsBU,KAAK,GAAG;wBAAEC,YAAY;oBAAQ;gBACtD,OAAO;oBACL,4BAA4B;oBAC5BX,sBAAsBU,KAAK,CAACC,UAAU,GAAG;gBAC3C;YACF;YACAnB,OAAOK,WAAW,CAACC,IAAI,CAACE;QAC1B,OAAO;YACL,wDAAwD;YACxDA,wBAAwBtB,6BAA6B;gBACnD0B,MAAMb,cAAcU,yBAAyB;gBAC7CQ,iBAAiBlB,cAAckB,eAAe;YAChD;YACAjB,OAAOK,WAAW,CAACC,IAAI,CAACE;QAC1B;QAEA,IAAIT,cAAcM,WAAW,EAAE;YAC7B,IAAK,MAAMe,kBAAkBrB,cAAcM,WAAW,CAAE;gBACtD,MAAMM,aAAaX,OAAOK,WAAW,CAACK,IAAI,CACxC,CAACC,aAAeA,WAAWC,IAAI,KAAKQ;gBAGtC,IAAIT,YAAY;oBACdA,WAAWI,MAAM,CAACT,IAAI,CAACvB;gBACzB;YACF;QACF;QAEA;;;KAGC,GACD,IAAIgB,cAAcsB,QAAQ,EAAE;YAC1B,OAAOrB;QACT;QAEA,IAAI,CAACA,OAAOkB,KAAK,EAAE;YACjBlB,OAAOkB,KAAK,GAAG,CAAC;QAClB;QAEA,IAAI,CAAClB,OAAOkB,KAAK,CAACI,UAAU,EAAE;YAC5BtB,OAAOkB,KAAK,CAACI,UAAU,GAAG,CAAC;QAC7B;QAEA,IAAI,CAACtB,OAAOkB,KAAK,CAACI,UAAU,CAACC,eAAe,EAAE;YAC5CvB,OAAOkB,KAAK,CAACI,UAAU,CAACC,eAAe,GAAG,EAAE;QAC9C;QAEA,IAAI,CAACvB,OAAOwB,SAAS,EAAE;YACrBxB,OAAOwB,SAAS,GAAG,EAAE;QACvB;QAEAxB,OAAOwB,SAAS,CAAClB,IAAI,CACnBlB,0BACAC,qBAAqB;YACnBoB,2BAA2BD,sBAAsBI,IAAI;QACvD,IACAtB,+BAA+B;YAC7BmB,2BAA2BD,sBAAsBI,IAAI;YACrDV;YACAC;QACF,IACAZ,wBAAwB;YACtBkB,2BAA2BD,sBAAsBI,IAAI;YACrDV;YACAC;QACF,IACAX,6BAA6B;YAC3BiB,2BAA2BD,sBAAsBI,IAAI;QACvD,IACAnB,0BAA0B;YACxBgB,2BAA2BD,sBAAsBI,IAAI;QACvD,IACAlB,8BAA8B;YAC5Be,2BAA2BD,sBAAsBI,IAAI;QACvD;QAGF,MAAMa,iBAAiBzB,OAAO0B,MAAM;QAEpC,MAAMC,UAAU,CAACC,WAAoC,OAAOC;gBAC1D,gFAAgF;gBAChF,IAAIJ,gBAAgB;oBAClB,MAAMA,eAAeI;gBACvB;gBAEA,oFAAoF;gBACpF,MAAM,EAAEC,WAAWC,WAAW,EAAE,GAAG,MAAMF,QAAQG,KAAK,CAAC;oBACrDrB,YAAY;oBACZsB,OAAO;wBACLC,OAAO;4BACLC,QAAQ;wBACV;oBACF;gBACF;gBAEA,IAAIJ,gBAAgB,GAAG;oBACrB,MAAMF,QAAQO,MAAM,CAAC;wBACnBzB,YAAY;wBACZ0B,MAAM;4BACJC,QAAQ;4BACRJ,OAAO;wBACT;oBACF;gBACF;gBAEA,iDAAiD;gBACjD,mCAAmC;gBACnC,aAAa;gBACb,eAAe;gBACf,oCAAoC;gBACpC,SAAS;gBACT,OAAO;gBACP,KAAK;gBAEL,MAAM,EAAEJ,WAAWS,gBAAgB,EAAE,GAAG,MAAMV,QAAQG,KAAK,CAAC;oBAC1DrB,YAAYH,sBAAsBI,IAAI;oBACtCqB,OAAO;wBACLO,OAAO;4BACLL,QAAQP,SAASa,SAAS;wBAC5B;oBACF;gBACF;gBAEA,MAAM,EAAEC,SAAS,EAAE,GAAG9C,kBAAkB,aAAa;;gBACrD,uEAAuE;gBACvE,IAAI2C,qBAAqB,GAAG;oBAC1B,MAAMV,QAAQO,MAAM,CAAC;wBACnBzB,YAAYH,sBAAsBI,IAAI;wBACtCyB,MAAM;4BACJG,OAAOZ,SAASa,SAAS;4BACzBE,UAAUD;4BACVE,QAAQ;wBACV;oBACF;gBACF;YACF;QAEA,qDAAqD;QACrD5C,OAAO0B,MAAM,GAAGC,QAAQ;YAAEc,WAAW9C;QAAe;QAEpD,OAAOK;IACT,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { BasePayload, CollectionSlug, Config } from 'payload'\n\nimport { OptedInChannels } from './collections/fields/OptedInChannels.js'\nimport OptInChannels from './collections/OptInChannels.js'\nimport {\n defaultTokenExpiration,\n SubscribersCollectionFactory,\n subscribersCollectionFields,\n} from './collections/Subscribers.js'\nimport getOptInChannelsEndpoint from './endpoints/getOptInChannels.js'\nimport createEndpointLogout from './endpoints/logout.js'\nimport createEndpointRequestMagicLink from './endpoints/requestMagicLink.js'\nimport createEndpointSubscribe from './endpoints/subscribe.js'\nimport createEndpointSubscriberAuth from './endpoints/subscriberAuth.js'\nimport createEndpointUnsubscribe from './endpoints/unsubscribe.js'\nimport createEndpointVerifyMagicLink from './endpoints/verifyMagicLink.js'\nimport { getTestEmail } from './helpers/testData.js'\nimport { getTokenAndHash } from './helpers/token.js'\nimport { isAbsoluteURL } from './helpers/utilities.js'\n\nexport type PayloadSubscribersConfig = {\n /**\n * List of collections to add a custom field\n */\n collections?: Partial<Record<CollectionSlug, true>>\n /**\n * Defaults to false-y. When true:\n * - Database schema changes are still made and seeded\n * - APIs return null or undefined success\n * - Admin components are not added\n * - App components return nothing\n */\n disabled?: boolean\n /**\n * The collection to use as the subscribers collection\n * - Optional. If not specified, the plugin will add a 'subscribers' collection.\n * - Sets the collection auth if not already.\n * - Adds (or overrides) fields: email, firstName, status, optIns, verificationToken, verificationTokenExpires.\n */\n subscribersCollectionSlug?: CollectionSlug\n /**\n * Defaults to 30 minutes\n */\n tokenExpiration?: number\n /**\n * The route or full URL for unsubscribe links\n */\n unsubscribeURL?: string\n /**\n * The route or full URL for verify links\n */\n verifyURL?: string\n}\n\n/**\n * Adds the payload-subscribers-plugin to your payload config\n *\n * @param pluginOptions - Plugin options\n * @param pluginOptions.collections - (optional) An array of existing collection slugs to add an optIns relationship field to\n * @param pluginOptions.disabled - (optional) A convenience option to disable the plugin\n * @param pluginOptions.subscribersCollectionSlug - (optional) The slug of an existing collection to use for subscribers. If omitted, the plugin will create the 'subscribers' collection\n * @param pluginOptions.tokenExpiration - (optional) The expiration time for a token, in milliseconds. Defaults to 30 minutes\n * @param pluginOptions.unsubscribeURL - (optional) The route or full URL for unsubscribe links\n * @param pluginOptions.verifyURL - (optional) The route or full URL for verify links\n * @returns Payload config modified to include the plugin\n */\nexport const payloadSubscribersPlugin =\n (pluginOptions: PayloadSubscribersConfig) =>\n (config: Config): Config => {\n if (!config.serverURL && !(pluginOptions.unsubscribeURL && pluginOptions.verifyURL)) {\n throw new Error(\n 'payloadSubscribersPlugin requires config.serverURL OR valid values for all URL options: unsubscribeURL, verifyURL',\n )\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(OptInChannels)\n\n const unsubscribeURL = !pluginOptions.unsubscribeURL\n ? new URL('/unsubscribe', config.serverURL)\n : isAbsoluteURL(pluginOptions.unsubscribeURL)\n ? new URL(pluginOptions.unsubscribeURL)\n : new URL(pluginOptions.unsubscribeURL, config.serverURL)\n\n // Get a URL object from the verifyURL option\n const verifyURL = !pluginOptions.verifyURL\n ? new URL('/verify', config.serverURL)\n : isAbsoluteURL(pluginOptions.verifyURL)\n ? new URL(pluginOptions.verifyURL)\n : new URL(pluginOptions.verifyURL, config.serverURL)\n\n let subscribersCollection = pluginOptions.subscribersCollectionSlug\n ? config.collections.find(\n (collection) => collection.slug == pluginOptions.subscribersCollectionSlug,\n )\n : undefined\n\n if (subscribersCollection) {\n // Configure the input collection to be the subscribers collection\n config.collections = config.collections.filter(\n (collection) => collection.slug != subscribersCollection?.slug,\n )\n subscribersCollection.fields.push(...subscribersCollectionFields)\n if (!subscribersCollection.auth) {\n subscribersCollection = {\n ...subscribersCollection,\n auth: { tokenExpiration: defaultTokenExpiration },\n }\n }\n if (!subscribersCollection.admin?.useAsTitle) {\n if (!subscribersCollection.admin) {\n subscribersCollection.admin = { useAsTitle: 'email' }\n } else {\n // Throw error? Or override?\n subscribersCollection.admin.useAsTitle = 'email'\n }\n }\n config.collections.push(subscribersCollection)\n } else {\n // Configure the default built-in subscribers collection\n subscribersCollection = SubscribersCollectionFactory({\n slug: pluginOptions.subscribersCollectionSlug,\n tokenExpiration: pluginOptions.tokenExpiration,\n })\n config.collections.push(subscribersCollection)\n }\n\n if (pluginOptions.collections) {\n for (const collectionSlug in pluginOptions.collections) {\n const collection = config.collections.find(\n (collection) => collection.slug === collectionSlug,\n )\n\n if (collection) {\n collection.fields.push(OptedInChannels)\n }\n }\n }\n\n /**\n * If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.\n * If your plugin heavily modifies the database schema, you may want to remove this property.\n */\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.admin) {\n config.admin = {}\n }\n\n if (!config.admin.components) {\n config.admin.components = {}\n }\n\n if (!config.admin.components.beforeDashboard) {\n config.admin.components.beforeDashboard = []\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n config.endpoints.push(\n getOptInChannelsEndpoint,\n createEndpointLogout({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointRequestMagicLink({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n unsubscribeURL,\n verifyURL,\n }),\n createEndpointSubscribe({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n unsubscribeURL,\n verifyURL,\n }),\n createEndpointSubscriberAuth({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointUnsubscribe({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n createEndpointVerifyMagicLink({\n subscribersCollectionSlug: subscribersCollection.slug as CollectionSlug,\n }),\n )\n\n const incomingOnInit = config.onInit\n\n const genInit = (testData: { testEmail: string }) => async (payload: BasePayload) => {\n // Ensure we are executing any existing onInit functions before running our own.\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n\n // console.log('Object.keys(payload.collections)', Object.keys(payload.collections))\n const { totalDocs: totalOptIns } = await payload.count({\n collection: 'opt-in-channels',\n where: {\n title: {\n equals: 'seeded-by-plugin',\n },\n },\n })\n\n if (totalOptIns === 0) {\n await payload.create({\n collection: 'opt-in-channels',\n data: {\n active: true,\n title: 'seeded-by-plugin',\n },\n })\n }\n\n // const { seededChannel } = await payload.find({\n // collection: 'opt-in-channels',\n // where: {\n // title: {\n // equals: 'seeded-by-plugin',\n // },\n // },\n // })\n\n const { totalDocs: totalSubscribers } = await payload.count({\n collection: subscribersCollection.slug as CollectionSlug,\n where: {\n email: {\n equals: testData.testEmail,\n },\n },\n })\n\n const { tokenHash } = getTokenAndHash() // Unknowable\n // payload.logger.info(`testData.testEmail == '${testData.testEmail}'`)\n if (totalSubscribers === 0) {\n await payload.create({\n collection: subscribersCollection.slug as CollectionSlug,\n data: {\n email: testData.testEmail,\n password: tokenHash,\n status: 'pending',\n },\n })\n }\n }\n\n // console.log(`getTestEmail == '${getTestEmail()}'`)\n config.onInit = genInit({ testEmail: getTestEmail() })\n\n return config\n }\n"],"names":["OptedInChannels","OptInChannels","defaultTokenExpiration","SubscribersCollectionFactory","subscribersCollectionFields","getOptInChannelsEndpoint","createEndpointLogout","createEndpointRequestMagicLink","createEndpointSubscribe","createEndpointSubscriberAuth","createEndpointUnsubscribe","createEndpointVerifyMagicLink","getTestEmail","getTokenAndHash","isAbsoluteURL","payloadSubscribersPlugin","pluginOptions","config","serverURL","unsubscribeURL","verifyURL","Error","collections","push","URL","subscribersCollection","subscribersCollectionSlug","find","collection","slug","undefined","filter","fields","auth","tokenExpiration","admin","useAsTitle","collectionSlug","disabled","components","beforeDashboard","endpoints","incomingOnInit","onInit","genInit","testData","payload","totalDocs","totalOptIns","count","where","title","equals","create","data","active","totalSubscribers","email","testEmail","tokenHash","password","status"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"registry": "https://registry.npmjs.org/",
|
|
71
71
|
"dependencies": {},
|
|
72
|
-
"version": "0.0.
|
|
72
|
+
"version": "0.0.13",
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
|
|
75
75
|
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|