Skip to content

Commit 620ed8e

Browse files
authored
Merge pull request #546 from GetStream/mark-unread
Mark unread
2 parents 147d45f + fa930a9 commit 620ed8e

19 files changed

+724
-158
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ Users can interact with the messages in the message list. The following table pr
1717
| Receive read events | read-events | [`Message`](../components/MessageComponent.mdx) |
1818
| Reply to a message in a thread | send-reply | [`Message`](../components/MessageComponent.mdx) |
1919
| Quote reply to a message | quote-message | [`MessageActionsBox`](../components/MessageActionsBoxComponent.mdx) |
20+
| Mark a message as unread | read-events | [`MessageActionsBox`](../components/MessageActionsBoxComponent.mdx) |

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
@@ -126,7 +126,7 @@
126126
"ngx-popperjs": "^12.2.2",
127127
"pretty-bytes": "^5.6.0",
128128
"rxjs": "^7.1.0",
129-
"stream-chat": "^8.14.2",
129+
"stream-chat": "^8.15.0",
130130
"ts-node": "^10.2.1",
131131
"tslib": "^2.3.0",
132132
"uuidv4": "^6.2.12",

projects/stream-chat-angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@angular/common": "^12.2.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0",
1313
"@angular/core": "^12.2.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0",
1414
"@ngx-translate/core": "^13.0.0 || ^14.0.0 || ^15.0.0",
15-
"stream-chat": "^8.11.0"
15+
"stream-chat": "^8.15.0"
1616
},
1717
"dependencies": {
1818
"angular-mentions": "^1.4.0",

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,12 @@ export const en = {
107107
'See original (automatically translated)':
108108
'See original (automatically translated)',
109109
'See translation': 'See translation',
110+
'Mark as unread': 'Mark as unread',
111+
'Error marking message as unread': 'Error marking message as unread',
112+
'Error, only the first {{count}} message can be marked as unread':
113+
'Error, only the first {{count}} message can be marked as unread',
114+
'Unread messages': 'Unread messages',
115+
'{{count}} unread messages': '{{count}} unread messages',
116+
'{{count}} unread message': '{{count}} unread message',
110117
},
111118
};

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { By } from '@angular/platform-browser';
33
import { TranslateModule } from '@ngx-translate/core';
44
import { ChannelPreviewComponent } from '../channel-preview/channel-preview.component';
55
import { ChannelService } from '../channel.service';
6-
import { ChatClientService } from '../chat-client.service';
6+
import { ChatClientService, ClientEvent } from '../chat-client.service';
77
import {
88
generateMockChannels,
99
mockChannelService,
@@ -12,6 +12,7 @@ import {
1212
import { ThemeService } from '../theme.service';
1313
import { ChannelListToggleService } from './channel-list-toggle.service';
1414
import { ChannelListComponent } from './channel-list.component';
15+
import { Subject } from 'rxjs';
1516

1617
describe('ChannelListComponent', () => {
1718
let channelServiceMock: MockChannelService;
@@ -34,7 +35,10 @@ describe('ChannelListComponent', () => {
3435
{ provide: ChannelService, useValue: channelServiceMock },
3536
{
3637
provide: ChatClientService,
37-
useValue: { chatClient: { user: { id: 'userid' } } },
38+
useValue: {
39+
chatClient: { user: { id: 'userid' } },
40+
events$: new Subject<ClientEvent>(),
41+
},
3842
},
3943
ThemeService,
4044
],

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

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
22
import { By } from '@angular/platform-browser';
33
import { TranslateModule } from '@ngx-translate/core';
4-
import { UserResponse } from 'stream-chat';
4+
import { Event, UserResponse } from 'stream-chat';
55
import { AvatarPlaceholderComponent } from '../avatar-placeholder/avatar-placeholder.component';
66
import { AvatarComponent } from '../avatar/avatar.component';
77
import { ChannelService } from '../channel.service';
8-
import { ChatClientService } from '../chat-client.service';
8+
import { ChatClientService, ClientEvent } from '../chat-client.service';
99
import {
1010
generateMockChannels,
1111
mockChannelService,
1212
MockChannelService,
1313
mockMessage,
1414
} from '../mocks';
1515
import { ChannelPreviewComponent } from './channel-preview.component';
16-
import { Observable, of } from 'rxjs';
16+
import { Observable, Subject, of } from 'rxjs';
17+
import { DefaultStreamChatGenerics } from '../types';
1718

1819
describe('ChannelPreviewComponent', () => {
1920
let fixture: ComponentFixture<ChannelPreviewComponent>;
@@ -22,6 +23,7 @@ describe('ChannelPreviewComponent', () => {
2223
let channelServiceMock: MockChannelService;
2324
let chatClientServiceMock: {
2425
chatClient: { user: UserResponse };
26+
events$: Subject<ClientEvent>;
2527
user$: Observable<{ id: string }>;
2628
};
2729
let queryContainer: () => HTMLElement | null;
@@ -33,6 +35,7 @@ describe('ChannelPreviewComponent', () => {
3335
beforeEach(() => {
3436
channelServiceMock = mockChannelService();
3537
chatClientServiceMock = {
38+
events$: new Subject(),
3639
chatClient: { user: { id: 'currentUser' } },
3740
user$: of({ id: 'currentUser' }),
3841
};
@@ -164,6 +167,30 @@ describe('ChannelPreviewComponent', () => {
164167
expect(component.unreadCount).toBe(0);
165168
});
166169

170+
it('should set unread state based on `notification.mark_unread`', () => {
171+
const channels = generateMockChannels();
172+
const channel = channels[0];
173+
const countUnreadSpy = spyOn(channel, 'countUnread');
174+
countUnreadSpy.and.returnValue(1);
175+
component.channel = channel;
176+
channelServiceMock.activeChannel$.next(channel);
177+
component.channel = channel;
178+
component.ngOnInit();
179+
180+
expect(component.isUnread).toBe(false);
181+
expect(component.unreadCount).toBe(0);
182+
183+
chatClientServiceMock.events$.next({
184+
eventType: 'notification.mark_unread',
185+
event: {
186+
channel_id: channel.id,
187+
} as Event<DefaultStreamChatGenerics>,
188+
});
189+
190+
expect(component.isUnread).toBe(true);
191+
expect(component.unreadCount).toBe(1);
192+
});
193+
167194
it('should set channel as active', () => {
168195
const channel = generateMockChannels()[0];
169196
component.channel = channel;

projects/stream-chat-angular/src/lib/channel-preview/channel-preview.component.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Component, Input, NgZone, OnDestroy, OnInit } from '@angular/core';
22
import { Subscription } from 'rxjs';
3+
import { filter } from 'rxjs/operators';
34
import {
45
Channel,
56
Event,
@@ -27,6 +28,7 @@ export class ChannelPreviewComponent implements OnInit, OnDestroy {
2728
*/
2829
@Input() channel: Channel<DefaultStreamChatGenerics> | undefined;
2930
isActive = false;
31+
isUnreadMessageWasCalled = false;
3032
isUnread = false;
3133
unreadCount: number | undefined;
3234
latestMessage: string = 'streamChat.Nothing yet...';
@@ -73,10 +75,27 @@ export class ChannelPreviewComponent implements OnInit, OnDestroy {
7375
this.subscriptions.push(
7476
this.channel!.on('message.read', () =>
7577
this.ngZone.run(() => {
78+
this.isUnreadMessageWasCalled = false;
7679
this.updateUnreadState();
7780
})
7881
)
7982
);
83+
this.subscriptions.push(
84+
this.chatClientService.events$
85+
.pipe(
86+
filter(
87+
(e) =>
88+
e.eventType === 'notification.mark_unread' &&
89+
this.channel!.id === e.event?.channel_id
90+
)
91+
)
92+
.subscribe(() => {
93+
this.ngZone.run(() => {
94+
this.isUnreadMessageWasCalled = true;
95+
this.updateUnreadState();
96+
});
97+
})
98+
);
8099
}
81100

82101
ngOnDestroy(): void {
@@ -140,7 +159,10 @@ export class ChannelPreviewComponent implements OnInit, OnDestroy {
140159
}
141160

142161
private updateUnreadState() {
143-
if (this.isActive || !this.canSendReadEvents) {
162+
if (
163+
(this.isActive && !this.isUnreadMessageWasCalled) ||
164+
!this.canSendReadEvents
165+
) {
144166
this.unreadCount = 0;
145167
this.isUnread = false;
146168
return;

0 commit comments

Comments
 (0)