Skip to content

Commit 1ecb9f6

Browse files
authored
♻️ Move disable notifications api (#1490)
1 parent 26e4d5e commit 1ecb9f6

File tree

12 files changed

+106
-341
lines changed

12 files changed

+106
-341
lines changed

apps/web/next.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const nextConfig = {
5151
destination: "/settings/profile",
5252
permanent: true,
5353
},
54+
{
55+
source: "/auth/disable-notifications",
56+
destination: "/api/notifications/unsubscribe",
57+
permanent: true,
58+
},
5459
];
5560
},
5661
experimental: {

apps/web/public/locales/en/app.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,12 @@
198198
"requireParticipantEmailLabel": "Make email address required for participants",
199199
"hideParticipantsLabel": "Hide participant list from other participants",
200200
"hideScoresLabel": "Hide scores until after a participant has voted",
201-
"authErrorTitle": "Login Error",
202-
"authErrorDescription": "There was an error logging you in. Please try again.",
203-
"authErrorCta": "Go to login page",
204201
"continueAs": "Continue as",
205202
"pageMovedDescription": "Redirecting to <a>{newUrl}</a>",
206203
"notRegistered": "Don't have an account? <a>Register</a>",
207204
"unlockFeatures": "Unlock all Pro features.",
208205
"pollStatusFinalized": "Finalized",
209206
"share": "Share",
210-
"pageXOfY": "Page {currentPage} of {pageCount}",
211207
"noParticipants": "No participants",
212208
"userId": "User ID",
213209
"aboutGuest": "Guest User",
@@ -281,5 +277,7 @@
281277
"savePercentage": "Save {percentage}%",
282278
"1month": "1 month",
283279
"subscribe": "Subscribe",
284-
"cancelAnytime": "Cancel anytime from your <a>billing page</a>."
280+
"cancelAnytime": "Cancel anytime from your <a>billing page</a>.",
281+
"unsubscribeToastTitle": "You have disabled notifications",
282+
"unsubscribeToastDescription": "You will no longer receive notifications for this poll"
285283
}

apps/web/src/app/[locale]/poll/[urlId]/admin-page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import { useTouchBeacon } from "@/components/poll/use-touch-beacon";
99
import { VotingForm } from "@/components/poll/voting-form";
1010

1111
import { GuestPollAlert } from "./guest-poll-alert";
12+
import { UnsubscribeAlert } from "./unsubscribe-alert";
1213

1314
export function AdminPage() {
1415
useTouchBeacon();
1516
return (
1617
<div className="space-y-3 lg:space-y-4">
18+
<UnsubscribeAlert />
1719
<PollHeader />
1820
<GuestPollAlert />
1921
<EventCard />
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"use client";
2+
3+
import { useToast } from "@rallly/ui/hooks/use-toast";
4+
import Cookies from "js-cookie";
5+
import { useParams } from "next/navigation";
6+
import { useEffect } from "react";
7+
import { useTranslation } from "react-i18next";
8+
9+
export function UnsubscribeAlert() {
10+
const { toast } = useToast();
11+
const { t } = useTranslation("app");
12+
13+
const urlId = useParams<{ urlId: string }>()?.urlId;
14+
15+
useEffect(() => {
16+
if (!urlId) return;
17+
const cookieName = `notifications-unsubscribed-${urlId}`;
18+
const unsubscribed = Cookies.get(cookieName);
19+
if (unsubscribed) {
20+
Cookies.remove(cookieName);
21+
toast({
22+
title: t("unsubscribeToastTitle", {
23+
defaultValue: "You have disabled notifications",
24+
}),
25+
description: t("unsubscribeToastDescription", {
26+
defaultValue:
27+
"You will no longer receive notifications for this poll",
28+
}),
29+
});
30+
}
31+
}, [t, toast, urlId]);
32+
33+
return null;
34+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { prisma } from "@rallly/database";
2+
import { cookies } from "next/headers";
3+
import type { NextRequest } from "next/server";
4+
import { NextResponse } from "next/server";
5+
6+
import { getServerSession } from "@/auth";
7+
import type { DisableNotificationsPayload } from "@/trpc/types";
8+
import { decryptToken } from "@/utils/session";
9+
10+
export const GET = async (req: NextRequest) => {
11+
const token = req.nextUrl.searchParams.get("token");
12+
13+
if (!token) {
14+
return NextResponse.redirect(new URL("/login", req.url));
15+
}
16+
17+
const session = await getServerSession();
18+
19+
if (!session || !session.user.email) {
20+
return NextResponse.redirect(new URL("/login", req.url));
21+
}
22+
23+
const payload = await decryptToken<DisableNotificationsPayload>(token);
24+
25+
if (!payload) {
26+
return NextResponse.redirect(new URL("/login", req.url));
27+
}
28+
29+
const watcher = await prisma.watcher.findFirst({
30+
where: {
31+
userId: session.user.id,
32+
pollId: payload.pollId,
33+
},
34+
});
35+
36+
if (watcher) {
37+
await prisma.watcher.delete({
38+
where: {
39+
id: watcher.id,
40+
},
41+
select: {
42+
pollId: true,
43+
},
44+
});
45+
46+
// Set a session cookie to indicate that the user has unsubscribed
47+
cookies().set(`notifications-unsubscribed-${watcher.pollId}`, "1", {
48+
path: "/",
49+
httpOnly: false,
50+
secure: false,
51+
sameSite: "lax",
52+
maxAge: 5,
53+
});
54+
55+
// redirect to poll
56+
return NextResponse.redirect(new URL(`/poll/${watcher.pollId}`, req.url));
57+
}
58+
59+
return NextResponse.redirect(new URL(`/poll/${payload.pollId}`, req.url));
60+
};

apps/web/src/pages/[locale]/auth/disable-notifications.tsx

Lines changed: 0 additions & 59 deletions
This file was deleted.

apps/web/src/pages/[locale]/auth/error.tsx

Lines changed: 0 additions & 39 deletions
This file was deleted.

apps/web/src/pages/_app.tsx

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)