Skip to content

Commit 64843a0

Browse files
authored
Merge pull request #186 from GetStream/qoute-message
feat: Quote reply to messages #174
2 parents 32aa69f + cb1f6c6 commit 64843a0

21 files changed

+513
-84
lines changed

docusaurus/docs/Angular/components/message-actions.mdx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ export class CustomMessageComponent {
3434
}
3535
```
3636

37-
:::important
38-
The `MessageActionBox` component doesn't check if the user has the necessary capabilities to perform a given aciton, it is done by the [`MessageList`](./message-list.mdx/#enabledmessageactions) component.
39-
:::
40-
4137
## Inputs
4238

4339
### <div class="label required basic">Required</div> message

docusaurus/docs/Angular/concepts/message-interactions.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ Users can interact with the messages in the message list. The following table pr
1616
| Send reaction to a message | send-reaction | [`MessageReactions`](../components/message-reactions.mdx) |
1717
| Receive read events | read-events | [`Message`](../components/message.mdx) |
1818
| Reply to a message in a thread | send-reply | [`Message`](../components/message.mdx) |
19+
| Quote reply to a message | quote-message | [`MessageActionsBox`](../components/message-actions.mdx) |

docusaurus/docs/Angular/services/channel.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ Sets the given `channel` as active.
5454

5555
Sets the given `message` as an active parent message. If `undefined` is provided, it will deleselect the current parent message.
5656

57+
## selectMessageToQuote
58+
59+
Sets the given `message` as a message to be quoted. If `undefined` is provided, it will deleselect the current message.
60+
5761
## hasMoreChannels$
5862

5963
Emits `false` if there are no more pages of channels that can be loaded.
@@ -74,6 +78,10 @@ Emits the id of the currently selected parent message. If no message is selected
7478

7579
Emits the currently selected parent message. If no message is selected, it emits undefined.
7680

81+
## messageToQuote$
82+
83+
Emits the currently selected message to be quoted. If no message is selected, it emits undefined.
84+
7785
## activeThreadMessages$
7886

7987
Emits the list of currently loaded thread replies belonging to the selected parent message. If there is no currently active thread it emits an empty array.

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
"@angular/router": "~12.2.0",
7979
"@ngx-translate/core": "^13.0.0",
8080
"@ngx-translate/http-loader": "^6.0.0",
81-
"@stream-io/stream-chat-css": "2.1.0",
81+
"@stream-io/stream-chat-css": "2.2.0",
8282
"@stream-io/transliterate": "^1.5.2",
8383
"angular-mentions": "^1.4.0",
8484
"dayjs": "^1.10.7",

projects/stream-chat-angular/package.json

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,7 @@
1313
"@angular/core": "^12.2.0 || ^13.0.0",
1414
"@ngx-translate/core": "^13.0.0 || ^14.0.0",
1515
"stream-chat": ">=4.3.0",
16-
"stream-chat-css": "1.0.23",
17-
"@stream-io/stream-chat-css": "2.1.0"
18-
},
19-
"peerDependenciesMeta": {
20-
"stream-chat-css": {
21-
"optional": true
22-
},
23-
"@stream-io/stream-chat-css": {
24-
"optional": true
25-
}
16+
"@stream-io/stream-chat-css": "2.2.0"
2617
},
2718
"dependencies": {
2819
"angular-mentions": "^1.4.0",

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,18 @@ describe('ChannelService', () => {
130130
service.activeChannel$.subscribe(activeChannelSpy);
131131
const channelsSpy = jasmine.createSpy();
132132
service.channels$.subscribe(channelsSpy);
133+
const messageToQuoteSpy = jasmine.createSpy();
134+
service.messageToQuote$.subscribe(messageToQuoteSpy);
133135
messagesSpy.calls.reset();
134136
activeChannelSpy.calls.reset();
135137
channelsSpy.calls.reset();
138+
messageToQuoteSpy.calls.reset();
136139
service.reset();
137140

138141
expect(messagesSpy).toHaveBeenCalledWith([]);
139142
expect(channelsSpy).toHaveBeenCalledWith(undefined);
140143
expect(activeChannelSpy).toHaveBeenCalledWith(undefined);
144+
expect(messageToQuoteSpy).toHaveBeenCalledWith(undefined);
141145
});
142146

143147
it('should tell if user #hasMoreChannels$', async () => {
@@ -183,6 +187,9 @@ describe('ChannelService', () => {
183187
const messagesSpy = jasmine.createSpy();
184188
service.activeChannelMessages$.subscribe(messagesSpy);
185189
messagesSpy.calls.reset();
190+
const messageToQuoteSpy = jasmine.createSpy();
191+
service.messageToQuote$.subscribe(messageToQuoteSpy);
192+
messageToQuoteSpy.calls.reset();
186193
const newActiveChannel = mockChannels[1];
187194
spyOn(newActiveChannel, 'markRead');
188195
service.setAsActiveChannel(newActiveChannel);
@@ -191,6 +198,7 @@ describe('ChannelService', () => {
191198
expect(result.cid).toBe(newActiveChannel.cid);
192199
expect(messagesSpy).toHaveBeenCalledWith(jasmine.any(Object));
193200
expect(newActiveChannel.markRead).toHaveBeenCalledWith();
201+
expect(messageToQuoteSpy).toHaveBeenCalledWith(undefined);
194202
});
195203

196204
it('should emit #activeChannelMessages$', async () => {
@@ -782,11 +790,18 @@ describe('ChannelService', () => {
782790
const text = 'Hi';
783791
const attachments = [{ fallback: 'image.png', url: 'url/to/image' }];
784792
const mentionedUsers = [{ id: 'sara', name: 'Sara' }];
793+
const quotedMessageId = 'quotedMessage';
785794
let prevMessageCount!: number;
786795
service.activeChannelMessages$
787796
.pipe(first())
788797
.subscribe((m) => (prevMessageCount = m.length));
789-
await service.sendMessage(text, attachments, mentionedUsers);
798+
await service.sendMessage(
799+
text,
800+
attachments,
801+
mentionedUsers,
802+
undefined,
803+
quotedMessageId
804+
);
790805
let latestMessage!: StreamMessage;
791806
let messageCount!: number;
792807
service.activeChannelMessages$.subscribe((m) => {
@@ -800,6 +815,7 @@ describe('ChannelService', () => {
800815
mentioned_users: ['sara'],
801816
id: jasmine.any(String),
802817
parent_id: undefined,
818+
quoted_message_id: quotedMessageId,
803819
});
804820

805821
expect(channel.state.addMessageSorted).toHaveBeenCalledWith(
@@ -1167,4 +1183,23 @@ describe('ChannelService', () => {
11671183

11681184
expect(result.length).toBe(1);
11691185
});
1186+
1187+
it('should select message to be quoted', async () => {
1188+
await init();
1189+
const spy = jasmine.createSpy();
1190+
service.messageToQuote$.subscribe(spy);
1191+
const message = mockMessage();
1192+
service.selectMessageToQuote(message);
1193+
1194+
expect(spy).toHaveBeenCalledWith(message);
1195+
});
1196+
1197+
it('should deselect message to be quoted', async () => {
1198+
await init();
1199+
const spy = jasmine.createSpy();
1200+
service.messageToQuote$.subscribe(spy);
1201+
service.selectMessageToQuote(undefined);
1202+
1203+
expect(spy).toHaveBeenCalledWith(undefined);
1204+
});
11701205
});

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,43 @@ describe('ChannelService - threads', () => {
126126

127127
it('should remove active parent message and reset thread messages', async () => {
128128
await init();
129+
const messageToQuote = mockMessage();
130+
messageToQuote.parent_id = 'parentId';
131+
service.selectMessageToQuote(messageToQuote);
129132
const messagesSpy = jasmine.createSpy();
130133
const activeParentMessageIdSpy = jasmine.createSpy();
131134
const activeParentMessageSpy = jasmine.createSpy();
135+
const messageToQuoteSpy = jasmine.createSpy();
132136
service.activeThreadMessages$.subscribe(messagesSpy);
133137
service.activeParentMessageId$.subscribe(activeParentMessageIdSpy);
134138
service.activeParentMessage$.subscribe(activeParentMessageSpy);
139+
service.messageToQuote$.subscribe(messageToQuoteSpy);
135140
messagesSpy.calls.reset();
136141
activeParentMessageIdSpy.calls.reset();
137142
activeParentMessageSpy.calls.reset();
143+
messageToQuoteSpy.calls.reset();
138144
await service.setAsActiveParentMessage(undefined);
139145

140146
expect(messagesSpy).toHaveBeenCalledWith([]);
141147
expect(activeParentMessageIdSpy).toHaveBeenCalledWith(undefined);
142148
expect(activeParentMessageSpy).toHaveBeenCalledWith(undefined);
149+
expect(messageToQuoteSpy).toHaveBeenCalledWith(undefined);
150+
});
151+
152+
it(`shouldn't deselect message to quote, if not a thread reply`, async () => {
153+
await init();
154+
const messageToQuoteSpy = jasmine.createSpy();
155+
service.messageToQuote$.subscribe(messageToQuoteSpy);
156+
const parentMessage = mockMessage();
157+
parentMessage.id = 'parentMessage';
158+
const messageToQuote = mockMessage();
159+
messageToQuote.parent_id = undefined;
160+
await service.setAsActiveParentMessage(parentMessage);
161+
service.selectMessageToQuote(messageToQuote);
162+
messageToQuoteSpy.calls.reset();
163+
await service.setAsActiveParentMessage(undefined);
164+
165+
expect(messageToQuoteSpy).not.toHaveBeenCalled();
143166
});
144167

