From aac59a34ed120ad269aad70fb6600782cdd2bae8 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Thu, 25 Apr 2024 19:26:05 +0200
Subject: [PATCH] [Glitch] Add in-app notifications for moderation
 actions/warnings

Port 4ef0b48b951d65920d3a3d9cdfd099967c5e4f6d to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
---
 .../components/moderation_warning.tsx         | 78 +++++++++++++++++++
 .../notifications/components/notification.jsx | 25 ++++++
 .../flavours/glitch/reducers/notifications.js |  1 +
 .../flavours/glitch/styles/components.scss    |  3 +-
 4 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 app/javascript/flavours/glitch/features/notifications/components/moderation_warning.tsx

diff --git a/app/javascript/flavours/glitch/features/notifications/components/moderation_warning.tsx b/app/javascript/flavours/glitch/features/notifications/components/moderation_warning.tsx
new file mode 100644
index 00000000000..28c3aa19841
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/notifications/components/moderation_warning.tsx
@@ -0,0 +1,78 @@
+import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
+
+import WarningIcon from '@/material-icons/400-24px/warning-fill.svg?react';
+import { Icon } from 'flavours/glitch/components/icon';
+
+// This needs to be kept in sync with app/models/account_warning.rb
+const messages = defineMessages({
+  none: {
+    id: 'notification.moderation_warning.action_none',
+    defaultMessage: 'Your account has received a moderation warning.',
+  },
+  disable: {
+    id: 'notification.moderation_warning.action_disable',
+    defaultMessage: 'Your account has been disabled.',
+  },
+  mark_statuses_as_sensitive: {
+    id: 'notification.moderation_warning.action_mark_statuses_as_sensitive',
+    defaultMessage: 'Some of your posts have been marked as sensitive.',
+  },
+  delete_statuses: {
+    id: 'notification.moderation_warning.action_delete_statuses',
+    defaultMessage: 'Some of your posts have been removed.',
+  },
+  sensitive: {
+    id: 'notification.moderation_warning.action_sensitive',
+    defaultMessage: 'Your posts will be marked as sensitive from now on.',
+  },
+  silence: {
+    id: 'notification.moderation_warning.action_silence',
+    defaultMessage: 'Your account has been limited.',
+  },
+  suspend: {
+    id: 'notification.moderation_warning.action_suspend',
+    defaultMessage: 'Your account has been suspended.',
+  },
+});
+
+interface Props {
+  action:
+    | 'none'
+    | 'disable'
+    | 'mark_statuses_as_sensitive'
+    | 'delete_statuses'
+    | 'sensitive'
+    | 'silence'
+    | 'suspend';
+  id: string;
+  hidden: boolean;
+}
+
+export const ModerationWarning: React.FC<Props> = ({ action, id, hidden }) => {
+  const intl = useIntl();
+
+  if (hidden) {
+    return null;
+  }
+
+  return (
+    <a
+      href={`/disputes/strikes/${id}`}
+      target='_blank'
+      rel='noopener noreferrer'
+      className='notification__moderation-warning'
+    >
+      <Icon id='warning' icon={WarningIcon} />
+
+      <div className='notification__moderation-warning__content'>
+        <p>{intl.formatMessage(messages[action])}</p>
+        <span className='link-button'>
+          <FormattedMessage
+            id='notification.moderation-warning.learn_more'
+            defaultMessage='Learn more'
+          />
+        </span>
+      </div>
+    </a>
+  );
+};
diff --git a/app/javascript/flavours/glitch/features/notifications/components/notification.jsx b/app/javascript/flavours/glitch/features/notifications/components/notification.jsx
index 211a319679c..d03674bd573 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/notification.jsx
+++ b/app/javascript/flavours/glitch/features/notifications/components/notification.jsx
@@ -22,6 +22,7 @@ import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
 import FollowRequestContainer from '../containers/follow_request_container';
 import NotificationOverlayContainer from '../containers/overlay_container';
 
+import { ModerationWarning } from './moderation_warning';
 import { RelationshipsSeveranceEvent } from './relationships_severance_event';
 import Report from './report';
 
@@ -30,6 +31,7 @@ const messages = defineMessages({
   adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
   adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
   relationshipsSevered: { id: 'notification.relationships_severance_event', defaultMessage: 'Lost connections with {name}' },
+  moderationWarning: { id: 'notification.moderation_warning', defaultMessage: 'Your have received a moderation warning' },
 });
 
 const notificationForScreenReader = (intl, message, timestamp) => {
@@ -328,6 +330,27 @@ class Notification extends ImmutablePureComponent {
     );
   }
 
+  renderModerationWarning (notification) {
+    const { intl, unread, hidden } = this.props;
+    const warning = notification.get('moderation_warning');
+
+    if (!warning) {
+      return null;
+    }
+
+    return (
+      <HotKeys handlers={this.getHandlers()}>
+        <div className={classNames('notification notification-moderation-warning focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.moderationWarning), notification.get('created_at'))}>
+          <ModerationWarning
+            action={warning.get('action')}
+            id={warning.get('id')}
+            hidden={hidden}
+          />
+        </div>
+      </HotKeys>
+    );
+  }
+
   renderAdminSignUp (notification, account, link) {
     const { intl, unread } = this.props;
 
@@ -423,6 +446,8 @@ class Notification extends ImmutablePureComponent {
       return this.renderPoll(notification);
     case 'severed_relationships':
       return this.renderRelationshipsSevered(notification);
+    case 'moderation_warning':
+      return this.renderModerationWarning(notification);
     case 'admin.sign_up':
       return this.renderAdminSignUp(notification, account, link);
     case 'admin.report':
diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js
index 6edf84b67a1..c5f36dd94ae 100644
--- a/app/javascript/flavours/glitch/reducers/notifications.js
+++ b/app/javascript/flavours/glitch/reducers/notifications.js
@@ -62,6 +62,7 @@ export const notificationToMap = (notification, markForDelete = false) => Immuta
   status: notification.status ? notification.status.id : null,
   report: notification.report ? fromJS(notification.report) : null,
   event: notification.event ? fromJS(notification.event) : null,
+  moderation_warning: notification.moderation_warning ? fromJS(notification.moderation_warning) : null,
 });
 
 const normalizeNotification = (state, notification, usePendingItems) => {
diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss
index a52f3846575..96f0c81665a 100644
--- a/app/javascript/flavours/glitch/styles/components.scss
+++ b/app/javascript/flavours/glitch/styles/components.scss
@@ -2394,7 +2394,8 @@ a.account__display-name {
   }
 }
 
-.notification__relationships-severance-event {
+.notification__relationships-severance-event,
+.notification__moderation-warning {
   display: flex;
   gap: 16px;
   color: $secondary-text-color;