Skip to content

Commit 81b88de

Browse files
committed
feat: empty message list placeholders
1 parent 3e6e2c1 commit 81b88de

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

projects/stream-chat-angular/src/lib/custom-templates.service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,18 @@ export class CustomTemplatesService {
292292
newMessagesIndicatorTemplate$ = new BehaviorSubject<
293293
TemplateRef<void> | undefined
294294
>(undefined);
295+
/**
296+
* The template to show if the main message list is empty
297+
*/
298+
emptyMainMessageListPlaceholder$ = new BehaviorSubject<
299+
TemplateRef<void> | undefined
300+
>(undefined);
301+
/**
302+
* The template to show if the thread message list is empty
303+
*/
304+
emptyThreadMessageListPlaceholder$ = new BehaviorSubject<
305+
TemplateRef<void> | undefined
306+
>(undefined);
295307

296308
constructor() {}
297309
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
class="str-chat__list"
55
style="overscroll-behavior: none"
66
>
7+
<ng-container *ngIf="mode === 'main' && isEmpty && emptyListTemplate">
8+
<ng-container *ngTemplateOutlet="emptyListTemplate"></ng-container>
9+
</ng-container>
710
<div class="str-chat__reverse-infinite-scroll str-chat__message-list-scroll">
811
<ul
912
class="str-chat__ul"
@@ -28,6 +31,9 @@
2831
replies' | translate:replyCountParam)}}
2932
</div>
3033
</li>
34+
<ng-container *ngIf="mode === 'thread' && isEmpty && emptyListTemplate">
35+
<ng-container *ngTemplateOutlet="emptyListTemplate"></ng-container>
36+
</ng-container>
3137
<stream-loading-indicator
3238
data-testid="top-loading-indicator"
3339
*ngIf="

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

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
AfterViewChecked,
33
AfterViewInit,
4+
ChangeDetectorRef,
45
Component,
56
ElementRef,
67
HostBinding,
@@ -87,10 +88,11 @@ export class MessageListComponent
8788
messageTemplate: TemplateRef<MessageContext> | undefined;
8889
customDateSeparatorTemplate: TemplateRef<DateSeparatorContext> | undefined;
8990
customnewMessagesIndicatorTemplate: TemplateRef<void> | undefined;
91+
emptyMainMessageListTemplate: TemplateRef<void> | null = null;
92+
emptyThreadMessageListTemplate: TemplateRef<void> | null = null;
9093
messages$!: Observable<StreamMessage[]>;
9194
enabledMessageActions: string[] = [];
92-
@HostBinding('class') private class =
93-
'str-chat-angular__main-panel-inner str-chat-angular__message-list-host str-chat__main-panel-inner';
95+
isEmpty = true;
9496
unreadMessageCount = 0;
9597
isUserScrolled: boolean | undefined;
9698
groupStyles: GroupStyle[] = [];
@@ -125,12 +127,20 @@ export class MessageListComponent
125127
private channelId?: string;
126128
private parsedDates = new Map<Date, string>();
127129

130+
@HostBinding('class')
131+
private get class() {
132+
return `str-chat-angular__main-panel-inner str-chat-angular__message-list-host str-chat__main-panel-inner ${
133+
this.isEmpty ? 'str-chat-angular__message-list-host--empty' : ''
134+
}`;
135+
}
136+
128137
constructor(
129138
private channelService: ChannelService,
130139
private chatClientService: ChatClientService,
131140
private customTemplatesService: CustomTemplatesService,
132141
private dateParser: DateParserService,
133-
private ngZone: NgZone
142+
private ngZone: NgZone,
143+
private cdRef: ChangeDetectorRef
134144
) {
135145
this.subscriptions.push(
136146
this.channelService.activeChannel$.subscribe((channel) => {
@@ -269,6 +279,28 @@ export class MessageListComponent
269279
}
270280
})
271281
);
282+
this.subscriptions.push(
283+
this.customTemplatesService.emptyMainMessageListPlaceholder$.subscribe(
284+
(template) => {
285+
const isChanged = this.emptyMainMessageListTemplate !== template;
286+
this.emptyMainMessageListTemplate = template || null;
287+
if (isChanged) {
288+
this.cdRef.detectChanges();
289+
}
290+
}
291+
)
292+
);
293+
this.subscriptions.push(
294+
this.customTemplatesService.emptyThreadMessageListPlaceholder$.subscribe(
295+
(template) => {
296+
const isChanged = this.emptyThreadMessageListTemplate !== template;
297+
this.emptyThreadMessageListTemplate = template || null;
298+
if (isChanged) {
299+
this.cdRef.detectChanges();
300+
}
301+
}
302+
)
303+
);
272304
}
273305

274306
ngAfterViewChecked() {
@@ -451,6 +483,12 @@ export class MessageListComponent
451483
return { replyCount: this.parentMessage?.reply_count };
452484
}
453485

486+
get emptyListTemplate() {
487+
return this.mode === 'main'
488+
? this.emptyMainMessageListTemplate
489+
: this.emptyThreadMessageListTemplate;
490+
}
491+
454492
private preserveScrollbarPosition() {
455493
this.scrollContainer.nativeElement.scrollTop =
456494
(this.prevScrollTop || 0) +
@@ -508,6 +546,10 @@ export class MessageListComponent
508546
this.resetScrollState();
509547
return;
510548
}
549+
if (this.isEmpty) {
550+
// cdRef.detectChanges() isn't enough here, test will fail
551+
setTimeout(() => (this.isEmpty = false), 0);
552+
}
511553
this.chatClientService.chatClient?.logger?.(
512554
'info',
513555
`Received one or more messages`,
@@ -574,6 +616,7 @@ export class MessageListComponent
574616
}
575617

576618
private resetScrollState() {
619+
this.isEmpty = true;
577620
this.latestMessage = undefined;
578621
this.hasNewMessages = true;
579622
this.isUserScrolled = false;

0 commit comments

Comments
 (0)