From 4bfb8887bfac0839fd94f57ec76d082d20f4c805 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Thu, 21 Nov 2024 17:10:07 +0100
Subject: [PATCH] Fix duplicate notifications in notification groups when using
 slow mode (#33014)

---
 .../mastodon/models/notification_group.ts     | 32 +++++++++++++++----
 .../mastodon/reducers/notification_groups.ts  | 11 ++++---
 2 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/app/javascript/mastodon/models/notification_group.ts b/app/javascript/mastodon/models/notification_group.ts
index 01a341e8d..d98e755aa 100644
--- a/app/javascript/mastodon/models/notification_group.ts
+++ b/app/javascript/mastodon/models/notification_group.ts
@@ -17,6 +17,7 @@ export const NOTIFICATIONS_GROUP_MAX_AVATARS = 8;
 interface BaseNotificationGroup
   extends Omit<BaseNotificationGroupJSON, 'sample_account_ids'> {
   sampleAccountIds: string[];
+  partial: boolean;
 }
 
 interface BaseNotificationWithStatus<Type extends NotificationWithStatusType>
@@ -142,6 +143,7 @@ export function createNotificationGroupFromJSON(
       return {
         statusId: statusId ?? undefined,
         sampleAccountIds,
+        partial: false,
         ...groupWithoutStatus,
       };
     }
@@ -150,12 +152,14 @@ export function createNotificationGroupFromJSON(
       return {
         report: createReportFromJSON(report),
         sampleAccountIds,
+        partial: false,
         ...groupWithoutTargetAccount,
       };
     }
     case 'severed_relationships':
       return {
         ...group,
+        partial: false,
         event: createAccountRelationshipSeveranceEventFromJSON(group.event),
         sampleAccountIds,
       };
@@ -163,6 +167,7 @@ export function createNotificationGroupFromJSON(
       const { moderation_warning, ...groupWithoutModerationWarning } = group;
       return {
         ...groupWithoutModerationWarning,
+        partial: false,
         moderationWarning: createAccountWarningFromJSON(moderation_warning),
         sampleAccountIds,
       };
@@ -171,6 +176,7 @@ export function createNotificationGroupFromJSON(
       const { annual_report, ...groupWithoutAnnualReport } = group;
       return {
         ...groupWithoutAnnualReport,
+        partial: false,
         annualReport: createAnnualReportEventFromJSON(annual_report),
         sampleAccountIds,
       };
@@ -178,6 +184,7 @@ export function createNotificationGroupFromJSON(
     default:
       return {
         sampleAccountIds,
+        partial: false,
         ...group,
       };
   }
@@ -185,17 +192,17 @@ export function createNotificationGroupFromJSON(
 
 export function createNotificationGroupFromNotificationJSON(
   notification: ApiNotificationJSON,
-) {
+): NotificationGroup {
   const group = {
     sampleAccountIds: [notification.account.id],
     group_key: notification.group_key,
     notifications_count: 1,
-    type: notification.type,
     most_recent_notification_id: notification.id,
     page_min_id: notification.id,
     page_max_id: notification.id,
     latest_page_notification_at: notification.created_at,
-  } as NotificationGroup;
+    partial: true,
+  };
 
   switch (notification.type) {
     case 'favourite':
@@ -204,12 +211,21 @@ export function createNotificationGroupFromNotificationJSON(
     case 'mention':
     case 'poll':
     case 'update':
-      return { ...group, statusId: notification.status?.id };
+      return {
+        ...group,
+        type: notification.type,
+        statusId: notification.status?.id,
+      };
     case 'admin.report':
-      return { ...group, report: createReportFromJSON(notification.report) };
+      return {
+        ...group,
+        type: notification.type,
+        report: createReportFromJSON(notification.report),
+      };
     case 'severed_relationships':
       return {
         ...group,
+        type: notification.type,
         event: createAccountRelationshipSeveranceEventFromJSON(
           notification.event,
         ),
@@ -217,11 +233,15 @@ export function createNotificationGroupFromNotificationJSON(
     case 'moderation_warning':
       return {
         ...group,
+        type: notification.type,
         moderationWarning: createAccountWarningFromJSON(
           notification.moderation_warning,
         ),
       };
     default:
-      return group;
+      return {
+        ...group,
+        type: notification.type,
+      };
   }
 }
diff --git a/app/javascript/mastodon/reducers/notification_groups.ts b/app/javascript/mastodon/reducers/notification_groups.ts
index 7a165f5fe..d43714beb 100644
--- a/app/javascript/mastodon/reducers/notification_groups.ts
+++ b/app/javascript/mastodon/reducers/notification_groups.ts
@@ -534,10 +534,13 @@ export const notificationGroupsReducer = createReducer<NotificationGroupsState>(
             if (existingGroupIndex > -1) {
               const existingGroup = state.groups[existingGroupIndex];
               if (existingGroup && existingGroup.type !== 'gap') {
-                group.notifications_count += existingGroup.notifications_count;
-                group.sampleAccountIds = group.sampleAccountIds
-                  .concat(existingGroup.sampleAccountIds)
-                  .slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS);
+                if (group.partial) {
+                  group.notifications_count +=
+                    existingGroup.notifications_count;
+                  group.sampleAccountIds = group.sampleAccountIds
+                    .concat(existingGroup.sampleAccountIds)
+                    .slice(0, NOTIFICATIONS_GROUP_MAX_AVATARS);
+                }
                 state.groups.splice(existingGroupIndex, 1);
               }
             }