Skip to content

Commit 418841d

Browse files
committed
feat: optimized message list
1 parent 22566f8 commit 418841d

19 files changed

Lines changed: 1719 additions & 457 deletions

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
@@ -117,7 +117,7 @@
117117
"@ctrl/ngx-emoji-mart": "^8.2.0",
118118
"@floating-ui/dom": "^1.6.3",
119119
"@ngx-translate/core": "^14.0.0",
120-
"@stream-io/stream-chat-css": "4.17.3",
120+
"@stream-io/stream-chat-css": "4.17.4",
121121
"@stream-io/transliterate": "^1.5.2",
122122
"angular-mentions": "1.4.0",
123123
"dayjs": "^1.11.10",

projects/stream-chat-angular/.eslintrc.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,29 @@
3434
"paths": ["stream-chat-angular"],
3535
"patterns": ["dist/*", "public-api"]
3636
}
37+
],
38+
"@typescript-eslint/no-unused-vars": [
39+
"error",
40+
{
41+
"args": "all",
42+
"argsIgnorePattern": "^_",
43+
"caughtErrors": "all",
44+
"caughtErrorsIgnorePattern": "^error",
45+
"destructuredArrayIgnorePattern": "^_",
46+
"varsIgnorePattern": "^_",
47+
"ignoreRestSiblings": true
48+
}
3749
]
3850
}
3951
},
52+
{
53+
"files": ["*.spec.ts"],
54+
"rules": {
55+
"@typescript-eslint/no-unsafe-call": "off",
56+
"@typescript-eslint/no-unsafe-assignment": "off",
57+
"@typescript-eslint/no-unsafe-member-access": "off"
58+
}
59+
},
4060
{
4161
"files": ["*.service.ts"],
4262
"parserOptions": {

projects/stream-chat-angular/src/lib/avatar/avatar.component.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Input,
66
NgZone,
77
OnChanges,
8+
OnDestroy,
89
OnInit,
910
SimpleChanges,
1011
} from '@angular/core';
@@ -27,7 +28,7 @@ import {
2728
styleUrls: ['./avatar.component.scss'],
2829
})
2930
export class AvatarComponent
30-
implements OnChanges, OnInit, OnChanges, AfterViewInit
31+
implements OnChanges, OnInit, OnChanges, AfterViewInit, OnDestroy
3132
{
3233
/**
3334
* An optional name of the image, used for fallback image or image title (if `imageUrl` is provided)
@@ -109,6 +110,10 @@ export class AvatarComponent
109110
}
110111
}
111112

113+
ngOnDestroy(): void {
114+
this.subscriptions.forEach((s) => s.unsubscribe());
115+
}
116+
112117
private setFallbackChannelImage() {
113118
if (this.type !== 'channel') {
114119
this.fallbackChannelImage = undefined;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class ChannelListComponent implements OnDestroy {
6363
this.isLoadingMoreChannels = false;
6464
}
6565

66-
trackByChannelId(index: number, item: Channel<DefaultStreamChatGenerics>) {
66+
trackByChannelId(_: number, item: Channel<DefaultStreamChatGenerics>) {
6767
return item.cid;
6868
}
6969
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,18 @@ describe('ChannelService', () => {
671671
(activeChannel as MockChannel).handleEvent('message.deleted', { message });
672672

673673
expect(spy).toHaveBeenCalledWith(jasmine.arrayContaining([message]));
674+
675+
spy.calls.reset();
676+
activeChannel.state.messages.splice(
677+
activeChannel.state.messages.findIndex((m) => m.id === message.id)
678+
);
679+
(activeChannel as MockChannel).handleEvent('message.deleted', {
680+
message,
681+
type: 'message.deleted',
682+
});
683+
684+
expect(spy).toHaveBeenCalled();
685+
expect(spy).not.toHaveBeenCalledWith(jasmine.arrayContaining([message]));
674686
});
675687

676688
it('should move channel to the top of the list', async () => {

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

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,11 @@ export class ChannelService<
311311
beforeUpdateMessage?: (
312312
message: StreamMessage<T>
313313
) => StreamMessage<T> | Promise<StreamMessage<T>>;
314-
/**
315-
* @internal
316-
*/
317-
static readonly MAX_MESSAGE_COUNT_IN_MESSAGE_LIST = 250;
318314
/**
319315
* @internal
320316
*/
321317
static readonly MAX_MESSAGE_REACTIONS_TO_FETCH = 1200;
318+
messagePageSize = 25;
322319
private channelsSubject = new BehaviorSubject<Channel<T>[] | undefined>(
323320
undefined
324321
);
@@ -347,7 +344,6 @@ export class ChannelService<
347344
private latestMessageDateByUserByChannelsSubject = new BehaviorSubject<{
348345
[key: string]: Date;
349346
}>({});
350-
private messagePageSize = 25;
351347
private readonly attachmentMaxSizeFallbackInMB = 100;
352348
private messageToQuoteSubject = new BehaviorSubject<
353349
StreamMessage<T> | undefined
@@ -518,28 +514,6 @@ export class ChannelService<
518514
.pipe(shareReplay(1));
519515
}
520516

