From 5d02192f757aeb4928a5b33475f364bbb8bebe41 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Fri, 9 Sep 2022 12:55:04 +0000 Subject: [PATCH 01/16] chore: cancel event email content --- server/src/controllers/Events/resolver.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 7c00557953..73fc913777 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -699,6 +699,7 @@ ${venueDetails}`; data: { canceled: true }, include: { tags: { include: { tag: true } }, + chapter: true, event_users: { include: { user: true }, where: { @@ -710,17 +711,31 @@ ${venueDetails}`; }); await deleteEventReminders(id); + const chapterURL = `${process.env.CLIENT_LOCATION}/chapters/${event.chapter.id}`; + const eventURL = `${process.env.CLIENT_LOCATION}/events/${event.id}?emaillink=true`; const notCanceledRsvps = event.event_users; if (notCanceledRsvps.length) { const emailList = notCanceledRsvps.map(({ user }) => user.email); - const subject = `Event ${event.name} canceled`; - const body = `The event ${event.name} was canceled`; + const subject = `Event ${event.name} is canceled`; + const cancelEventEmail = `The Upcoming Event for ${event.name} is canceled.
+
+ View Upcoming Events for ${event.chapter.name}: ${event.chapter.name} chapter.
+ ----------------------------
+
+ - Stop receiving upcoming event notifications for ${event.chapter.name}. you can do it here: ${eventURL}.
+ - More about ${event.chapter.name} or to unfollow this chapter: ${chapterURL}.
+
+ ----------------------------
+ You received this email because you subscribed to ${event.name} Event.
+
+ See the options above to change your notifications. + `; new MailerService({ emailList: emailList, subject: subject, - htmlEmail: body, + htmlEmail: cancelEventEmail, }).sendEmail(); } From b8f019fd78d722c3b0eef9f91aad44c738cb798b Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Fri, 9 Sep 2022 21:46:51 +0200 Subject: [PATCH 02/16] Grammar typo Co-authored-by: Krzysztof G. <60067306+gikf@users.noreply.github.com> --- server/src/controllers/Events/resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 73fc913777..5c5277ce2c 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -718,7 +718,7 @@ ${venueDetails}`; if (notCanceledRsvps.length) { const emailList = notCanceledRsvps.map(({ user }) => user.email); const subject = `Event ${event.name} is canceled`; - const cancelEventEmail = `The Upcoming Event for ${event.name} is canceled.
+ const cancelEventEmail = `The Upcoming Event ${event.name} is canceled.

View Upcoming Events for ${event.chapter.name}: ${event.chapter.name} chapter.
----------------------------
From ee94fe74d09fd32d4171d37a778da5551c3271d0 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Sat, 10 Sep 2022 08:20:57 +0000 Subject: [PATCH 03/16] Change notification wording --- server/src/controllers/Events/resolver.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 5c5277ce2c..624a75f6cb 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -718,16 +718,19 @@ ${venueDetails}`; if (notCanceledRsvps.length) { const emailList = notCanceledRsvps.map(({ user }) => user.email); const subject = `Event ${event.name} is canceled`; + + // ToDo: change "subscribed to event" to "join event" when join and leave function is on + const cancelEventEmail = `The Upcoming Event ${event.name} is canceled.

View Upcoming Events for ${event.chapter.name}: ${event.chapter.name} chapter.
----------------------------

- - Stop receiving upcoming event notifications for ${event.chapter.name}. you can do it here: ${eventURL}.
+ - Stop receiving event notifications for ${event.name}. you can do it here: ${eventURL}.
- More about ${event.chapter.name} or to unfollow this chapter: ${chapterURL}.

----------------------------
- You received this email because you subscribed to ${event.name} Event.
+ You received this email because you Subscribed to ${event.name} Event.

See the options above to change your notifications. `; From d2ef1eb8d005a7402d428bdb572abbda71a28c9d Mon Sep 17 00:00:00 2001 From: Sboonny Date: Sat, 10 Sep 2022 08:23:55 +0000 Subject: [PATCH 04/16] fix grammar typo --- server/src/controllers/Events/resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 624a75f6cb..01431f78cf 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -726,7 +726,7 @@ ${venueDetails}`; View Upcoming Events for ${event.chapter.name}: ${event.chapter.name} chapter.
----------------------------

- - Stop receiving event notifications for ${event.name}. you can do it here: ${eventURL}.
+ - Stop receiving event notifications for ${event.name}. You can do it here: ${eventURL}.
- More about ${event.chapter.name} or to unfollow this chapter: ${chapterURL}.

----------------------------
From 6a471ea31067cadc36b60d6ae938fdc7811f6571 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Thu, 15 Sep 2022 09:02:50 +0000 Subject: [PATCH 05/16] update select to include id and name --- server/src/controllers/Events/resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index ce64f4ce1f..4b10ede4a8 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -844,7 +844,7 @@ ${venueDetails}`; data: { canceled: true }, include: { tags: { include: { tag: true } }, - chapter: { select: { calendar_id: true } }, + chapter: { select: { id: true, name: true, calendar_id: true } }, event_users: { include: { user: true }, where: { From a6126b7eabdfaae0a420794d9ba6b006481e75f0 Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Mon, 24 Oct 2022 12:16:34 +0200 Subject: [PATCH 06/16] fix event url and alter the flow of the email Co-authored-by: Oliver Eyton-Williams --- server/src/controllers/Events/resolver.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index f14660aced..ee7410d5ca 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -855,7 +855,7 @@ ${unsubscribeOptions}`, await deleteEventReminders(id); const chapterURL = `${process.env.CLIENT_LOCATION}/chapters/${event.chapter.id}`; - const eventURL = `${process.env.CLIENT_LOCATION}/events/${event.id}?emaillink=true`; + const eventURL = `${process.env.CLIENT_LOCATION}/events/${event.id}`; const notCanceledRsvps = event.event_users; if (notCanceledRsvps.length) { @@ -864,18 +864,10 @@ ${unsubscribeOptions}`, // ToDo: change "subscribed to event" to "join event" when join and leave function is on - const cancelEventEmail = `The Upcoming Event ${event.name} is canceled.
+ const cancelEventEmail = `The upcoming event ${event.name} has been canceled.

- View Upcoming Events for ${event.chapter.name}: ${event.chapter.name} chapter.
+ View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
----------------------------
-
- - Stop receiving event notifications for ${event.name}. You can do it here: ${eventURL}.
- - More about ${event.chapter.name} or to unfollow this chapter: ${chapterURL}.
-
- ----------------------------
- You received this email because you Subscribed to ${event.name} Event.
-
- See the options above to change your notifications. `; new MailerService({ From 06209830d3c450a4d4096bfdf2d3cbf61cb47e11 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Mon, 24 Oct 2022 19:01:11 +0200 Subject: [PATCH 07/16] Dry Email content text --- server/src/controllers/Events/resolver.ts | 54 +++++++---------------- server/src/util/eventEmail.ts | 36 +++++++++++++++ 2 files changed, 53 insertions(+), 37 deletions(-) create mode 100644 server/src/util/eventEmail.ts diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index ee7410d5ca..b616e274c8 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -37,10 +37,6 @@ import { deleteEventReminders, updateRemindAt, } from '../../services/Reminders'; -import { - generateToken, - UnsubscribeType, -} from '../../services/UnsubscribeToken'; import { cancelCalendarEvent, createCalendarEvent, @@ -48,6 +44,10 @@ import { patchCalendarEvent, updateCalendarEvent, } from '../../services/Google'; +import { + getUnsubscribeOptions, + getChapterUnsubscribeToken, +} from '../../util/eventEmail'; import { EventInputs } from './inputs'; const eventUserIncludes = { @@ -76,31 +76,6 @@ const isPhysical = (venue_type: events_venue_type_enum) => const isOnline = (venue_type: events_venue_type_enum) => venue_type !== events_venue_type_enum.Physical; -const getUnsubscribeOptions = ({ - chapterId, - eventId, - userId, -}: { - chapterId: number; - eventId: number; - userId: number; -}) => { - const chapterUnsubscribeToken = generateToken( - UnsubscribeType.Chapter, - chapterId, - userId, - ); - const eventUnsubscribeToken = generateToken( - UnsubscribeType.Event, - eventId, - userId, - ); - return ` -Unsubscribe Options
-- Attend this event, but only turn off future notifications for this event
-- Or, stop receiving all notifications by unfollowing chapter`; -}; - const sendRsvpInvitation = async ( user: Required['user'], event: events & { venue: venues | null }, @@ -237,11 +212,10 @@ const rsvpNotifyAdministrators = async ( await batchSender(function* () { for (const { chapter_id, user } of chapterAdministrators) { const email = user.email; - const chapterUnsubscribeToken = generateToken( - UnsubscribeType.Chapter, - chapter_id, - user.id, - ); + const chapterUnsubscribeToken = getChapterUnsubscribeToken({ + chapterId: chapter_id, + userId: user.id, + }); const text = `${body}
${eventUnsubscribeToken}.
+ - To manage notifications from this chapter about new events, go to ${chapterUnsubscribeToken}.
`; +}; + +export const getChapterUnsubscribeToken = ({ + chapterId, + userId, +}: { + chapterId: number; + userId: number; +}) => { + generateToken(UnsubscribeType.Chapter, chapterId, userId); +}; From 16de04313f30ee8a75352c49e7a4aa6437cdc82b Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 25 Oct 2022 12:43:43 +0200 Subject: [PATCH 08/16] use the current user id in cancel email --- server/src/controllers/Events/resolver.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 3676efcec0..8dd1f6bc37 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -752,7 +752,10 @@ ${unsubscribeOptions}`, @Authorized(Permission.EventEdit) @Mutation(() => Event) - async cancelEvent(@Arg('id', () => Int) id: number): Promise { + async cancelEvent( + @Arg('id', () => Int) id: number, + @Ctx() ctx: Required, + ): Promise { const event = await prisma.events.update({ where: { id }, data: { canceled: true }, @@ -774,7 +777,7 @@ ${unsubscribeOptions}`, const unsubScribeOptions = getUnsubscribeOptions({ chapterId: event.chapter_id, eventId: event.id, - userId: user.id, + userId: ctx.user.id, }); if (notCanceledRsvps.length) { const emailList = notCanceledRsvps.map(({ user }) => user.email); From 4ee6854f0877308aa43a4598d65c9a1d78b02735 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 25 Oct 2022 13:22:00 +0200 Subject: [PATCH 09/16] WIP: add generic notification text function for cancel event email --- server/src/controllers/Events/resolver.ts | 14 ++++++-------- server/src/util/eventEmail.ts | 21 +++++++++++++++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 8dd1f6bc37..cd154ccb02 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -46,6 +46,7 @@ import { import { getUnsubscribeOptions, getChapterUnsubscribeToken, + NotificationContextText, } from '../../util/eventEmail'; import { updateCalendarEventAttendees } from '../../util/updateCalendarEventAttendees'; import { updateEventWaitlist } from '../../util/updateEventWaitlist'; @@ -752,10 +753,7 @@ ${unsubscribeOptions}`, @Authorized(Permission.EventEdit) @Mutation(() => Event) - async cancelEvent( - @Arg('id', () => Int) id: number, - @Ctx() ctx: Required, - ): Promise { + async cancelEvent(@Arg('id', () => Int) id: number): Promise { const event = await prisma.events.update({ where: { id }, data: { canceled: true }, @@ -773,11 +771,11 @@ ${unsubscribeOptions}`, await deleteEventReminders(id); const chapterURL = `${process.env.CLIENT_LOCATION}/chapters/${event.chapter.id}`; + const eventURL = `${process.env.CLIENT_LOCATION}/events/${event.id}`; const notCanceledRsvps = event.event_users; - const unsubScribeOptions = getUnsubscribeOptions({ - chapterId: event.chapter_id, - eventId: event.id, - userId: ctx.user.id, + const unsubScribeOptions = NotificationContextText({ + linkToEvent: eventURL, + linkToChapter: chapterURL, }); if (notCanceledRsvps.length) { const emailList = notCanceledRsvps.map(({ user }) => user.email); diff --git a/server/src/util/eventEmail.ts b/server/src/util/eventEmail.ts index 923bc0b165..640bb637fe 100644 --- a/server/src/util/eventEmail.ts +++ b/server/src/util/eventEmail.ts @@ -1,5 +1,18 @@ import { generateToken, UnsubscribeType } from '../services/UnsubscribeToken'; +export const NotificationContextText = ({ + linkToEvent, + linkToChapter, +}: { + linkToEvent: string; + linkToChapter: string; +}) => { + return ` + Unsubscribe Options
+ - To manage notifications about this event, go to ${linkToEvent}.
+ - To manage notifications from this chapter about new events, go to ${linkToChapter}.
`; +}; + export const getUnsubscribeOptions = ({ chapterId, eventId, @@ -19,10 +32,10 @@ export const getUnsubscribeOptions = ({ eventId, userId, ); - return ` - Unsubscribe Options
- - To manage notifications about this event, go to ${eventUnsubscribeToken}.
- - To manage notifications from this chapter about new events, go to ${chapterUnsubscribeToken}.
`; + return NotificationContextText({ + linkToEvent: eventUnsubscribeToken, + linkToChapter: chapterUnsubscribeToken, + }); }; export const getChapterUnsubscribeToken = ({ From b582327509947eb2c5dee3652214addfec1815b2 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 08:45:01 +0000 Subject: [PATCH 10/16] Fix merge conflict errors --- server/src/controllers/Events/resolver.ts | 31 ++++------------------- server/src/util/eventEmail.ts | 4 +-- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 7e1f7e6a4e..8aff9e5543 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -54,6 +54,11 @@ import { } from '../../util/calendar'; import { updateWaitlistForUserRemoval } from '../../util/waitlist'; import { redactSecrets } from '../../util/redact-secrets'; +import { + getChapterUnsubscribeToken, + getUnsubscribeOptions, + NotificationContextText, +} from '../../util/eventEmail'; import { EventInputs } from './inputs'; const eventUserIncludes = { @@ -82,32 +87,6 @@ const isPhysical = (venue_type: events_venue_type_enum) => const isOnline = (venue_type: events_venue_type_enum) => venue_type !== events_venue_type_enum.Physical; - -const getUnsubscribeOptions = ({ - chapterId, - eventId, - userId, -}: { - chapterId: number; - eventId: number; - userId: number; -}) => { - const chapterUnsubscribeToken = generateToken( - UnsubscribeType.Chapter, - chapterId, - userId, - ); - const eventUnsubscribeToken = generateToken( - UnsubscribeType.Event, - eventId, - userId, - ); - return ` -Unsubscribe Options
-- Attend this event, but only turn off future notifications for this event
-- Or, stop receiving notifications about new events by unfollowing chapter`; -}; - const sendRsvpInvitation = async ( user: Required['user'], event: events & { venue: venues | null }, diff --git a/server/src/util/eventEmail.ts b/server/src/util/eventEmail.ts index 640bb637fe..da414862ee 100644 --- a/server/src/util/eventEmail.ts +++ b/server/src/util/eventEmail.ts @@ -33,8 +33,8 @@ export const getUnsubscribeOptions = ({ userId, ); return NotificationContextText({ - linkToEvent: eventUnsubscribeToken, - linkToChapter: chapterUnsubscribeToken, + linkToEvent: `${process.env.CLIENT_LOCATION}/unsubscribe?token=${eventUnsubscribeToken}`, + linkToChapter: `${process.env.CLIENT_LOCATION}/unsubscribe?token=${chapterUnsubscribeToken}`, }); }; From 66a33375440b68ccbe49bf04fe622358e402c2ba Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 10:21:20 +0000 Subject: [PATCH 11/16] make it clearer and reduce boilerplate code Co-authored-by: Oliver Eyton-Williams --- server/src/controllers/Events/resolver.ts | 35 ++++++++++------------- server/src/util/eventEmail.ts | 26 +++++------------ 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 8aff9e5543..98a9f8e8c0 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -55,9 +55,8 @@ import { import { updateWaitlistForUserRemoval } from '../../util/waitlist'; import { redactSecrets } from '../../util/redact-secrets'; import { - getChapterUnsubscribeToken, - getUnsubscribeOptions, - NotificationContextText, + getChapterUnsubscribeOptions, + getEventUnsubscribeOptions, } from '../../util/eventEmail'; import { EventInputs } from './inputs'; @@ -99,7 +98,7 @@ const sendRsvpInvitation = async ( }; if (event.venue?.name) linkDetails.location = event.venue?.name; - const unsubscribeOptions = getUnsubscribeOptions({ + const unsubscribeOptions = getEventUnsubscribeOptions({ chapterId: event.chapter_id, eventId: event.id, userId: user.id, @@ -223,7 +222,7 @@ const rsvpNotifyAdministrators = async ( await batchSender(function* () { for (const { chapter_id, user } of chapterAdministrators) { const email = user.email; - const chapterUnsubscribeToken = getChapterUnsubscribeToken({ + const chapterUnsubscribeToken = getChapterUnsubscribeOptions({ chapterId: chapter_id, userId: user.id, }); @@ -548,7 +547,7 @@ export class EventResolver { include: { event: { include: { chapter: true } }, ...eventUserIncludes }, }); - const unsubscribeOptions = getUnsubscribeOptions({ + const unsubscribeOptions = getEventUnsubscribeOptions({ chapterId: updatedUser.event.chapter_id, eventId: updatedUser.event_id, userId, @@ -737,12 +736,12 @@ ${unsubscribeOptions}`, batchSender(function* () { for (const { user } of event.event_users) { const email = user.email; - const unsubScribeOptions = getUnsubscribeOptions({ + const unsubscribeOptions = getEventUnsubscribeOptions({ chapterId: event.chapter_id, eventId: event.id, userId: user.id, }); - const text = `${body}
${unsubScribeOptions}`; + const text = `${body}
${unsubscribeOptions}`; yield { email, subject, text }; } }); @@ -798,26 +797,22 @@ ${unsubscribeOptions}`, }); await deleteEventReminders(id); - const chapterURL = `${process.env.CLIENT_LOCATION}/chapters/${event.chapter.id}`; - const eventURL = `${process.env.CLIENT_LOCATION}/events/${event.id}`; const notCanceledRsvps = event.event_users; - const unsubScribeOptions = NotificationContextText({ - linkToEvent: eventURL, - linkToChapter: chapterURL, + const unsubscribeOptions = getEventUnsubscribeOptions({ + chapterId: event.chapter_id, + eventId: event.id, + userId: user.id, }); if (notCanceledRsvps.length) { const emailList = notCanceledRsvps.map(({ user }) => user.email); const subject = `Event ${event.name} is canceled`; - // ToDo: change "subscribed to event" to "join event" when join and leave function is on - const cancelEventEmail = `The upcoming event ${event.name} has been canceled.

- View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
- ${unsubScribeOptions} + View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
You received this email because you Subscribed to ${event.name} Event.

- See the options above to change your notifications. + ${unsubscribeOptions} `; new MailerService({ @@ -999,12 +994,12 @@ ${unsubscribeOptions}`, await batchSender(function* () { for (const { user } of users) { const email = user.email; - const unsubScribeOptions = getUnsubscribeOptions({ + const unsubscribeOptions = getEventUnsubscribeOptions({ chapterId: event.chapter_id, eventId: event.id, userId: user.id, }); - const text = `${subsequentEventEmail}
${unsubScribeOptions}`; + const text = `${subsequentEventEmail}
${unsubscribeOptions}`; yield { email, subject, text, options: { iCalEvent } }; } }); diff --git a/server/src/util/eventEmail.ts b/server/src/util/eventEmail.ts index da414862ee..9f5361290a 100644 --- a/server/src/util/eventEmail.ts +++ b/server/src/util/eventEmail.ts @@ -1,19 +1,6 @@ import { generateToken, UnsubscribeType } from '../services/UnsubscribeToken'; -export const NotificationContextText = ({ - linkToEvent, - linkToChapter, -}: { - linkToEvent: string; - linkToChapter: string; -}) => { - return ` - Unsubscribe Options
- - To manage notifications about this event, go to ${linkToEvent}.
- - To manage notifications from this chapter about new events, go to ${linkToChapter}.
`; -}; - -export const getUnsubscribeOptions = ({ +export const getEventUnsubscribeOptions = ({ chapterId, eventId, userId, @@ -32,13 +19,14 @@ export const getUnsubscribeOptions = ({ eventId, userId, ); - return NotificationContextText({ - linkToEvent: `${process.env.CLIENT_LOCATION}/unsubscribe?token=${eventUnsubscribeToken}`, - linkToChapter: `${process.env.CLIENT_LOCATION}/unsubscribe?token=${chapterUnsubscribeToken}`, - }); + const linkToEvent = `${process.env.CLIENT_LOCATION}/unsubscribe?token=${eventUnsubscribeToken}`; + const linkToChapter = `${process.env.CLIENT_LOCATION}/unsubscribe?token=${chapterUnsubscribeToken}`; + return `
+ - To stop receiving notifications about this event, go to ${linkToEvent}.
+ - To stop receiving notifications about new events in this chapter, go to ${linkToChapter}.
`; }; -export const getChapterUnsubscribeToken = ({ +export const getChapterUnsubscribeOptions = ({ chapterId, userId, }: { From 2e7c4da47ed6e081415c3678c46dc4cc1b1e85cd Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 12:08:13 +0000 Subject: [PATCH 12/16] Send email to subscribed memebers --- server/src/controllers/Events/resolver.ts | 74 +++++++++++++++-------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 98a9f8e8c0..5d966800f0 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -780,7 +780,14 @@ ${unsubscribeOptions}`, @Authorized(Permission.EventEdit) @Mutation(() => Event) - async cancelEvent(@Arg('id', () => Int) id: number): Promise { + async cancelEvent( + @Arg('id', () => Int) id: number, + @Arg('emailGroups', () => [String], { + nullable: true, + defaultValue: ['interested'], + }) + emailGroups: Array, + ): Promise { const event = await prisma.events.update({ where: { id }, data: { canceled: true }, @@ -795,31 +802,50 @@ ${unsubscribeOptions}`, }, }, }); + interface User { + user: { id: number; email: string }; + subscribed: boolean; + } + const users: User[] = []; + + if (emailGroups.includes(true)) { + const subscribedUsers = event.event_users.filter( + ({ subscribed }) => subscribed === true, + ); + users.push(...subscribedUsers); + } await deleteEventReminders(id); - const notCanceledRsvps = event.event_users; - const unsubscribeOptions = getEventUnsubscribeOptions({ - chapterId: event.chapter_id, - eventId: event.id, - userId: user.id, - }); - if (notCanceledRsvps.length) { - const emailList = notCanceledRsvps.map(({ user }) => user.email); - const subject = `Event ${event.name} is canceled`; - - const cancelEventEmail = `The upcoming event ${event.name} has been canceled.
-
- View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
- You received this email because you Subscribed to ${event.name} Event.
-
- ${unsubscribeOptions} - `; - - new MailerService({ - emailList: emailList, - subject: subject, - htmlEmail: cancelEventEmail, - }).sendEmail(); + try { + for (const { user } of users) { + const notCanceledRsvps = event.event_users; + const unsubscribeOptions = getEventUnsubscribeOptions({ + chapterId: event.chapter_id, + eventId: event.id, + userId: user.id, + }); + if (notCanceledRsvps.length) { + const emailList = notCanceledRsvps.map(({ user }) => user.email); + const subject = `Event ${event.name} is canceled`; + + const cancelEventEmail = `The upcoming event ${event.name} has been canceled.
+
+ View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
+ You received this email because you Subscribed to ${event.name} Event.
+
+ ${unsubscribeOptions} + `; + + new MailerService({ + emailList: emailList, + subject: subject, + htmlEmail: cancelEventEmail, + }).sendEmail(); + } + } + } catch (err) { + console.log('there is no members subscribed'); + throw err; } if (event.chapter.calendar_id && event.calendar_event_id) { From f157474e4d6b54219f279232323c6821cf26437e Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 13:47:07 +0000 Subject: [PATCH 13/16] remove extra code and used already there variable --- server/src/controllers/Events/resolver.ts | 114 ++++++++++++++-------- 1 file changed, 73 insertions(+), 41 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 5d966800f0..171dce578c 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -780,14 +780,7 @@ ${unsubscribeOptions}`, @Authorized(Permission.EventEdit) @Mutation(() => Event) - async cancelEvent( - @Arg('id', () => Int) id: number, - @Arg('emailGroups', () => [String], { - nullable: true, - defaultValue: ['interested'], - }) - emailGroups: Array, - ): Promise { + async cancelEvent(@Arg('id', () => Int) id: number): Promise { const event = await prisma.events.update({ where: { id }, data: { canceled: true }, @@ -802,33 +795,20 @@ ${unsubscribeOptions}`, }, }, }); - interface User { - user: { id: number; email: string }; - subscribed: boolean; - } - const users: User[] = []; - - if (emailGroups.includes(true)) { - const subscribedUsers = event.event_users.filter( - ({ subscribed }) => subscribed === true, - ); - users.push(...subscribedUsers); - } await deleteEventReminders(id); + const notCanceledRsvps = event.event_users; - try { - for (const { user } of users) { - const notCanceledRsvps = event.event_users; - const unsubscribeOptions = getEventUnsubscribeOptions({ - chapterId: event.chapter_id, - eventId: event.id, - userId: user.id, - }); - if (notCanceledRsvps.length) { - const emailList = notCanceledRsvps.map(({ user }) => user.email); - const subject = `Event ${event.name} is canceled`; + for (const { user } of notCanceledRsvps) { + const unsubscribeOptions = getEventUnsubscribeOptions({ + chapterId: event.chapter_id, + eventId: event.id, + userId: user.id, + }); + if (notCanceledRsvps.length) { + const emailList = notCanceledRsvps.map(({ user }) => user.email); + const subject = `Event ${event.name} is canceled`; - const cancelEventEmail = `The upcoming event ${event.name} has been canceled.
+ const cancelEventEmail = `The upcoming event ${event.name} has been canceled.

View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
You received this email because you Subscribed to ${event.name} Event.
@@ -836,16 +816,12 @@ ${unsubscribeOptions}`, ${unsubscribeOptions} `; - new MailerService({ - emailList: emailList, - subject: subject, - htmlEmail: cancelEventEmail, - }).sendEmail(); - } + new MailerService({ + emailList: emailList, + subject: subject, + htmlEmail: cancelEventEmail, + }).sendEmail(); } - } catch (err) { - console.log('there is no members subscribed'); - throw err; } if (event.chapter.calendar_id && event.calendar_event_id) { @@ -871,6 +847,62 @@ ${unsubscribeOptions}`, return event; } + // @Authorized(Permission.EventEdit) + // @Mutation(() => Event) + // async cancelEvent(@Arg('id', () => Int) id: number): Promise { + // const event = await prisma.events.update({ + // where: { id }, + // data: { canceled: true }, + // include: { + // chapter: { select: { calendar_id: true } }, + // event_users: { + // include: { user: true }, + // where: { + // subscribed: true, + // rsvp: { name: { not: 'no' } }, + // }, + // }, + // }, + // }); + // await deleteEventReminders(id); + + // const notCanceledRsvps = event.event_users; + + // if (notCanceledRsvps.length) { + // const emailList = notCanceledRsvps.map(({ user }) => user.email); + // const subject = `Event ${event.name} canceled`; + // const body = `The event ${event.name} was canceled`; + + // new MailerService({ + // emailList: emailList, + // subject: subject, + // htmlEmail: body, + // }).sendEmail(); + // } + + // if (event.chapter.calendar_id && event.calendar_event_id) { + // try { + // // TODO: consider not awaiting. Ideally the user would see the app + // // respond immediately, but be informed of any failures later. + // // Client-side this could be handled by something like redux-saga, but + // // I'm not sure how to approach that server-side. + // await cancelCalendarEvent({ + // calendarId: event.chapter.calendar_id, + // calendarEventId: event.calendar_event_id, + // summary: event.name, + // start: event.start_at, + // end: event.ends_at, + // attendeeEmails: event.event_users.map(({ user }) => user.email), + // }); + // } catch (e) { + // console.error('Unable to cancel calendar event'); + // console.error(inspect(redactSecrets(e), { depth: null })); + // } + // } + + // return event; + // } + @Authorized(Permission.EventDelete) @Mutation(() => Event) async deleteEvent(@Arg('id', () => Int) id: number): Promise { From 28dd21781de7b8884208e05392c78e9b1563b9ae Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 13:48:41 +0000 Subject: [PATCH 14/16] fix the if statement logic --- server/src/controllers/Events/resolver.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 171dce578c..d976248088 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -798,17 +798,17 @@ ${unsubscribeOptions}`, await deleteEventReminders(id); const notCanceledRsvps = event.event_users; + if (!notCanceledRsvps) return null; for (const { user } of notCanceledRsvps) { const unsubscribeOptions = getEventUnsubscribeOptions({ chapterId: event.chapter_id, eventId: event.id, userId: user.id, }); - if (notCanceledRsvps.length) { - const emailList = notCanceledRsvps.map(({ user }) => user.email); - const subject = `Event ${event.name} is canceled`; + const emailList = notCanceledRsvps.map(({ user }) => user.email); + const subject = `Event ${event.name} is canceled`; - const cancelEventEmail = `The upcoming event ${event.name} has been canceled.
+ const cancelEventEmail = `The upcoming event ${event.name} has been canceled.

View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
You received this email because you Subscribed to ${event.name} Event.
@@ -816,12 +816,11 @@ ${unsubscribeOptions}`, ${unsubscribeOptions} `; - new MailerService({ - emailList: emailList, - subject: subject, - htmlEmail: cancelEventEmail, - }).sendEmail(); - } + new MailerService({ + emailList: emailList, + subject: subject, + htmlEmail: cancelEventEmail, + }).sendEmail(); } if (event.chapter.calendar_id && event.calendar_event_id) { From ae35156da2c4c80d256898a6ec6ce78edbe454f5 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 14:12:31 +0000 Subject: [PATCH 15/16] change if logic to check for length --- server/src/controllers/Events/resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index d976248088..5d1e60d3a5 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -798,7 +798,7 @@ ${unsubscribeOptions}`, await deleteEventReminders(id); const notCanceledRsvps = event.event_users; - if (!notCanceledRsvps) return null; + if (!notCanceledRsvps.length) return null; for (const { user } of notCanceledRsvps) { const unsubscribeOptions = getEventUnsubscribeOptions({ chapterId: event.chapter_id, From 35611a4c03ab7948e68eb10251db34ae8cf4de09 Mon Sep 17 00:00:00 2001 From: Sboonny Date: Tue, 22 Nov 2022 14:36:59 +0000 Subject: [PATCH 16/16] fix the if statement logic --- server/src/controllers/Events/resolver.ts | 88 +++++------------------ 1 file changed, 16 insertions(+), 72 deletions(-) diff --git a/server/src/controllers/Events/resolver.ts b/server/src/controllers/Events/resolver.ts index 5d1e60d3a5..b09e3cc2a0 100644 --- a/server/src/controllers/Events/resolver.ts +++ b/server/src/controllers/Events/resolver.ts @@ -798,17 +798,17 @@ ${unsubscribeOptions}`, await deleteEventReminders(id); const notCanceledRsvps = event.event_users; - if (!notCanceledRsvps.length) return null; - for (const { user } of notCanceledRsvps) { - const unsubscribeOptions = getEventUnsubscribeOptions({ - chapterId: event.chapter_id, - eventId: event.id, - userId: user.id, - }); - const emailList = notCanceledRsvps.map(({ user }) => user.email); - const subject = `Event ${event.name} is canceled`; + if (notCanceledRsvps.length) { + for (const { user } of notCanceledRsvps) { + const unsubscribeOptions = getEventUnsubscribeOptions({ + chapterId: event.chapter_id, + eventId: event.id, + userId: user.id, + }); + const emailList = notCanceledRsvps.map(({ user }) => user.email); + const subject = `Event ${event.name} is canceled`; - const cancelEventEmail = `The upcoming event ${event.name} has been canceled.
+ const cancelEventEmail = `The upcoming event ${event.name} has been canceled.

View upcoming events for ${event.chapter.name}: ${event.chapter.name} chapter.
You received this email because you Subscribed to ${event.name} Event.
@@ -816,13 +816,13 @@ ${unsubscribeOptions}`, ${unsubscribeOptions} `; - new MailerService({ - emailList: emailList, - subject: subject, - htmlEmail: cancelEventEmail, - }).sendEmail(); + new MailerService({ + emailList: emailList, + subject: subject, + htmlEmail: cancelEventEmail, + }).sendEmail(); + } } - if (event.chapter.calendar_id && event.calendar_event_id) { try { // TODO: consider not awaiting. Ideally the user would see the app @@ -846,62 +846,6 @@ ${unsubscribeOptions}`, return event; } - // @Authorized(Permission.EventEdit) - // @Mutation(() => Event) - // async cancelEvent(@Arg('id', () => Int) id: number): Promise { - // const event = await prisma.events.update({ - // where: { id }, - // data: { canceled: true }, - // include: { - // chapter: { select: { calendar_id: true } }, - // event_users: { - // include: { user: true }, - // where: { - // subscribed: true, - // rsvp: { name: { not: 'no' } }, - // }, - // }, - // }, - // }); - // await deleteEventReminders(id); - - // const notCanceledRsvps = event.event_users; - - // if (notCanceledRsvps.length) { - // const emailList = notCanceledRsvps.map(({ user }) => user.email); - // const subject = `Event ${event.name} canceled`; - // const body = `The event ${event.name} was canceled`; - - // new MailerService({ - // emailList: emailList, - // subject: subject, - // htmlEmail: body, - // }).sendEmail(); - // } - - // if (event.chapter.calendar_id && event.calendar_event_id) { - // try { - // // TODO: consider not awaiting. Ideally the user would see the app - // // respond immediately, but be informed of any failures later. - // // Client-side this could be handled by something like redux-saga, but - // // I'm not sure how to approach that server-side. - // await cancelCalendarEvent({ - // calendarId: event.chapter.calendar_id, - // calendarEventId: event.calendar_event_id, - // summary: event.name, - // start: event.start_at, - // end: event.ends_at, - // attendeeEmails: event.event_users.map(({ user }) => user.email), - // }); - // } catch (e) { - // console.error('Unable to cancel calendar event'); - // console.error(inspect(redactSecrets(e), { depth: null })); - // } - // } - - // return event; - // } - @Authorized(Permission.EventDelete) @Mutation(() => Event) async deleteEvent(@Arg('id', () => Int) id: number): Promise {