payload-subscribers-plugin 0.0.10 → 0.0.12

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 CHANGED
@@ -260,7 +260,7 @@ Use these hooks inside components that are descendants of **SubscriberProvider**
260
260
  Requests a magic-login link by email (POST /api/emailToken). Exposes `sendMagicLink`, plus `result` and `status` for rendering messages and loading state.
261
261
 
262
262
  ```typescript
263
- import { useRequestMagicLink } from 'payload-subscribers-plugin/hooks'
263
+ import { useRequestMagicLink } from 'payload-subscribers-plugin/ui'
264
264
 
265
265
  function MySignInForm() {
266
266
  const { result, sendMagicLink, status } = useRequestMagicLink({
@@ -292,7 +292,7 @@ Handles the verify step of the magic-link flow: reads `email` and `token` from U
292
292
 
293
293
  ```typescript
294
294
  import { useEffect } from 'react'
295
- import { useVerifyMagicLink } from 'payload-subscribers-plugin/hooks'
295
+ import { useVerifyMagicLink } from 'payload-subscribers-plugin/ui'
296
296
 
297
297
  function VerifyPage() {
298
298
  const { isError, isLoading, result, verify } = useVerifyMagicLink()
@@ -311,7 +311,7 @@ function VerifyPage() {
311
311
  Updates the current subscriber’s opt-in channels (POST /api/subscribe). Exposes `updateSubscriptions`, plus `subscriber`, `result`, and `status`. Use with **SubscriberProvider** so `subscriber` and refresh are available.
312
312
 
313
313
  ```typescript
314
- import { useSubscribe } from 'payload-subscribers-plugin/hooks'
314
+ import { useSubscribe } from 'payload-subscribers-plugin/ui'
315
315
 
316
316
  function MyPreferencesForm() {
317
317
  const { result, status, subscriber, updateSubscriptions } = useSubscribe({
@@ -344,7 +344,7 @@ Calls POST /api/unsubscribe with email and token (from the hook args or from sub
344
344
  ```typescript
345
345
  import { useEffect } from 'react'
346
346
  import { useSearchParams } from 'next/navigation'
347
- import { useUnsubscribe } from 'payload-subscribers-plugin/hooks'
347
+ import { useUnsubscribe } from 'payload-subscribers-plugin/ui'
348
348
 
349
349
  function UnsubscribePage() {
350
350
  const searchParams = useSearchParams()
@@ -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?: (result: OptInChannel[]) => void;
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 { serverURL } = useServerUrl();
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
- async function verify() {
34
- const sdk = new PayloadSDK({
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
- setAllOptInChannels(channels);
35
+ if (channels) {
36
+ setAllOptInChannels(channels);
37
+ }
38
+ setIsLoaded(true);
60
39
  }, [
61
- result,
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
- !result ? /*#__PURE__*/ _jsx("p", {
52
+ !isLoaded ? /*#__PURE__*/ _jsx("p", {
74
53
  className: mergeClassNames([
75
54
  styles.loading,
76
55
  classNames.loading
77
56
  ]),
78
- children: "verifying..."
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 { PayloadSDK } from '@payloadcms/sdk'\nimport { useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { GetOptInChannelsResponse } from '../../endpoints/getOptInChannels.js'\n\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\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?: (result: OptInChannel[]) => void\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 selectedOptInChannelIDs,\n}: ISelectOptInChannels) => {\n const { serverURL } = useServerUrl()\n // const { serverURL } = { serverURL: 'http://localhost:3001' }\n type OptInChannelCheckbox = {\n isChecked: boolean\n } & OptInChannel\n const [result, setResult] = useState<any>()\n const [allOptInChannels, setAllOptInChannels] = useState<OptInChannelCheckbox[]>([])\n\n useEffect(() => {\n async function verify() {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n console.log('calling optinchannels endpoint')\n const result = await sdk.request({\n method: 'GET',\n path: '/api/optinchannels',\n })\n if (result.ok) {\n const resultJson: GetOptInChannelsResponse = await result.json()\n setResult(resultJson)\n } else {\n const resultText = await result.text()\n setResult(resultText)\n }\n }\n void verify()\n }, [serverURL])\n\n useEffect(() => {\n const channels = result?.optInChannels?.map((channel: OptInChannel) => ({\n ...channel,\n isChecked: selectedOptInChannelIDs?.includes(channel.id),\n }))\n setAllOptInChannels(channels)\n }, [result, selectedOptInChannelIDs])\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h3>Opt-in Channels</h3>\n {!result ? (\n <p className={mergeClassNames([styles.loading, classNames.loading])}>verifying...</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":["PayloadSDK","useEffect","useState","useServerUrl","mergeClassNames","styles","SelectOptInChannels","classNames","button","container","error","form","loading","message","optInCheckbox","optInCheckboxItem","optInCheckboxLabel","optionsGroup","handleOptInChannelsSelected","selectedOptInChannelIDs","serverURL","result","setResult","allOptInChannels","setAllOptInChannels","verify","sdk","baseURL","console","log","request","method","path","ok","resultJson","json","resultText","text","channels","optInChannels","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":"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"}
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: null | Subscriber;
9
+ subscriber: ({
10
+ optIns?: null | OptInChannel[];
11
+ } & Omit<Subscriber, 'optIns'>) | null;
10
12
  };
11
13
  /** Props for SubscriberProvider. */
12
14
  interface ProviderProps {
@@ -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: string[] })>(null)\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 { permissions, subscriber } = await authResponse.json()\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","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,SAAqD;IAEzF,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,MAAM,EAAEP,WAAW,EAAEL,UAAU,EAAE,GAAG,MAAMS,aAAaI,IAAI;oBAC3D,2CAA2C;oBAC3C,6CAA6C;oBAC7CP,eAAeD;oBACfJ,cAAcD;gBAChB,OAAO;oBACLM,eAAe;oBACfL,cAAc;gBAChB;YACF,EAAE,OAAOa,OAAgB;gBACvBC,QAAQC,GAAG,CAAC,CAAC,kBAAkB,CAAC,EAAEF;YACpC;YACAV,YAAY;QACd;QACA,MAAMI;IACR,GAAG;QAACN;KAAU;IAEd,MAAMe,SAAS5B,YAAY;QACzBe,YAAY;QACZ,IAAI;YACF,uCAAuC;YACvC,8BAA8B;YAC9B,KAAK;YACL,6CAA6C;YAC7C,cAAc;YACd,oBAAoB;YACpB,yBAAyB;YACzB,KAAK;YACL,oCAAoC;YACpC,MAAMc,iBAAiB,MAAMR,MAAM,eAAe;gBAChDC,QAAQ;YACV;YAEA,gDAAgD;YAEhD,IAAIO,eAAeN,EAAE,EAAE;gBACrBX,cAAc;gBACdK,eAAe;YACjB;QACF,EAAE,OAAOQ,OAAgB;YACvBC,QAAQC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAEF;QACtC;QACAV,YAAY;IACd,GAAG,EAAE;IAELd,UAAU;QACR,KAAKiB;IACP,GAAG;QAACA;KAAkB;IAEtB,mEAAmE;IACnE,MAAMY,eAAsC1B,QAC1C,IAAO,CAAA;YACLU;YACAc;YACAZ;YACAE;YACAP;QACF,CAAA,GACA;QAACG;QAAUc;QAAQZ;QAAaE;QAAmBP;KAAW;IAGhE,qBAAO,KAACJ,kBAAkBwB,QAAQ;QAACC,OAAOF;kBAAepB;;AAC3D;AAEA;;;;;CAKC,GACD,OAAO,SAASuB;IACd,MAAMC,UAAU/B,WAAWI;IAC3B,IAAI2B,YAAY1B,WAAW;QACzB,MAAM,IAAI2B,MAAM;IAClB;IACA,OAAOD;AACT"}
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 { permissions, subscriber } = await authResponse.json()\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","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,MAAM,EAAEP,WAAW,EAAEL,UAAU,EAAE,GAAG,MAAMS,aAAaI,IAAI;oBAC3D,2CAA2C;oBAC3C,6CAA6C;oBAC7CP,eAAeD;oBACfJ,cAAcD;gBAChB,OAAO;oBACLM,eAAe;oBACfL,cAAc;gBAChB;YACF,EAAE,OAAOa,OAAgB;gBACvBC,QAAQC,GAAG,CAAC,CAAC,kBAAkB,CAAC,EAAEF;YACpC;YACAV,YAAY;QACd;QACA,MAAMI;IACR,GAAG;QAACN;KAAU;IAEd,MAAMe,SAAS5B,YAAY;QACzBe,YAAY;QACZ,IAAI;YACF,uCAAuC;YACvC,8BAA8B;YAC9B,KAAK;YACL,6CAA6C;YAC7C,cAAc;YACd,oBAAoB;YACpB,yBAAyB;YACzB,KAAK;YACL,oCAAoC;YACpC,MAAMc,iBAAiB,MAAMR,MAAM,eAAe;gBAChDC,QAAQ;YACV;YAEA,gDAAgD;YAEhD,IAAIO,eAAeN,EAAE,EAAE;gBACrBX,cAAc;gBACdK,eAAe;YACjB;QACF,EAAE,OAAOQ,OAAgB;YACvBC,QAAQC,GAAG,CAAC,CAAC,oBAAoB,CAAC,EAAEF;QACtC;QACAV,YAAY;IACd,GAAG,EAAE;IAELd,UAAU;QACR,KAAKiB;IACP,GAAG;QAACA;KAAkB;IAEtB,mEAAmE;IACnE,MAAMY,eAAsC1B,QAC1C,IAAO,CAAA;YACLU;YACAc;YACAZ;YACAE;YACAP;QACF,CAAA,GACA;QAACG;QAAUc;QAAQZ;QAAaE;QAAmBP;KAAW;IAGhE,qBAAO,KAACJ,kBAAkBwB,QAAQ;QAACC,OAAOF;kBAAepB;;AAC3D;AAEA;;;;;CAKC,GACD,OAAO,SAASuB;IACd,MAAMC,UAAU/B,WAAWI;IAC3B,IAAI2B,YAAY1B,WAAW;QACzB,MAAM,IAAI2B,MAAM;IAClB;IACA,OAAOD;AACT"}
@@ -1,5 +1,5 @@
1
- import type { OptInChannel } from '../copied/payload-types.js';
2
1
  import type { Endpoint, PayloadHandler } from 'payload';
2
+ import type { OptInChannel } from '../copied/payload-types.js';
3
3
  export type GetOptInChannelsResponse = {
4
4
  error: string;
5
5
  now: string;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/getOptInChannels.ts"],"sourcesContent":["import type { OptInChannel } from '../copied/payload-types.js'\nimport type { CollectionSlug, Endpoint, PayloadHandler } from 'payload'\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":"AAGA,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
+ {"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)=>typeof channel == 'string' ? channel : channel.id);
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: 400
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: 400 },\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","defaultCollectionSlug","createEndpointSubscriberAuth","subscribersCollectionSlug","subscriberAuthHandler","req","permissions","user","payload","auth","collection","subscriber","optIns","map","channel","id","Response","json","now","Date","toISOString","status","error","subscriberAuthEndpoint","handler","method","path"],"mappings":"AAAA,SAASA,WAAWC,WAAW,QAAQ,kBAAiB;AAOxD,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,MAAMN,UAAU,MAAMC;QAEtB,IAAI;YACF,MAAM,EAAEM,WAAW,EAAEC,IAAI,EAAE,GAAG,MAAMF,IAAIG,OAAO,CAACC,IAAI,CAAC;gBACnDV;YACF;YAEA,4DAA4D;YAC5D,0EAA0E;YAE1E,IAAIQ,QAAQA,KAAKG,UAAU,IAAIP,2BAA2B;gBACxD,MAAMQ,aAAyBJ;gBAC/B,IAAII,WAAWC,MAAM,EAAE;oBACrBD,WAAWC,MAAM,GAAGD,WAAWC,MAAM,CAACC,GAAG,CAAC,CAACC,UACzC,OAAOA,WAAW,WAAWA,UAAUA,QAAQC,EAAE;gBAErD;gBACA,OAAOC,SAASC,IAAI,CAAC;oBACnBC,KAAK,IAAIC,OAAOC,WAAW;oBAC3Bd;oBACAK;gBACF;YACF;YAEA,yEAAyE;YACzE,OAAOK,SAASC,IAAI,CAClB;gBACE,iCAAiC;gBACjCC,KAAK,IAAIC,OAAOC,WAAW;gBAC3Bd;gBACAK,YAAY;YACd,GACA;gBAAEZ;gBAASsB,QAAQ;YAAI;QAE3B,EAAE,OAAOC,OAAgB;YACvB,4EAA4E;YAC5E,OAAON,SAASC,IAAI,CAClB;gBACEK;gBACAJ,KAAK,IAAIC,OAAOC,WAAW;YAC7B,GACA;gBAAErB;gBAASsB,QAAQ;YAAI;QAE3B;IACF;IAEA,yFAAyF,GACzF,MAAME,yBAAmC;QACvCC,SAASpB;QACTqB,QAAQ;QACRC,MAAM;IACR;IACA,OAAOH;AACT;AAEA,eAAerB,6BAA4B"}
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"}
@@ -10,4 +10,9 @@ export type { VerifyMagicLinkResponse } from '../components/app/VerifyMagicLink.
10
10
  export { VerifyMagicLink } from '../components/app/VerifyMagicLink.js';
11
11
  export type { SubscriberContextType } from '../contexts/SubscriberProvider.js';
12
12
  export { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js';
13
+ export { useRequestMagicLink } from '../hooks/useRequestMagicLink.js';
14
+ export { useSubscribe } from '../hooks/useSubscribe.js';
15
+ export { useUnsubscribe } from '../hooks/useUnsubscribe.js';
16
+ export { useVerifyMagicLink } from '../hooks/useVerifyMagicLink.js';
13
17
  export { getServerUrl } from '../server-functions/serverUrl.js';
18
+ export type { OptInChannel } from 'src/copied/payload-types.js';
@@ -5,6 +5,10 @@ export { SubscriberMenu } from '../components/app/SubscriberMenu.js';
5
5
  export { Unsubscribe } from '../components/app/Unsubscribe.js';
6
6
  export { VerifyMagicLink } from '../components/app/VerifyMagicLink.js';
7
7
  export { SubscriberProvider, useSubscriber } from '../contexts/SubscriberProvider.js';
8
+ export { useRequestMagicLink } from '../hooks/useRequestMagicLink.js';
9
+ export { useSubscribe } from '../hooks/useSubscribe.js';
10
+ export { useUnsubscribe } from '../hooks/useUnsubscribe.js';
11
+ export { useVerifyMagicLink } from '../hooks/useVerifyMagicLink.js';
8
12
  export { getServerUrl } from '../server-functions/serverUrl.js';
9
13
 
10
14
  //# sourceMappingURL=ui.js.map
@@ -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 { getServerUrl } from '../server-functions/serverUrl.js'\n"],"names":["RequestMagicLink","RequestOrSubscribe","Subscribe","SubscriberMenu","Unsubscribe","VerifyMagicLink","SubscriberProvider","useSubscriber","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,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: null | 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","status","setStatus","result","setResult","updateSubscriptions","selectedChannelIDs","subscribeResult","fetch","body","JSON","stringify","email","optIns","method","ok","resultJson","json","emailResult","error"],"mappings":"AAAA;AAEA,SAASA,QAAQ,QAAQ,QAAO;AAOhC,SAASC,aAAa,QAAQ,oCAAmC;AA8BjE;;;;;;;;CAQC,GACD,OAAO,MAAMC,eAAe,CAAC,EAC3BC,eAAe,EACfC,UAAU,EACW;IACrB,MAAM,EAAEC,iBAAiB,EAAEC,UAAU,EAAE,GAAGL;IAE1C,MAAM,CAACM,QAAQC,UAAU,GAAGR,SAAwC;IACpE,MAAM,CAACS,QAAQC,UAAU,GAAGV;IAE5B,MAAMW,sBAAsB,OAAOC;QACjCJ,UAAU;QACVE,UAAU,CAAC,WAAW,CAAC;QACvB,MAAMG,kBAAkB,MAAMC,MAAM,kBAAkB;YACpDC,MAAMC,KAAKC,SAAS,CAAC;gBACnBC,OAAOZ,YAAYY;gBACnBC,QAAQP;gBACRR;YACF;YACAgB,QAAQ;QACV;QACA,IAAIP,gBAAgBQ,EAAE,EAAE;YACtB,MAAMC,aAAgC,MAAMT,gBAAgBU,IAAI;YAChE,wCAAwC;YACxC,MAAM,EAAEC,WAAW,EAAEC,KAAK,EAAE,GAAGH;YAC/B,IAAIG,OAAO;gBACTjB,UAAU;gBACVE,UAAU,CAAC,uCAAuC,EAAEe,OAAO;YAC7D,OAAO,IAAID,aAAa;gBACtBhB,UAAU;gBACVE,UAAU;YACZ,OAAO,IAAIJ,YAAY;gBACrBE,UAAU;gBACVE,UAAU,CAAC,uCAAuC,CAAC;YACrD,OAAO;gBACLF,UAAU;gBACVE,UAAU,CAAC,oDAAoD,CAAC;YAClE;YAEAL;YAEA,IAAIF,iBAAiB;gBACnBA,gBAAgBmB;YAClB;QACF,OAAO;YACL,kDAAkD;YAClDd,UAAU;YACVE,UAAU,CAAC,oDAAoD,CAAC;QAClE;IACF;IAEA,OAAO;QACLD;QACAF;QACAD;QACAK;IACF;AACF,EAAC"}
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,GAAGL;IAEvB,MAAM,CAACM,QAAQC,UAAU,GAAGR,SAAiB;IAC7C,MAAM,CAACS,SAASC,WAAW,GAAGV,SAAkB;IAChD,MAAM,CAACW,WAAWC,aAAa,GAAGZ,SAAkB;IAEpD,MAAMa,cAAcd,YAClB,OAAOe;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,GAAGlB,YAAYA,YAAY,GAAG,gBAAgB,CAAC,EAC/C;gBACEmB,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;QAEb,IAAIR,mBAAmB;YACrBA,kBAAkBa;QACpB;IACF,GACA;QAACZ;QAAWD;QAAmBE;KAAW;IAG5C,OAAO;QAAEG;QAASE;QAAWJ;QAAQM;IAAY;AACnD,EAAC"}
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/package.json CHANGED
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "registry": "https://registry.npmjs.org/",
71
71
  "dependencies": {},
72
- "version": "0.0.10",
72
+ "version": "0.0.12",
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",