Skip to content

Commit 0ecd147

Browse files
feat: [REACT-218] add MessageBlocked component (#2675)
This PR adds a `MessageBlocked` component to render instead of a normal message UI whenever sync/async moderation blocks an inappropriate message. Ref: GetStream/stream-chat-js#1510 Ref: GetStream/stream-chat-css#327 Also awaits BE changes to the `message.updated` message object's payload.
1 parent 50ee29c commit 0ecd147

19 files changed

+74
-9
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
"@playwright/test": "^1.42.1",
187187
"@semantic-release/changelog": "^6.0.3",
188188
"@semantic-release/git": "^10.0.1",
189-
"@stream-io/stream-chat-css": "^5.7.2",
189+
"@stream-io/stream-chat-css": "^5.8.0",
190190
"@testing-library/dom": "^10.4.0",
191191
"@testing-library/jest-dom": "^6.6.3",
192192
"@testing-library/react": "^16.2.0",

src/components/Channel/Channel.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ type ChannelPropsForwardedToComponentContext<
128128
| 'Message'
129129
| 'MessageActions'
130130
| 'MessageBouncePrompt'
131+
| 'MessageBlocked'
131132
| 'MessageDeleted'
132133
| 'MessageListNotifications'
133134
| 'MessageListMainPanel'
@@ -1337,6 +1338,7 @@ const ChannelInner = <
13371338
LoadingIndicator: props.LoadingIndicator,
13381339
Message: props.Message,
13391340
MessageActions: props.MessageActions,
1341+
MessageBlocked: props.MessageBlocked,
13401342
MessageBouncePrompt: props.MessageBouncePrompt,
13411343
MessageDeleted: props.MessageDeleted,
13421344
MessageListNotifications: props.MessageListNotifications,
@@ -1389,6 +1391,7 @@ const ChannelInner = <
13891391
props.DateSeparator,
13901392
props.EditMessageInput,
13911393
props.EmojiPicker,
1394+
props.emojiSearchIndex,
13921395
props.EmptyStateIndicator,
13931396
props.FileUploadIcon,
13941397
props.GiphyPreviewMessage,
@@ -1398,6 +1401,7 @@ const ChannelInner = <
13981401
props.LoadingIndicator,
13991402
props.Message,
14001403
props.MessageActions,
1404+
props.MessageBlocked,
14011405
props.MessageBouncePrompt,
14021406
props.MessageDeleted,
14031407
props.MessageListNotifications,
@@ -1417,11 +1421,14 @@ const ChannelInner = <
14171421
props.QuotedMessage,
14181422
props.QuotedMessagePreview,
14191423
props.QuotedPoll,
1424+
props.reactionOptions,
14201425
props.ReactionSelector,
14211426
props.ReactionsList,
14221427
props.ReactionsListModal,
14231428
props.SendButton,
14241429
props.StartRecordingAudioButton,
1430+
props.StopAIGenerationButton,
1431+
props.StreamedMessageText,
14251432
props.ThreadHead,
14261433
props.ThreadHeader,
14271434
props.ThreadStart,
@@ -1431,10 +1438,6 @@ const ChannelInner = <
14311438
props.UnreadMessagesNotification,
14321439
props.UnreadMessagesSeparator,
14331440
props.VirtualMessage,
1434-
props.StopAIGenerationButton,
1435-
props.StreamedMessageText,
1436-
props.emojiSearchIndex,
1437-
props.reactionOptions,
14381441
],
14391442
);
14401443

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import clsx from 'clsx';
3+
4+
import { useUserRole } from './hooks/useUserRole';
5+
import { useTranslationContext } from '../../context/TranslationContext';
6+
import { useMessageContext } from '../../context';
7+
8+
export const MessageBlocked = () => {
9+
const { message } = useMessageContext();
10+
const { t } = useTranslationContext('MessageBlocked');
11+
12+
const { isMyMessage } = useUserRole(message);
13+
14+
const messageClasses = clsx(
15+
'str-chat__message str-chat__message-simple str-chat__message--blocked',
16+
message.type,
17+
{
18+
'str-chat__message--me str-chat__message-simple--me': isMyMessage,
19+
'str-chat__message--other': !isMyMessage,
20+
},
21+
);
22+
23+
return (
24+
<div
25+
className={messageClasses}
26+
data-testid='message-blocked-component'
27+
key={message.id}
28+
>
29+
<div className='str-chat__message--blocked-inner'>
30+
{t<string>('Message was blocked by moderation policies')}
31+
</div>
32+
</div>
33+
);
34+
};

