Skip to content

Commit f2aa7c1

Browse files
authored
Merge pull request #499 from GetStream/empty-channel-placeholder
Empty channel placeholder
2 parents 230c3d2 + 6b9a2ee commit f2aa7c1

File tree

7 files changed

+94
-3
lines changed

7 files changed

+94
-3
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,11 @@
282282
<ng-template #dateSeparator let-date="date" let-parsedDate="parsedDate">
283283
{{ date }} - {{ parsedDate }}
284284
</ng-template>
285+
286+
<ng-template #emptyMainMessageList>
287+
<div class="empty-list"><div>No messages yet</div></div>
288+
</ng-template>
289+
290+
<ng-template #emptyThreadMessageList>
291+
<div class="empty-list"><div>No thread replies yet</div></div>
292+
</ng-template>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.empty-list {
2+
display: flex;
3+
width: 100%;
4+
height: 100%;
5+
align-items: center;
6+
justify-content: center;
7+
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ export class AppComponent implements AfterViewInit {
9191
private customAttachmentUploadTemplate!: TemplateRef<CustomAttachmentUploadContext>;
9292
@ViewChild('dateSeparator')
9393
private dateSeparatorTemplate!: TemplateRef<DateSeparatorContext>;
94+
@ViewChild('emptyMainMessageList')
95+
private emptyMainMessageListTemplate!: TemplateRef<void>;
96+
@ViewChild('emptyThreadMessageList')
97+
private emptyThreadMessageListTemplate!: TemplateRef<void>;
9498

9599
constructor(
96100
private chatService: ChatClientService,
@@ -170,6 +174,12 @@ export class AppComponent implements AfterViewInit {
170174
this.customTemplatesService.dateSeparatorTemplate$.next(
171175
this.dateSeparatorTemplate
172176
);
177+
this.customTemplatesService.emptyMainMessageListPlaceholder$.next(
178+
this.emptyMainMessageListTemplate
179+
);
180+
this.customTemplatesService.emptyThreadMessageListPlaceholder$.next(
181+
this.emptyThreadMessageListTemplate
182+
);
173183
}
174184

175185
inviteClicked(channel: Channel) {

projects/customizations-example/src/styles.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ body {
2727
.material-icons {
2828
font-family: "Material Icons" !important;
2929
}
30+
31+
.str-chat__thread
32+
.str-chat__main-panel-inner.str-chat-angular__message-list-host--empty {
33+
height: 100%;
34+
}

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)