From ea113e992be39115d5bed2e08fe9d508845f2e67 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 7 Apr 2026 09:22:23 +0000
Subject: [PATCH 1/2] Initial plan
From f822bb557b604188ff59999a1cdbe34c2efa3b1a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 7 Apr 2026 09:27:08 +0000
Subject: [PATCH 2/2] feat: add one-click contest email unsubscribe flow
Agent-Logs-Url: https://github.com/vansh1293/Notify/sessions/e92135f5-88ac-4fa0-982a-68d30418930c
Co-authored-by: Himaanshuuuu04 <145840915+Himaanshuuuu04@users.noreply.github.com>
---
README.md | 1 +
emails/ContestEmail.tsx | 16 ++++++-
src/app/api/unsubscribe-contest/route.ts | 56 ++++++++++++++++++++++++
src/helpers/sendContestDetails.ts | 52 ++++++++++++----------
src/lib/unsubscribeToken.ts | 25 +++++++++++
5 files changed, 125 insertions(+), 25 deletions(-)
create mode 100644 src/app/api/unsubscribe-contest/route.ts
create mode 100644 src/lib/unsubscribeToken.ts
diff --git a/README.md b/README.md
index 3fcbfe8..411fe53 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,7 @@ A huge shoutout to [Tashif Khan](https://github.com/Tashifkhan) for generously p
- Customizable notification timing (1 hour, 1 day before)
- Multiple reminder types: browser notifications, email alerts
+- One-click unsubscribe link in contest update emails
- Contest difficulty and duration-based filtering
- Time zone intelligent scheduling
diff --git a/emails/ContestEmail.tsx b/emails/ContestEmail.tsx
index 578a6b9..859a949 100644
--- a/emails/ContestEmail.tsx
+++ b/emails/ContestEmail.tsx
@@ -13,6 +13,10 @@ import {
import * as React from "react";
import { Contest } from "@/model/Contest";
+type ContestEmailProps = Contest & {
+ unsubscribeUrl?: string;
+};
+
const platformStyles = {
LeetCode: {
color: "text-yellow-600",
@@ -52,7 +56,8 @@ export default function ContestEmail({
endTime,
duration,
url,
-}: Contest) {
+ unsubscribeUrl,
+}: ContestEmailProps) {
const theme = platformStyles[platform as keyof typeof platformStyles];
return (
@@ -88,10 +93,17 @@ export default function ContestEmail({
You’re receiving this email because you opted in for contest alerts.
+ {unsubscribeUrl && (
+
+
+ Unsubscribe from contest emails
+
+
+ )}