src/components/Message/MessageSimple.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import clsx from 'clsx';
44
import { MessageErrorIcon } from './icons';
55
import { MessageBouncePrompt as DefaultMessageBouncePrompt } from '../MessageBounce';
66
import { MessageDeleted as DefaultMessageDeleted } from './MessageDeleted';
7+
import { MessageBlocked as DefaultMessageBlocked } from './MessageBlocked';
78
import { MessageOptions as DefaultMessageOptions } from './MessageOptions';
89
import { MessageRepliesCountButton as DefaultMessageRepliesCountButton } from './MessageRepliesCountButton';
910
import { MessageStatus as DefaultMessageStatus } from './MessageStatus';
1011
import { MessageText } from './MessageText';
1112
import { MessageTimestamp as DefaultMessageTimestamp } from './MessageTimestamp';
1213
import {
1314
areMessageUIPropsEqual,
15+
isMessageBlocked,
1416
isMessageBounced,
1517
isMessageEdited,
1618
messageHasAttachments,
@@ -77,6 +79,7 @@ const MessageSimpleWithContext = <
7779
// TODO: remove this "passthrough" in the next
7880
// major release and use the new default instead
7981
MessageActions = MessageOptions,
82+
MessageBlocked = DefaultMessageBlocked,
8083
MessageDeleted = DefaultMessageDeleted,
8184
MessageBouncePrompt = DefaultMessageBouncePrompt,
8285
MessageRepliesCountButton = DefaultMessageRepliesCountButton,
@@ -102,6 +105,10 @@ const MessageSimpleWithContext = <
102105
return <MessageDeleted message={message} />;
103106
}
104107

108+
if (isMessageBlocked(message)) {
109+
return <MessageBlocked />;
110+
}
111+
105112
const showMetadata = !groupedByUser || endOfGroup;
106113
const showReplyCountButton = !threadList && !!message.reply_count;
107114
const allowRetry = message.status === 'failed' && message.errorStatusCode !== 403;

src/components/Message/utils.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,13 @@ export const isMessageBounced = <
496496
(message.moderation_details?.action === 'MESSAGE_RESPONSE_ACTION_BOUNCE' ||
497497
message.moderation?.action === 'bounce');
498498