521-
/**
522-
* internal
523-
*/
524-
removeOldMessageFromMessageList() {
525-
const channel = this.activeChannelSubject.getValue();
526-
const channelMessages = channel?.state.latestMessages;
527-
const targetLength = Math.ceil(
528-
ChannelService.MAX_MESSAGE_COUNT_IN_MESSAGE_LIST / 2
529-
);
530-
if (
531-
!channel ||
532-
!channelMessages ||
533-
channelMessages !== channel?.state.latestMessages ||
534-
channelMessages.length <= targetLength
535-
) {
536-
return;
537-
}
538-
const messages = channelMessages;
539-
messages.splice(0, messages.length - targetLength);
540-
this.activeChannelMessagesSubject.next(messages);
541-
}
542-
543517
/**
544518
* If set to false, read events won't be sent as new messages are received. If set to true active channel (if any) will immediately be marked as read.
545519
*/
@@ -1646,6 +1620,13 @@ export class ChannelService<
16461620
return this.activeChannelMessagesSubject.getValue() || [];
16471621
}
16481622

1623+
/**
1624+
* The current thread replies
1625+
*/
1626+
get activeChannelThreadReplies() {
1627+
return this.activeThreadMessagesSubject.getValue() || [];
1628+
}
1629+
16491630
/**
16501631
* Get the last 1200 reactions of a message in the current active channel. If you need to fetch more reactions please use the [following endpoint](https://getstream.io/chat/docs/javascript/send_reaction/?language=javascript#paginating-reactions).
16511632
* @param messageId
@@ -1750,7 +1731,7 @@ export class ChannelService<
17501731
const messageIndex = messages.findIndex(
17511732
(m) => m.id === event?.message?.id
17521733
);
1753-
if (messageIndex !== -1) {
1734+
if (messageIndex !== -1 || event.type === 'message.deleted') {
17541735
isThreadReply
17551736
? this.activeThreadMessagesSubject.next([...messages])
17561737
: this.activeChannelMessagesSubject.next([...messages]);

projects/stream-chat-angular/src/lib/message-actions.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class MessageActionsService<
4343
},
4444
isVisible: (
4545
enabledActions: string[],
46-
isMine: boolean,
46+
_: boolean,
4747
message: StreamMessage<T>
4848
) => enabledActions.indexOf('read-events') !== -1 && !message.parent_id,
4949
},
@@ -64,7 +64,7 @@ export class MessageActionsService<
6464
},
6565
isVisible: (
6666
enabledActions: string[],
67-
isMine: boolean,
67+
_: boolean,
6868
message: StreamMessage<T>
6969
) => enabledActions.indexOf('send-reply') !== -1 && !message.parent_id,
7070
},
@@ -91,7 +91,7 @@ export class MessageActionsService<
9191
'streamChat.Message has been successfully flagged',
9292
'success'
9393
);
94-
} catch (err) {
94+
} catch (error) {
9595
this.notificationService.addTemporaryNotification(
9696
'streamChat.Error adding flag'
9797
);

projects/stream-chat-angular/src/lib/message-list/message-list.component.html

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,12 @@
5757
</button>
5858
</div>
5959
</ng-template>
60-
<div #scrollContainer data-testid="scroll-container" class="str-chat__list">
60+
<div
61+
#scrollContainer
62+
data-testid="scroll-container"
63+
class="str-chat__list"
64+
style="overscroll-behavior-y: none"
65+
>
6166
<ng-container *ngIf="mode === 'main' && isEmpty && emptyListTemplate">
6267
<ng-container *ngTemplateOutlet="emptyListTemplate"></ng-container>
6368
</ng-container>
@@ -83,12 +88,16 @@
8388
<ng-container *ngIf="mode === 'thread' && isEmpty && emptyListTemplate">
8489
<ng-container *ngTemplateOutlet="emptyListTemplate"></ng-container>
8590
</ng-container>
86-
<stream-loading-indicator
91+
<stream-loading-indicator-placeholder
8792
*ngIf="
88-
isLoading && direction === 'bottom-to-top' && displayLoadingIndicator
93+
((loadingState === 'loading-top' && direction === 'bottom-to-top') ||
94+
(loadingState === 'loading-bottom' &&
95+
direction === 'top-to-bottom')) &&
96+
displayLoadingIndicator;
97+
else loadingIndicatorPlaceholder
8998
"
9099
data-testid="top-loading-indicator"
91-
></stream-loading-indicator>
100+
></stream-loading-indicator-placeholder>
92101
<ng-container *ngIf="messages$ | async as messages">
93102
<ng-container
94103
*ngFor="
@@ -159,12 +168,20 @@
159168
</ng-container>
160169
</ng-container>
161170
</ng-container>
162-
<stream-loading-indicator
171+
<stream-loading-indicator-placeholder
163172
*ngIf="
164-
isLoading && direction === 'top-to-bottom' && displayLoadingIndicator
173+
((loadingState === 'loading-bottom' &&
174+
direction === 'bottom-to-top') ||
175+
(loadingState === 'loading-top' &&
176+
direction === 'top-to-bottom')) &&
177+
displayLoadingIndicator;
178+
else loadingIndicatorPlaceholder
165179
"
166180
data-testid="bottom-loading-indicator"
167-
></stream-loading-indicator>
181+
></stream-loading-indicator-placeholder>
182+
<ng-template #loadingIndicatorPlaceholder>
183+
<div class="str-chat__loading-indicator-placeholder"></div>
184+
</ng-template>
168185
</ul>
169186
<ng-template #defaultTypingIndicator let-usersTyping$="usersTyping$">
170187
<!-- eslint-disable-next-line @angular-eslint/template/no-any -->

0 commit comments

Comments
 (0)