145168
it('should reset', async () => {
@@ -434,6 +457,7 @@ describe('ChannelService - threads', () => {
434457
mentioned_users: ['sara'],
435458
id: jasmine.any(String),
436459
parent_id: 'parentId',
460+
quoted_message_id: undefined,
437461
});
438462

439463
expect(channel.state.addMessageSorted).toHaveBeenCalledWith(

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export class ChannelService {
3535
activeParentMessageId$: Observable<string | undefined>;
3636
activeThreadMessages$: Observable<StreamMessage[]>;
3737
activeParentMessage$: Observable<StreamMessage | undefined>;
38+
messageToQuote$: Observable<StreamMessage | undefined>;
3839
customNewMessageNotificationHandler?: (
3940
notification: Notification,
4041
channelListSetter: (channels: Channel[]) => void
@@ -116,6 +117,9 @@ export class ChannelService {
116117
private sort: ChannelSort | undefined;
117118
private options: ChannelOptions | undefined;
118119
private readonly messagePageSize = 25;
120+
private messageToQuoteSubject = new BehaviorSubject<
121+
StreamMessage | undefined
122+
>(undefined);
119123

120124
private channelListSetter = (channels: Channel[]) => {
121125
this.channelsSubject.next(channels);
@@ -176,6 +180,7 @@ export class ChannelService {
176180
),
177181
shareReplay()
178182
);
183+
this.messageToQuote$ = this.messageToQuoteSubject.asObservable();
179184

180185
this.chatClientService.connectionState$
181186
.pipe(filter((s) => s === 'online'))
@@ -198,9 +203,14 @@ export class ChannelService {
198203
this.activeChannelMessagesSubject.next([...channel.state.messages]);
199204
this.activeParentMessageIdSubject.next(undefined);
200205
this.activeThreadMessagesSubject.next([]);
206+
this.messageToQuoteSubject.next(undefined);
201207
}
202208

203209
async setAsActiveParentMessage(message: StreamMessage | undefined) {
210+
const messageToQuote = this.messageToQuoteSubject.getValue();
211+
if (messageToQuote && !!messageToQuote.parent_id) {
212+
this.messageToQuoteSubject.next(undefined);
213+
}
204214
if (!message) {
205215
this.activeParentMessageIdSubject.next(undefined);
206216
this.activeThreadMessagesSubject.next([]);
@@ -276,6 +286,7 @@ export class ChannelService {
276286
this.activeParentMessageIdSubject.next(undefined);
277287
this.activeThreadMessagesSubject.next([]);
278288
this.channelsSubject.next(undefined);
289+
this.selectMessageToQuote(undefined);
279290
}
280291

281292
async loadMoreChannels() {
@@ -299,14 +310,16 @@ export class ChannelService {
299310
text: string,
300311
attachments: Attachment[] = [],
301312
mentionedUsers: UserResponse[] = [],
302-
parentId: string | undefined = undefined
313+
parentId: string | undefined = undefined,
314+
quotedMessageId: string | undefined = undefined
303315
) {
304316
const preview = createMessagePreview(
305317
this.chatClientService.chatClient.user!,
306318
text,
307319
attachments,
308320
mentionedUsers,
309-
parentId
321+
parentId,
322+
quotedMessageId
310323
);
311324
const channel = this.activeChannelSubject.getValue()!;
312325
preview.readBy = [];
@@ -429,6 +442,10 @@ export class ChannelService {
429442
}
430443
}
431444

445+
selectMessageToQuote(message: StreamMessage | undefined) {
446+
this.messageToQuoteSubject.next(message);
447+
}
448+
432449
private async sendMessageRequest(preview: MessageResponse | StreamMessage) {
433450
const channel = this.activeChannelSubject.getValue()!;
434451
const isThreadReply = !!preview.parent_id;
@@ -444,6 +461,7 @@ export class ChannelService {
444461
mentioned_users: preview.mentioned_users?.map((u) => u.id),
445462
id: preview.id,
446463
parent_id: preview.parent_id,
464+
quoted_message_id: preview.quoted_message_id,
447465
});
448466
if (response?.message) {
449467
channel.state.addMessageSorted(

projects/stream-chat-angular/src/lib/icon/icon.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,11 @@
186186
/>
187187
</svg>
188188
<svg
189-
data-testid="close-no-outline"
190-
*ngIf="icon === 'close-no-outline'"
191189
height="10"
192190
width="10"
193191
xmlns="http://www.w3.org/2000/svg"
192+
data-testid="close-no-outline"
193+
*ngIf="icon === 'close-no-outline'"
194194
>
195195
<path
196196
d="M9.916 1.027L8.973.084 5 4.058 1.027.084l-.943.943L4.058 5 .084 8.973l.943.943L5 5.942l3.973 3.974.943-.943L5.942 5z"

0 commit comments

Comments
 (0)