499+
export const isMessageBlocked = (
500+
message: Pick<StreamMessage, 'type' | 'moderation' | 'moderation_details'>,
501+
) =>
502+
message.type === 'error' &&
503+
(message.moderation_details?.action === 'MESSAGE_RESPONSE_ACTION_REMOVE' ||
504+
message.moderation?.action === 'remove');
505+
499506
export const isMessageEdited = <
500507
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
501508
>(

src/context/ComponentContext.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ export type ComponentContextValue<
132132
MessageActions?: React.ComponentType;
133133
/** Custom UI component to display the contents of a bounced message modal. Usually it allows to retry, edit, or delete the message. Defaults to and accepts the same props as: [MessageBouncePrompt](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageBounce/MessageBouncePrompt.tsx) */
134134
MessageBouncePrompt?: React.ComponentType<MessageBouncePromptProps>;
135+
/** Custom UI component for a moderation-blocked message, defaults to and accepts same props as: [MessageBlocked](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageBlocked.tsx) */
136+
MessageBlocked?: React.ComponentType;
135137
/** Custom UI component for a deleted message, defaults to and accepts same props as: [MessageDeleted](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageDeleted.tsx) */
136138
MessageDeleted?: React.ComponentType<MessageDeletedProps<StreamChatGenerics>>;
137139
MessageListMainPanel?: React.ComponentType<PropsWithChildrenOnly>;

src/i18n/de.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Nachricht gelöscht",
6868
"Message has been successfully flagged": "Nachricht wurde erfolgreich gemeldet",
6969
"Message pinned": "Nachricht angeheftet",
70+
"Message was blocked by moderation policies": "Nachricht wurde durch moderationsrichtlinien blockiert",
7071
"Messages have been marked unread.": "Nachrichten wurden als ungelesen markiert.",
7172
"Missing permissions to upload the attachment": "Fehlende Berechtigungen zum Hochladen des Anhangs",
7273
"Multiple answers": "Mehrere Antworten",

src/i18n/en.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Message deleted",
6868
"Message has been successfully flagged": "Message has been successfully flagged",
6969
"Message pinned": "Message pinned",
70+
"Message was blocked by moderation policies": "Message was blocked by moderation policies",
7071
"Messages have been marked unread.": "Messages have been marked unread.",
7172
"Missing permissions to upload the attachment": "Missing permissions to upload the attachment",
7273
"Multiple answers": "Multiple answers",

src/i18n/es.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Mensaje eliminado",
6868
"Message has been successfully flagged": "El mensaje se marcó correctamente",
6969
"Message pinned": "Mensaje fijado",
70+
"Message was blocked by moderation policies": "El mensaje fue bloqueado por las políticas de moderación",
7071
"Messages have been marked unread.": "Los mensajes han sido marcados como no leídos.",
7172
"Missing permissions to upload the attachment": "Faltan permisos para subir el archivo adjunto",
7273
"Multiple answers": "Múltiples respuestas",

src/i18n/fr.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Message supprimé",
6868
"Message has been successfully flagged": "Le message a été signalé avec succès",
6969
"Message pinned": "Message épinglé",
70+
"Message was blocked by moderation policies": "Le message a été bloqué par les politiques de modération",
7071
"Messages have been marked unread.": "Les messages ont été marqués comme non lus.",
7172
"Missing permissions to upload the attachment": "Autorisations manquantes pour télécharger la pièce jointe",
7273
"Multiple answers": "Réponses multiples",

src/i18n/hi.json

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"Message deleted": "मैसेज हटा दिया गया",
6969
"Message has been successfully flagged": "मैसेज को फ्लैग कर दिया गया है",
7070
"Message pinned": "संदेश पिन किया गया",
71+
"Message was blocked by moderation policies": "संदेश को मॉडरेशन नीतियों द्वारा ब्लॉक कर दिया गया है",
7172
"Messages have been marked unread.": "संदेशों को अपठित चिह्नित किया गया है।",
7273
"Missing permissions to upload the attachment": "अटैचमेंट अपलोड करने के लिए अनुमतियां गायब",
7374
"Multiple answers": "कई उत्तर",

src/i18n/it.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Messaggio cancellato",
6868
"Message has been successfully flagged": "Il messaggio è stato segnalato con successo",
6969
"Message pinned": "Messaggio bloccato",
70+
"Message was blocked by moderation policies": "Il messaggio è stato bloccato dalle politiche di moderazione",
7071
"Messages have been marked unread.": "I messaggi sono stati contrassegnati come non letti.",
7172
"Missing permissions to upload the attachment": "Autorizzazioni mancanti per caricare l'allegato",
7273
"Multiple answers": "Risposte multiple",

src/i18n/ja.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "メッセージが削除されました",
6868
"Message has been successfully flagged": "メッセージに正常にフラグが付けられました",
6969
"Message pinned": "メッセージにピンが付けられました",
70+
"Message was blocked by moderation policies": "メッセージはモデレーションポリシーによってブロックされました",
7071
"Messages have been marked unread.": "メッセージは未読としてマークされました。",
7172
"Missing permissions to upload the attachment": "添付ファイルをアップロードするための許可がありません",
7273
"Multiple answers": "複数回答",

src/i18n/ko.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "메시지가 삭제되었습니다.",
6868
"Message has been successfully flagged": "메시지에 플래그가 지정되었습니다.",
6969
"Message pinned": "메시지 핀했습니다",
70+
"Message was blocked by moderation policies": "메시지가 관리 정책에 의해 차단되었습니다.",
7071
"Messages have been marked unread.": "메시지가 읽지 않음으로 표시되었습니다.",
7172
"Missing permissions to upload the attachment": "첨부 파일을 업로드하려면 권한이 필요합니다",
7273
"Multiple answers": "복수 응답",

src/i18n/nl.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Bericht verwijderd",
6868
"Message has been successfully flagged": "Bericht is succesvol gemarkeerd",
6969
"Message pinned": "Bericht vastgezet",
70+
"Message was blocked by moderation policies": "Bericht is geblokkeerd door moderatiebeleid",
7071
"Messages have been marked unread.": "Berichten zijn gemarkeerd als ongelezen.",
7172
"Missing permissions to upload the attachment": "Missende toestemmingen om de bijlage te uploaden",
7273
"Multiple answers": "Meerdere antwoorden",

src/i18n/pt.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Mensagem apagada",
6868
"Message has been successfully flagged": "A mensagem foi reportada com sucesso",
6969
"Message pinned": "Mensagem fixada",
70+
"Message was blocked by moderation policies": "A mensagem foi bloqueada pelas políticas de moderação",
7071
"Messages have been marked unread.": "Mensagens foram marcadas como não lidas.",
7172
"Missing permissions to upload the attachment": "Faltando permissões para enviar o anexo",
7273
"Multiple answers": "Múltiplas respostas",

src/i18n/ru.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Сообщение удалено",
6868
"Message has been successfully flagged": "Жалоба на сообщение была принята",
6969
"Message pinned": "Сообщение закреплено",
70+
"Message was blocked by moderation policies": "Сообщение было заблокировано модерацией",
7071
"Messages have been marked unread.": "Сообщения были отмечены как непрочитанные.",
7172
"Missing permissions to upload the attachment": "Отсутствуют разрешения для загрузки вложения",
7273
"Multiple answers": "Несколько ответов",

src/i18n/tr.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"Message deleted": "Mesaj silindi",
6868
"Message has been successfully flagged": "Mesaj başarıyla bayraklandı",
6969
"Message pinned": "Mesaj sabitlendi",
70+
"Message was blocked by moderation policies": "Mesaj moderasyon politikaları tarafından engellendi",
7071
"Messages have been marked unread.": "Mesajlar okunmamış olarak işaretlendi.",
7172
"Missing permissions to upload the attachment": "Ek yüklemek için izinler eksik",
7273
"Multiple answers": "Çoklu cevaplar",

yarn.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -2438,10 +2438,10 @@
24382438
resolved "https://registry.yarnpkg.com/@stream-io/escape-string-regexp/-/escape-string-regexp-5.0.1.tgz#362505c92799fea6afe4e369993fbbda8690cc37"
24392439
integrity sha512-qIaSrzJXieZqo2fZSYTdzwSbZgHHsT3tkd646vvZhh4fr+9nO4NlvqGmPF43Y+OfZiWf+zYDFgNiPGG5+iZulQ==
24402440

2441-
"@stream-io/stream-chat-css@^5.7.2":
2442-
version "5.7.2"
2443-
resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-5.7.2.tgz#0bd05bb62e9f43d6158af9c3b57798aab4bf2be4"
2444-
integrity sha512-AI5uoG9PHaTavkmEMLD1UXHAKD5L4CrGl/FWXH8RK5yldnnHh+4MSeyrPG4IZrCGNd1jqcZ9tYN1lRoY7jUwGA==
2441+
"@stream-io/stream-chat-css@^5.8.0":
2442+
version "5.8.0"
2443+
resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-5.8.0.tgz#6c0032544cfbc59447f71ef88b3c0736aaa676a8"
2444+
integrity sha512-p9Z1Ktx3E+o+7EFubNyZWNjg26UNgux8IAK0nhoC04t47Cok5pSHcUbPmTgiICSPXYNNEcDxrCil5crM0ItUUA==
24452445

24462446
"@stream-io/transliterate@^1.5.5":
24472447
version "1.5.5"

0 commit comments

Comments
 (0)