Skip to content

Commit

Permalink
maybe add unsubscribe page (#256)
Browse files Browse the repository at this point in the history
Adds a `/user/unsubscribe?email=<email>` page to allow one-click unsubscribing.
  • Loading branch information
elijahcarrel authored Jan 23, 2024
1 parent d8c9808 commit a6c7e08
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/src/handler/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "../util/error";
import {
createUser as serviceCreateUser,
unsubscribeUser as serviceUnsubscribeUser,
putUser as servicePutUser,
} from "../service/user";
import { InputUser } from "../api-models/user";
Expand Down Expand Up @@ -59,6 +60,9 @@ export const putUser = async (
args: { publicId: string; user: InputUser },
) => servicePutUser(args.publicId, args.user);

export const unsubscribeUser = async (_: any, args: { email: string }) =>
serviceUnsubscribeUser(args.email);

export const resendVerificationEmail = async (
_: any,
args: { email: string },
Expand Down
3 changes: 3 additions & 0 deletions api/src/router/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { gql } from "graphql-tag";
import {
createUser,
createUserWithoutEmail,
unsubscribeUser,
putUser,
resendVerificationEmail,
submitContactForm,
Expand Down Expand Up @@ -33,6 +34,7 @@ export const typeDefs = gql`
createUser(email: String!): User
createUserWithoutEmail: User
putUser(publicId: String!, user: InputUser): User
unsubscribeUser(email: String!): User
resendVerificationEmail(email: String!): Boolean
verifyEmail(email: String!, verificationHash: String!): Boolean
submitContactForm(
Expand All @@ -52,6 +54,7 @@ export const resolvers = {
Mutation: {
createUser,
createUserWithoutEmail,
unsubscribeUser,
putUser,
resendVerificationEmail,
verifyEmail,
Expand Down
12 changes: 12 additions & 0 deletions api/src/service/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { sendVerificationEmail } from "./email/templates/send-verification-email
import {
internalEmailSendError,
invalidUserByPublicIdError,
invalidUserError,
userInputErrorOptions,
} from "../util/error";
import { InputUser } from "../api-models/user";
Expand Down Expand Up @@ -94,3 +95,14 @@ export const putUser = async (publicId: string, inputUser: InputUser) => {
}
return user;
};

export const unsubscribeUser = async (email: string) => {
const user = await User.findOne({ email: email?.toLowerCase() || "" }).exec();
if (user == null) {
throw invalidUserError(email);
}

user.enabled = false;
await user.save();
return user;
};
79 changes: 79 additions & 0 deletions pages/user/unsubscribe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useMutation } from "@apollo/client";
import gql from "graphql-tag";
import Router from "next/router";
import React, { useEffect } from "react";
import toast from "react-hot-toast";
import { Layout } from "../../common-components/Layout/Layout";
import {
handleGraphQlResponse,
stringifyGraphQlError,
useUrlQuery,
} from "../../lib/utils";
import { H3 } from "../../common-components/H3/H3";
import { CommonLink } from "../../common-components/CommonLink/CommonLink";

const UnsubscribeUserPage = () => {
const [urlQuery, urlQueryIsReady] = useUrlQuery();
const email = `${urlQuery.email || ""}`;
const mutation = gql`
mutation unsubscribeUser($email: String!) {
unsubscribeUser(email: $email) {
email
}
}
`;
interface UnsubscribeUserResponse {
unsubscribeUser: {
email: string;
};
}
const [unsubscribeUserMutation, { error, loading }] =
useMutation<UnsubscribeUserResponse>(mutation, { variables: { email } });

const title = "Unsubscribing...";

useEffect(() => {
if (urlQueryIsReady && email.length > 0) {
handleGraphQlResponse<UnsubscribeUserResponse>(
unsubscribeUserMutation(),
).then(async ({ success, result }) => {
if (success) {
toast.success("Account is now disabled.");
const url = {
pathname: "/user",
query: {
email: result?.data?.unsubscribeUser.email,
},
};
await Router.push(url, url, { shallow: true });
}
});
}
}, [urlQueryIsReady, email, unsubscribeUserMutation]);

const errorAction = (
<H3>
Try heading over to your{" "}
<CommonLink href="/user/edit">account page</CommonLink> to unsubscribe.
</H3>
);

if (error) {
return (
<Layout error={stringifyGraphQlError(error)} errorAction={errorAction} />
);
}

if (urlQueryIsReady && !loading && email?.length === 0) {
return (
<Layout
error={"No email was found in the URL."}
errorAction={errorAction}
/>
);
}

return <Layout title={title} isLoading />;
};

export default UnsubscribeUserPage;

1 comment on commit a6c7e08

@vercel
Copy link

@vercel vercel bot commented on a6c7e08 Jan 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.