diff --git a/components/Icons/EmailTiny.tsx b/components/Icons/EmailTiny.tsx new file mode 100644 index 00000000..b6f448f7 --- /dev/null +++ b/components/Icons/EmailTiny.tsx @@ -0,0 +1,20 @@ +import { Props } from "./Props"; +export const EmailTiny = (props: Props) => { + return ( + + + + ); +}; diff --git a/components/Subscribe/EmailSubscribe.tsx b/components/Subscribe/EmailSubscribe.tsx index 5eeedbcd..8fb9fa08 100644 --- a/components/Subscribe/EmailSubscribe.tsx +++ b/components/Subscribe/EmailSubscribe.tsx @@ -7,6 +7,7 @@ import { theme } from "tailwind.config"; export const EmailInput = (props: { action: React.ReactNode; + leading?: React.ReactNode; autoFocus?: boolean; large?: boolean; value: string; @@ -29,6 +30,11 @@ export const EmailInput = (props: { } } > + {props.leading && ( +
+ {props.leading} +
+ )}
- + {props.leading ?? }
void; + onAtSuccess?: () => void; + leading?: React.ReactNode; user: { loggedIn: boolean; email: string | undefined; @@ -78,50 +80,57 @@ export const SubscribeWithHandle = (props: { if (props.user.loggedIn && props.user.handle) { return (
- { - if (subscribing) return; - setSubscribing(true); - setOauthError(null); - let url = new URL(window.location.href); - url.searchParams.set("refreshAuth", ""); - let result = await subscribeToPublication( - props.publicationUri, - url.toString(), - ); - if (!result.success) { - if (isOAuthSessionError(result.error)) - setOauthError(result.error); +
+ {props.leading} + { + if (subscribing) return; + setSubscribing(true); + setOauthError(null); + let url = new URL(window.location.href); + url.searchParams.set("refreshAuth", ""); + let result = await subscribeToPublication( + props.publicationUri, + url.toString(), + ); + if (!result.success) { + if (isOAuthSessionError(result.error)) + setOauthError(result.error); + setSubscribing(false); + return; + } + if (props.onAtSuccess) { + props.onAtSuccess(); + } else { + toaster({ + content:
You're Subscribed!
, + type: "success", + }); + } + props.onSubscribed?.(); setSubscribing(false); - return; - } - toaster({ - content:
You're Subscribed!
, - type: "success", - }); - props.onSubscribed?.(); - setSubscribing(false); - }} - > - {subscribing ? ( - - ) : ( - <> - Subscribe as - - + }} + > + {subscribing ? ( + + ) : ( + <> + Subscribe as + + -
{props.user.handle}
-
- - )} -
+
{props.user.handle}
+ + + )} + +
{oauthError && ( { let trimmed = handle.trim(); if (!trimmed) return; diff --git a/components/Subscribe/SubscribeButton.tsx b/components/Subscribe/SubscribeButton.tsx index f4c5bfce..52df92f4 100644 --- a/components/Subscribe/SubscribeButton.tsx +++ b/components/Subscribe/SubscribeButton.tsx @@ -1,7 +1,7 @@ "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; -import { SubscribeWithHandle } from "./HandleSubscribe"; +import { SubscribeWithHandle, AtSubscribeSuccess } from "./HandleSubscribe"; import { EmailInput, EmailConfirm } from "./EmailSubscribe"; import { EmailSubscribeSuccess } from "./EmailSubscribeSuccess"; import { LinkIdentityModal } from "./LinkIdentityModal"; @@ -10,6 +10,9 @@ import { ButtonPrimary } from "components/Buttons"; import { ManageSubscription } from "./ManageSubscribe"; import { useToaster } from "components/Toast"; import { useIdentityData } from "components/IdentityProvider"; +import { AtmosphereAccount } from "components/Icons/AtmosphereAccount"; +import { EmailTiny } from "components/Icons/EmailTiny"; +import { Menu, RadioMenuGroup, RadioMenuItem } from "components/Menu"; import { requestPublicationEmailSubscription, confirmPublicationEmailSubscription, @@ -17,6 +20,8 @@ import { import { useViewerSubscription } from "./viewerSubscription"; +type SubscribeMode = "email" | "atproto"; + export type SubscribeProps = { autoFocus?: boolean; publicationUri: string; @@ -78,7 +83,7 @@ export const SubscribeInput = (props: SubscribeProps) => { let toaster = useToaster(); let router = useRouter(); const user = useViewerSubscription(props.publicationUri); - const { identity } = useIdentityData(); + const { identity, mutate: mutateIdentity } = useIdentityData(); let [email, setEmail] = useState(user.email ?? ""); let [confirmState, setConfirmState] = useState<"confirm" | "success">( "confirm", @@ -88,6 +93,8 @@ export const SubscribeInput = (props: SubscribeProps) => { let [confirming, setConfirming] = useState(false); let [locallySubscribed, setLocallySubscribed] = useState(false); let [linkModalOpen, setLinkModalOpen] = useState(false); + let [subscribeMode, setSubscribeMode] = useState("email"); + let [atSuccessOpen, setAtSuccessOpen] = useState(false); // Tracks that the user passed through LinkIdentityModal — when they enter // the confirmation code we attach the email to their current atp identity // (or merge from any existing email-only identity) instead of creating a @@ -125,6 +132,9 @@ export const SubscribeInput = (props: SubscribeProps) => { ? user.emailSubscribed : user.atprotoSubscribed; const isSubscribed = showManage || locallySubscribed; + const modeMenu = ( + + ); return ( <>
@@ -180,30 +190,40 @@ export const SubscribeInput = (props: SubscribeProps) => { ) : props.newsletterMode ? (
- { - if (requesting) return; - if (needsLinkConfirmation) { - setLinkModalOpen(true); - return; - } - await sendRequest(false); - }} - > - Subscribe - - } - /> + {subscribeMode === "email" ? ( + { + if (!email || requesting) return; + if (needsLinkConfirmation) { + setLinkModalOpen(true); + return; + } + await sendRequest(false); + }} + > + Subscribe + + } + /> + ) : ( + setAtSuccessOpen(true)} + leading={modeMenu} + /> + )}
) : ( { /> )} {props.newsletterMode && ( - { - setConfirmOpen(open); - if (!open) { - if (confirmState === "success") setLocallySubscribed(true); - setConfirmState("confirm"); - setLinkToCurrent(false); - } - }} - > - {confirmState === "success" ? ( + <> + { + setAtSuccessOpen(open); + if (!open) { + setLocallySubscribed(true); + mutateIdentity(); + router.refresh(); + } + }} + > + + + { + setConfirmOpen(open); + if (!open) { + if (confirmState === "success") setLocallySubscribed(true); + setConfirmState("confirm"); + setLinkToCurrent(false); + } + }} + > + {confirmState === "success" ? ( ) : ( { }} /> )} - + + )} ); }; +const SubscribeModeMenu = (props: { + mode: SubscribeMode; + onChange: (mode: SubscribeMode) => void; +}) => { + return ( + + {props.mode === "email" ? : } + + } + > + props.onChange(v as SubscribeMode)} + > + + + Email + + + + + Atmosphere handle + + + + + ); +}; + type SubscribeError = | "invalid_email" | "newsletter_disabled"