Skip to content

Commit fc44d45

Browse files
committed
feat: message bounce flow
1 parent ceb7463 commit fc44d45

26 files changed

+508
-68
lines changed
910 KB
Loading

docusaurus/docs/Angular/components/ChannelComponent.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
The `Channel` component is a container component that displays the [`ChannelHeader`](./ChannelHeaderComponent.mdx), [`MessageList`](./MessageListComponent.mdx), [`NotificationList`](./NotificationListComponent.mdx), [`EditMessageForm`](./EditMessageFormComponent.mdx) and [`MessageInput`](./MessageInputComponent.mdx) components. You can also provide the [`Thread`](./ThreadComponent.mdx) component to use message [threads](https://getstream.io/chat/docs/javascript/threads/?language=javascript).
1+
The `Channel` component is a container component that displays the [`ChannelHeader`](./ChannelHeaderComponent.mdx), [`MessageList`](./MessageListComponent.mdx), [`NotificationList`](./NotificationListComponent.mdx), [`EditMessageForm`](./EditMessageFormComponent.mdx), [`MessageBouncePrompt`](./MessageBouncePromptComponent.mdx) and [`MessageInput`](./MessageInputComponent.mdx) components. You can also provide the [`Thread`](./ThreadComponent.mdx) component to use message [threads](https://getstream.io/chat/docs/javascript/threads/?language=javascript).
22

33
## Usage
44

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Screenshot from "../assets/message-bounce-screenshot.png";
2+
3+
The component watches for the [`channelService.bouncedMessage$` stream](../../services/ChannelService/#bouncedmessage) and opens the bounce modal if a message is emitted.
4+
5+
To bounce messages, you need to set up [semantic filters for moderation](https://getstream.io/automated-moderation/docs/automod_configuration/?q=semantic%20filters).
6+
7+
## Usage
8+
9+
To bounce messages, you need to set up [semantic filters for moderation](https://getstream.io/automated-moderation/docs/automod_configuration/?q=semantic%20filters).
10+
11+
When a user tries to send a message that voilates the content guidelines set up by the semantic filter the `StreamMessage` object will have the following format:
12+
13+
- `type` will be `error`
14+
- the `moderation_details` property will contain the details of the error
15+
16+
The built-in [message component](../../components/MessageComponent) will open the bounce prompt when a bounced message is clicked. If you have a custom UI, this is how you can do that:
17+
18+
```typescript
19+
constructor(private channelService: ChannelService) {
20+
// Open the modal
21+
this.messageActionsService.bouncedMessage$(<stream message>);
22+
// Close the modal
23+
this.messageActionsService.bouncedMessage$(undefined);
24+
}
25+
```
26+
27+
The built-in [channel component](../../components/ChannelComponent) displays the `stream-message-bounce-prompt` component. If you have a custom UI, this is how you can do that:
28+
29+
```html
30+
<stream-message-bounce-prompt></stream-message-bounce-prompt>
31+
```
32+
33+
<img src={Screenshot} width="1000" />
34+
35+
## Customization
36+
37+
You can provide your own custom component using the [`customTemplatesService.messageBouncePromptTemplate$` property](../../services/CustomTemplatesService/#messagebounceprompttemplate)
38+
39+
[//]: # "Start of generated content"
40+
[//]: # "End of generated content"

projects/customizations-example/src/app/app.module.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { PickerModule } from '@ctrl/ngx-emoji-mart';
1212
import { MessageActionComponent } from './message-action/message-action.component';
1313
import { ThreadHeaderComponent } from './thread-header/thread-header.component';
1414
import { IconComponent } from './icon/icon.component';
15-
import { MessageActionsComponent } from './message-actions/message-actions.component';
1615

1716
@NgModule({
1817
declarations: [
@@ -21,7 +20,6 @@ import { MessageActionsComponent } from './message-actions/message-actions.compo
2120
MessageActionComponent,
2221
ThreadHeaderComponent,
2322
IconComponent,
24-
MessageActionsComponent,
2523
],
2624
imports: [
2725
BrowserModule,

projects/customizations-example/src/app/message-actions/message-actions.component.html

Lines changed: 0 additions & 3 deletions
This file was deleted.

projects/customizations-example/src/app/message-actions/message-actions.component.scss

Whitespace-only changes.

projects/customizations-example/src/app/message-actions/message-actions.component.spec.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

projects/customizations-example/src/app/message-actions/message-actions.component.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

projects/stream-chat-angular/src/assets/i18n/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,8 @@ export const en = {
114114
'Unread messages': 'Unread messages',
115115
'{{count}} unread messages': '{{count}} unread messages',
116116
'{{count}} unread message': '{{count}} unread message',
117+
'This message did not meet our content guidelines':
118+
'This message did not meet our content guidelines',
119+
'Send Anyway': 'Send Anyway',
117120
},
118121
};

projects/stream-chat-angular/src/lib/channel.service.spec.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ChannelSort,
1010
Event,
1111
FormatMessageResponse,
12+
Message,
1213
ReactionResponse,
1314
SendMessageAPIResponse,
1415
UserResponse,
@@ -1349,6 +1350,28 @@ describe('ChannelService', () => {
13491350
expect(mockChatClient.updateMessage).toHaveBeenCalledWith(message);
13501351
});
13511352

1353+
it('should resend instead of update if message was bounced', async () => {
1354+
await init();
1355+
const message = mockMessage();
1356+
const channel = service.activeChannel!;
1357+
spyOn(channel, 'sendMessage');
1358+
message.moderation_details = {
1359+
original_text: 'Ricciardo should retire',
1360+
action: 'MESSAGE_RESPONSE_ACTION_BOUNCE',
1361+
harms: [
1362+
{
1363+
name: 'hammurabi_filter',
1364+
phrase_list_ids: [139],
1365+
},
1366+
],
1367+
error_msg: 'this message did not meet our content guidelines',
1368+
};
1369+
void service.updateMessage(message);
1370+
1371+
expect(mockChatClient.updateMessage).not.toHaveBeenCalledWith();
1372+
expect(channel.sendMessage).not.toHaveBeenCalledWith(message as Message);
1373+
});
1374+
13521375
it('should update message - #beforeUpdateMessage is provided', async () => {
13531376
const message = mockMessage();
13541377
mockChatClient.updateMessage.and.resolveTo({ message });
@@ -1375,11 +1398,29 @@ describe('ChannelService', () => {
13751398
expect(mockChatClient.updateMessage).toHaveBeenCalledWith(message);
13761399
});
13771400

1378-
it('should delete message', () => {
1401+
it('should delete message', async () => {
1402+
await init();
1403+
const channel = service.activeChannel;
1404+
spyOn(channel!.state, 'removeMessage');
13791405
const message = mockMessage();
13801406
void service.deleteMessage(message);
13811407

13821408
expect(mockChatClient.deleteMessage).toHaveBeenCalledWith(message.id);
1409+
expect(channel!.state.removeMessage).not.toHaveBeenCalled();
1410+
});
1411+
1412+
it('should delete message - local', async () => {
1413+
await init();
1414+
const message = mockMessage();
1415+
const channel = service.activeChannel;
1416+
spyOn(channel!.state, 'removeMessage');
1417+
void service.deleteMessage(message, true);
1418+
1419+
expect(mockChatClient.deleteMessage).not.toHaveBeenCalledWith();
1420+
expect(channel!.state.removeMessage).toHaveBeenCalledWith({
1421+
id: message.id,
1422+
parent_id: undefined,
1423+
});
13831424
});
13841425

13851426
it(`should call #messageDeleteConfirmationHandler is that's provided`, async () => {

0 commit comments

Comments
 (0)