payload-plugin-newsletter
Version:
Complete newsletter management plugin for Payload CMS with subscriber management, magic link authentication, and email service integration
860 lines (855 loc) • 27.6 kB
JavaScript
"use client";
"use client";
// src/components/NewsletterForm.tsx
import { useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
var defaultStyles = {
form: {
display: "flex",
flexDirection: "column",
gap: "1rem",
maxWidth: "400px",
margin: "0 auto"
},
inputGroup: {
display: "flex",
flexDirection: "column",
gap: "0.5rem"
},
label: {
fontSize: "0.875rem",
fontWeight: "500",
color: "#374151"
},
input: {
padding: "0.5rem 0.75rem",
fontSize: "1rem",
border: "1px solid #e5e7eb",
borderRadius: "0.375rem",
outline: "none",
transition: "border-color 0.2s"
},
button: {
padding: "0.75rem 1.5rem",
fontSize: "1rem",
fontWeight: "500",
color: "#ffffff",
backgroundColor: "#3b82f6",
border: "none",
borderRadius: "0.375rem",
cursor: "pointer",
transition: "background-color 0.2s"
},
buttonDisabled: {
opacity: 0.5,
cursor: "not-allowed"
},
error: {
fontSize: "0.875rem",
color: "#ef4444",
marginTop: "0.25rem"
},
success: {
fontSize: "0.875rem",
color: "#10b981",
marginTop: "0.25rem"
},
checkbox: {
display: "flex",
alignItems: "center",
gap: "0.5rem"
},
checkboxInput: {
width: "1rem",
height: "1rem"
},
checkboxLabel: {
fontSize: "0.875rem",
color: "#374151"
}
};
var NewsletterForm = ({
onSuccess,
onError,
showName = false,
showPreferences = false,
leadMagnet,
className,
styles: customStyles = {},
apiEndpoint = "/api/newsletter/subscribe",
buttonText = "Subscribe",
loadingText = "Subscribing...",
successMessage = "Successfully subscribed!",
placeholders = {
email: "Enter your email",
name: "Enter your name"
},
labels = {
email: "Email",
name: "Name",
newsletter: "Newsletter updates",
announcements: "Product announcements"
}
}) => {
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [preferences, setPreferences] = useState({
newsletter: true,
announcements: true
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
const styles = {
form: { ...defaultStyles.form, ...customStyles.form },
inputGroup: { ...defaultStyles.inputGroup, ...customStyles.inputGroup },
label: { ...defaultStyles.label, ...customStyles.label },
input: { ...defaultStyles.input, ...customStyles.input },
button: { ...defaultStyles.button, ...customStyles.button },
buttonDisabled: { ...defaultStyles.buttonDisabled, ...customStyles.buttonDisabled },
error: { ...defaultStyles.error, ...customStyles.error },
success: { ...defaultStyles.success, ...customStyles.success },
checkbox: { ...defaultStyles.checkbox, ...customStyles.checkbox },
checkboxInput: { ...defaultStyles.checkboxInput, ...customStyles.checkboxInput },
checkboxLabel: { ...defaultStyles.checkboxLabel, ...customStyles.checkboxLabel }
};
const handleSubmit = async (e) => {
e.preventDefault();
setError(null);
setLoading(true);
try {
const payload = {
email,
...showName && name && { name },
...showPreferences && { preferences },
...leadMagnet && { leadMagnet: leadMagnet.id },
metadata: {
signupPage: window.location.href,
...typeof window !== "undefined" && window.location.search && {
utmParams: Object.fromEntries(new URLSearchParams(window.location.search))
}
}
};
const response = await fetch(apiEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || data.errors?.join(", ") || "Subscription failed");
}
setSuccess(true);
setEmail("");
setName("");
if (onSuccess) {
onSuccess(data.subscriber);
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "An error occurred";
setError(errorMessage);
if (onError) {
onError(new Error(errorMessage));
}
} finally {
setLoading(false);
}
};
if (success && !showPreferences) {
return /* @__PURE__ */ jsx("div", { className, style: styles.form, children: /* @__PURE__ */ jsx("p", { style: styles.success, children: successMessage }) });
}
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className, style: styles.form, children: [
/* @__PURE__ */ jsxs("div", { style: styles.inputGroup, children: [
/* @__PURE__ */ jsx("label", { htmlFor: "email", style: styles.label, children: labels.email }),
/* @__PURE__ */ jsx(
"input",
{
id: "email",
type: "email",
value: email,
onChange: (e) => setEmail(e.target.value),
placeholder: placeholders.email,
required: true,
disabled: loading,
style: {
...styles.input,
...loading && { opacity: 0.5 }
}
}
)
] }),
showName && /* @__PURE__ */ jsxs("div", { style: styles.inputGroup, children: [
/* @__PURE__ */ jsx("label", { htmlFor: "name", style: styles.label, children: labels.name }),
/* @__PURE__ */ jsx(
"input",
{
id: "name",
type: "text",
value: name,
onChange: (e) => setName(e.target.value),
placeholder: placeholders.name,
disabled: loading,
style: {
...styles.input,
...loading && { opacity: 0.5 }
}
}
)
] }),
showPreferences && /* @__PURE__ */ jsxs("div", { style: styles.inputGroup, children: [
/* @__PURE__ */ jsx("label", { style: styles.label, children: "Email Preferences" }),
/* @__PURE__ */ jsxs("div", { style: styles.checkbox, children: [
/* @__PURE__ */ jsx(
"input",
{
id: "newsletter",
type: "checkbox",
checked: preferences.newsletter,
onChange: (e) => setPreferences({ ...preferences, newsletter: e.target.checked }),
disabled: loading,
style: styles.checkboxInput
}
),
/* @__PURE__ */ jsx("label", { htmlFor: "newsletter", style: styles.checkboxLabel, children: labels.newsletter })
] }),
/* @__PURE__ */ jsxs("div", { style: styles.checkbox, children: [
/* @__PURE__ */ jsx(
"input",
{
id: "announcements",
type: "checkbox",
checked: preferences.announcements,
onChange: (e) => setPreferences({ ...preferences, announcements: e.target.checked }),
disabled: loading,
style: styles.checkboxInput
}
),
/* @__PURE__ */ jsx("label", { htmlFor: "announcements", style: styles.checkboxLabel, children: labels.announcements })
] })
] }),
/* @__PURE__ */ jsx(
"button",
{
type: "submit",
disabled: loading,
style: {
...styles.button,
...loading && styles.buttonDisabled
},
children: loading ? loadingText : buttonText
}
),
error && /* @__PURE__ */ jsx("p", { style: styles.error, children: error }),
success && /* @__PURE__ */ jsx("p", { style: styles.success, children: successMessage })
] });
};
function createNewsletterForm(defaultProps) {
return (props) => /* @__PURE__ */ jsx(NewsletterForm, { ...defaultProps, ...props });
}
// src/components/PreferencesForm.tsx
import { useState as useState2, useEffect } from "react";
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
var defaultStyles2 = {
container: {
maxWidth: "600px",
margin: "0 auto",
padding: "2rem"
},
heading: {
fontSize: "1.5rem",
fontWeight: "600",
marginBottom: "1.5rem",
color: "#111827"
},
form: {
display: "flex",
flexDirection: "column",
gap: "1.5rem"
},
section: {
padding: "1.5rem",
backgroundColor: "#f9fafb",
borderRadius: "0.5rem",
border: "1px solid #e5e7eb"
},
sectionTitle: {
fontSize: "1.125rem",
fontWeight: "500",
marginBottom: "1rem",
color: "#111827"
},
inputGroup: {
display: "flex",
flexDirection: "column",
gap: "0.5rem"
},
label: {
fontSize: "0.875rem",
fontWeight: "500",
color: "#374151"
},
input: {
padding: "0.5rem 0.75rem",
fontSize: "1rem",
border: "1px solid #e5e7eb",
borderRadius: "0.375rem",
outline: "none",
transition: "border-color 0.2s"
},
select: {
padding: "0.5rem 0.75rem",
fontSize: "1rem",
border: "1px solid #e5e7eb",
borderRadius: "0.375rem",
outline: "none",
backgroundColor: "#ffffff"
},
checkbox: {
display: "flex",
alignItems: "center",
gap: "0.5rem",
marginBottom: "0.5rem"
},
checkboxInput: {
width: "1rem",
height: "1rem"
},
checkboxLabel: {
fontSize: "0.875rem",
color: "#374151"
},
buttonGroup: {
display: "flex",
gap: "1rem",
marginTop: "1rem"
},
button: {
padding: "0.75rem 1.5rem",
fontSize: "1rem",
fontWeight: "500",
borderRadius: "0.375rem",
cursor: "pointer",
transition: "all 0.2s",
border: "none"
},
primaryButton: {
color: "#ffffff",
backgroundColor: "#3b82f6"
},
secondaryButton: {
color: "#374151",
backgroundColor: "#ffffff",
border: "1px solid #e5e7eb"
},
dangerButton: {
color: "#ffffff",
backgroundColor: "#ef4444"
},
error: {
fontSize: "0.875rem",
color: "#ef4444",
marginTop: "0.5rem"
},
success: {
fontSize: "0.875rem",
color: "#10b981",
marginTop: "0.5rem"
},
info: {
fontSize: "0.875rem",
color: "#6b7280",
marginTop: "0.5rem"
}
};
var PreferencesForm = ({
subscriber: initialSubscriber,
onSuccess,
onError,
className,
styles: customStyles = {},
sessionToken,
apiEndpoint = "/api/newsletter/preferences",
showUnsubscribe = true,
locales = ["en"],
labels = {
title: "Newsletter Preferences",
personalInfo: "Personal Information",
emailPreferences: "Email Preferences",
name: "Name",
language: "Preferred Language",
newsletter: "Newsletter updates",
announcements: "Product announcements",
saveButton: "Save Preferences",
unsubscribeButton: "Unsubscribe",
saving: "Saving...",
saved: "Preferences saved successfully!",
unsubscribeConfirm: "Are you sure you want to unsubscribe? This cannot be undone."
}
}) => {
const [subscriber, setSubscriber] = useState2(initialSubscriber || {});
const [loading, setLoading] = useState2(false);
const [loadingData, setLoadingData] = useState2(!initialSubscriber);
const [error, setError] = useState2(null);
const [success, setSuccess] = useState2(false);
const styles = {
container: { ...defaultStyles2.container, ...customStyles.container },
heading: { ...defaultStyles2.heading, ...customStyles.heading },
form: { ...defaultStyles2.form, ...customStyles.form },
section: { ...defaultStyles2.section, ...customStyles.section },
sectionTitle: { ...defaultStyles2.sectionTitle, ...customStyles.sectionTitle },
inputGroup: { ...defaultStyles2.inputGroup, ...customStyles.inputGroup },
label: { ...defaultStyles2.label, ...customStyles.label },
input: { ...defaultStyles2.input, ...customStyles.input },
select: { ...defaultStyles2.select, ...customStyles.select },
checkbox: { ...defaultStyles2.checkbox, ...customStyles.checkbox },
checkboxInput: { ...defaultStyles2.checkboxInput, ...customStyles.checkboxInput },
checkboxLabel: { ...defaultStyles2.checkboxLabel, ...customStyles.checkboxLabel },
buttonGroup: { ...defaultStyles2.buttonGroup, ...customStyles.buttonGroup },
button: { ...defaultStyles2.button, ...customStyles.button },
primaryButton: { ...defaultStyles2.primaryButton, ...customStyles.primaryButton },
secondaryButton: { ...defaultStyles2.secondaryButton, ...customStyles.secondaryButton },
dangerButton: { ...defaultStyles2.dangerButton, ...customStyles.dangerButton },
error: { ...defaultStyles2.error, ...customStyles.error },
success: { ...defaultStyles2.success, ...customStyles.success },
info: { ...defaultStyles2.info, ...customStyles.info }
};
useEffect(() => {
if (!initialSubscriber && sessionToken) {
fetchPreferences();
}
}, []);
const fetchPreferences = async () => {
try {
const response = await fetch(apiEndpoint, {
headers: {
"Authorization": `Bearer ${sessionToken}`
}
});
if (!response.ok) {
throw new Error("Failed to load preferences");
}
const data = await response.json();
setSubscriber(data.subscriber);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to load preferences");
if (onError) {
onError(err instanceof Error ? err : new Error("Failed to load preferences"));
}
} finally {
setLoadingData(false);
}
};
const handleSave = async (e) => {
e.preventDefault();
setError(null);
setSuccess(false);
setLoading(true);
try {
const response = await fetch(apiEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${sessionToken}`
},
body: JSON.stringify({
name: subscriber.name,
locale: subscriber.locale,
emailPreferences: subscriber.emailPreferences
})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || "Failed to save preferences");
}
setSubscriber(data.subscriber);
setSuccess(true);
if (onSuccess) {
onSuccess(data.subscriber);
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "An error occurred";
setError(errorMessage);
if (onError) {
onError(new Error(errorMessage));
}
} finally {
setLoading(false);
}
};
const handleUnsubscribe = async () => {
if (!window.confirm(labels.unsubscribeConfirm)) {
return;
}
setLoading(true);
setError(null);
try {
const response = await fetch("/api/newsletter/unsubscribe", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${sessionToken}`
},
body: JSON.stringify({
email: subscriber.email
})
});
if (!response.ok) {
throw new Error("Failed to unsubscribe");
}
setSubscriber({ ...subscriber, subscriptionStatus: "unsubscribed" });
if (onSuccess) {
onSuccess({ ...subscriber, subscriptionStatus: "unsubscribed" });
}
} catch (err) {
setError("Failed to unsubscribe. Please try again.");
if (onError) {
onError(err instanceof Error ? err : new Error("Failed to unsubscribe"));
}
} finally {
setLoading(false);
}
};
if (loadingData) {
return /* @__PURE__ */ jsx2("div", { className, style: styles.container, children: /* @__PURE__ */ jsx2("p", { style: styles.info, children: "Loading preferences..." }) });
}
if (subscriber.subscriptionStatus === "unsubscribed") {
return /* @__PURE__ */ jsxs2("div", { className, style: styles.container, children: [
/* @__PURE__ */ jsx2("h2", { style: styles.heading, children: "Unsubscribed" }),
/* @__PURE__ */ jsx2("p", { style: styles.info, children: "You have been unsubscribed from all emails. To resubscribe, please sign up again." })
] });
}
return /* @__PURE__ */ jsxs2("div", { className, style: styles.container, children: [
/* @__PURE__ */ jsx2("h2", { style: styles.heading, children: labels.title }),
/* @__PURE__ */ jsxs2("form", { onSubmit: handleSave, style: styles.form, children: [
/* @__PURE__ */ jsxs2("div", { style: styles.section, children: [
/* @__PURE__ */ jsx2("h3", { style: styles.sectionTitle, children: labels.personalInfo }),
/* @__PURE__ */ jsxs2("div", { style: styles.inputGroup, children: [
/* @__PURE__ */ jsx2("label", { htmlFor: "name", style: styles.label, children: labels.name }),
/* @__PURE__ */ jsx2(
"input",
{
id: "name",
type: "text",
value: subscriber.name || "",
onChange: (e) => setSubscriber({ ...subscriber, name: e.target.value }),
disabled: loading,
style: styles.input
}
)
] }),
locales.length > 1 && /* @__PURE__ */ jsxs2("div", { style: styles.inputGroup, children: [
/* @__PURE__ */ jsx2("label", { htmlFor: "locale", style: styles.label, children: labels.language }),
/* @__PURE__ */ jsx2(
"select",
{
id: "locale",
value: subscriber.locale || locales[0],
onChange: (e) => setSubscriber({ ...subscriber, locale: e.target.value }),
disabled: loading,
style: styles.select,
children: locales.map((locale) => /* @__PURE__ */ jsx2("option", { value: locale, children: locale.toUpperCase() }, locale))
}
)
] })
] }),
/* @__PURE__ */ jsxs2("div", { style: styles.section, children: [
/* @__PURE__ */ jsx2("h3", { style: styles.sectionTitle, children: labels.emailPreferences }),
/* @__PURE__ */ jsxs2("div", { style: styles.checkbox, children: [
/* @__PURE__ */ jsx2(
"input",
{
id: "pref-newsletter",
type: "checkbox",
checked: subscriber.emailPreferences?.newsletter ?? true,
onChange: (e) => setSubscriber({
...subscriber,
emailPreferences: {
...subscriber.emailPreferences,
newsletter: e.target.checked
}
}),
disabled: loading,
style: styles.checkboxInput
}
),
/* @__PURE__ */ jsx2("label", { htmlFor: "pref-newsletter", style: styles.checkboxLabel, children: labels.newsletter })
] }),
/* @__PURE__ */ jsxs2("div", { style: styles.checkbox, children: [
/* @__PURE__ */ jsx2(
"input",
{
id: "pref-announcements",
type: "checkbox",
checked: subscriber.emailPreferences?.announcements ?? true,
onChange: (e) => setSubscriber({
...subscriber,
emailPreferences: {
...subscriber.emailPreferences,
announcements: e.target.checked
}
}),
disabled: loading,
style: styles.checkboxInput
}
),
/* @__PURE__ */ jsx2("label", { htmlFor: "pref-announcements", style: styles.checkboxLabel, children: labels.announcements })
] })
] }),
/* @__PURE__ */ jsxs2("div", { style: styles.buttonGroup, children: [
/* @__PURE__ */ jsx2(
"button",
{
type: "submit",
disabled: loading,
style: {
...styles.button,
...styles.primaryButton,
...loading && { opacity: 0.5, cursor: "not-allowed" }
},
children: loading ? labels.saving : labels.saveButton
}
),
showUnsubscribe && /* @__PURE__ */ jsx2(
"button",
{
type: "button",
onClick: handleUnsubscribe,
disabled: loading,
style: {
...styles.button,
...styles.dangerButton,
...loading && { opacity: 0.5, cursor: "not-allowed" }
},
children: labels.unsubscribeButton
}
)
] }),
error && /* @__PURE__ */ jsx2("p", { style: styles.error, children: error }),
success && /* @__PURE__ */ jsx2("p", { style: styles.success, children: labels.saved })
] })
] });
};
function createPreferencesForm(defaultProps) {
return (props) => /* @__PURE__ */ jsx2(PreferencesForm, { ...defaultProps, ...props });
}
// src/components/MagicLinkVerify.tsx
import { useState as useState3, useEffect as useEffect2 } from "react";
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
var defaultStyles3 = {
container: {
maxWidth: "400px",
margin: "4rem auto",
padding: "2rem",
textAlign: "center"
},
heading: {
fontSize: "1.5rem",
fontWeight: "600",
marginBottom: "1rem",
color: "#111827"
},
message: {
fontSize: "1rem",
color: "#6b7280",
marginBottom: "1.5rem"
},
error: {
fontSize: "1rem",
color: "#ef4444",
marginBottom: "1.5rem"
},
button: {
padding: "0.75rem 1.5rem",
fontSize: "1rem",
fontWeight: "500",
color: "#ffffff",
backgroundColor: "#3b82f6",
border: "none",
borderRadius: "0.375rem",
cursor: "pointer",
transition: "background-color 0.2s"
}
};
var MagicLinkVerify = ({
token: propToken,
onSuccess,
onError,
apiEndpoint = "/api/newsletter/verify-magic-link",
className,
styles: customStyles = {},
labels = {
verifying: "Verifying your magic link...",
success: "Successfully verified! Redirecting...",
error: "Failed to verify magic link",
expired: "This magic link has expired. Please request a new one.",
invalid: "This magic link is invalid. Please request a new one.",
redirecting: "Redirecting to your preferences...",
tryAgain: "Try Again"
}
}) => {
const [status, setStatus] = useState3("verifying");
const [error, setError] = useState3(null);
const [_sessionToken, setSessionToken] = useState3(null);
const styles = {
container: { ...defaultStyles3.container, ...customStyles.container },
heading: { ...defaultStyles3.heading, ...customStyles.heading },
message: { ...defaultStyles3.message, ...customStyles.message },
error: { ...defaultStyles3.error, ...customStyles.error },
button: { ...defaultStyles3.button, ...customStyles.button }
};
useEffect2(() => {
const token = propToken || new URLSearchParams(window.location.search).get("token");
if (token) {
verifyToken(token);
} else {
setStatus("error");
setError(labels.invalid || "Invalid magic link");
}
}, [propToken]);
const verifyToken = async (token) => {
try {
const response = await fetch(apiEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ token })
});
const data = await response.json();
if (!response.ok) {
if (data.error?.includes("expired")) {
throw new Error(labels.expired);
}
throw new Error(data.error || labels.error);
}
setStatus("success");
setSessionToken(data.sessionToken);
if (typeof window !== "undefined" && data.sessionToken) {
localStorage.setItem("newsletter_session", data.sessionToken);
}
if (onSuccess) {
onSuccess(data.sessionToken, data.subscriber);
}
} catch (err) {
setStatus("error");
const errorMessage = err instanceof Error ? err.message : labels.error || "Verification failed";
setError(errorMessage);
if (onError) {
onError(err instanceof Error ? err : new Error(errorMessage));
}
}
};
const handleTryAgain = () => {
window.location.href = "/";
};
return /* @__PURE__ */ jsxs3("div", { className, style: styles.container, children: [
status === "verifying" && /* @__PURE__ */ jsxs3(Fragment, { children: [
/* @__PURE__ */ jsx3("h2", { style: styles.heading, children: "Verifying" }),
/* @__PURE__ */ jsx3("p", { style: styles.message, children: labels.verifying })
] }),
status === "success" && /* @__PURE__ */ jsxs3(Fragment, { children: [
/* @__PURE__ */ jsx3("h2", { style: styles.heading, children: "Success!" }),
/* @__PURE__ */ jsx3("p", { style: styles.message, children: labels.success })
] }),
status === "error" && /* @__PURE__ */ jsxs3(Fragment, { children: [
/* @__PURE__ */ jsx3("h2", { style: styles.heading, children: "Verification Failed" }),
/* @__PURE__ */ jsx3("p", { style: styles.error, children: error }),
/* @__PURE__ */ jsx3("button", { onClick: handleTryAgain, style: styles.button, children: labels.tryAgain })
] })
] });
};
function createMagicLinkVerify(defaultProps) {
return (props) => /* @__PURE__ */ jsx3(MagicLinkVerify, { ...defaultProps, ...props });
}
// src/hooks/useNewsletterAuth.ts
import { useState as useState4, useEffect as useEffect3, useCallback } from "react";
function useNewsletterAuth(_options = {}) {
const [subscriber, setSubscriber] = useState4(null);
const [isLoading, setIsLoading] = useState4(true);
const [error, setError] = useState4(null);
const checkAuth = useCallback(async () => {
try {
const response = await fetch("/api/newsletter/me", {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json"
}
});
if (response.ok) {
const data = await response.json();
setSubscriber(data.subscriber);
setError(null);
} else {
setSubscriber(null);
if (response.status !== 401) {
setError(new Error("Failed to check authentication"));
}
}
} catch (err) {
console.error("Auth check failed:", err);
setError(err instanceof Error ? err : new Error("An error occurred"));
setSubscriber(null);
} finally {
setIsLoading(false);
}
}, []);
useEffect3(() => {
checkAuth();
}, [checkAuth]);
const signOut = useCallback(async () => {
try {
const response = await fetch("/api/newsletter/signout", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json"
}
});
if (response.ok) {
setSubscriber(null);
setError(null);
} else {
throw new Error("Failed to sign out");
}
} catch (err) {
console.error("Sign out error:", err);
setError(err instanceof Error ? err : new Error("Sign out failed"));
throw err;
}
}, []);
const refreshAuth = useCallback(async () => {
setIsLoading(true);
await checkAuth();
}, [checkAuth]);
const login = useCallback(async (_token) => {
await refreshAuth();
}, [refreshAuth]);
return {
subscriber,
isAuthenticated: !!subscriber,
isLoading,
loading: isLoading,
// Alias for backward compatibility
error,
signOut,
logout: signOut,
// Alias for backward compatibility
refreshAuth,
refreshSubscriber: refreshAuth,
// Alias for backward compatibility
login
// For backward compatibility
};
}
export {
MagicLinkVerify,
NewsletterForm,
PreferencesForm,
createMagicLinkVerify,
createNewsletterForm,
createPreferencesForm,
useNewsletterAuth
};