Skip to content

Commit a2ba847

Browse files
committed
feat: System message display #109
1 parent 36ccfbb commit a2ba847

File tree

2 files changed

+150
-122
lines changed

2 files changed

+150
-122
lines changed

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

Lines changed: 137 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -15,140 +15,142 @@
1515
data-testid="message-container"
1616
>
1717
<ng-container *ngIf="!message?.deleted_at; else deletedMessage">
18-
<ng-container
19-
*ngIf="
20-
isSentByCurrentUser &&
21-
((isLastSentMessage && message?.status === 'received') ||
22-
message?.status === 'sending')
23-
"
24-
>
25-
<ng-container *ngIf="message?.status === 'sending'; else sentStatus">
26-
<ng-container *ngTemplateOutlet="sendingStatus"></ng-container>
27-
</ng-container>
28-
<ng-template #sentStatus>
29-
<ng-container *ngIf="isMessageDeliveredAndRead; else deliveredStatus">
30-
<ng-container *ngTemplateOutlet="readStatus"></ng-container>
31-
</ng-container>
32-
</ng-template>
33-
</ng-container>
34-
<stream-avatar
35-
data-testid="avatar"
36-
class="str-chat-angular__avatar-host"
37-
[imageUrl]="message?.user?.image"
38-
[name]="message?.user?.name || message?.user?.id"
39-
></stream-avatar>
40-
<div class="str-chat__message-inner">
41-
<div
42-
class="str-chat__message-simple__actions"
43-
data-testid="message-options"
44-
*ngIf="areOptionsVisible"
18+
<ng-container *ngIf="message?.type !== 'system'; else systemMessage">
19+
<ng-container
20+
*ngIf="
21+
isSentByCurrentUser &&
22+
((isLastSentMessage && message?.status === 'received') ||
23+
message?.status === 'sending')
24+
"
4525
>
26+
<ng-container *ngIf="message?.status === 'sending'; else sentStatus">
27+
<ng-container *ngTemplateOutlet="sendingStatus"></ng-container>
28+
</ng-container>
29+
<ng-template #sentStatus>
30+
<ng-container *ngIf="isMessageDeliveredAndRead; else deliveredStatus">
31+
<ng-container *ngTemplateOutlet="readStatus"></ng-container>
32+
</ng-container>
33+
</ng-template>
34+
</ng-container>
35+
<stream-avatar
36+
data-testid="avatar"
37+
class="str-chat-angular__avatar-host"
38+
[imageUrl]="message?.user?.image"
39+
[name]="message?.user?.name || message?.user?.id"
40+
></stream-avatar>
41+
<div class="str-chat__message-inner">
4642
<div
47-
class="
48-
str-chat__message-simple__actions__action
49-
str-chat__message-simple__actions__action--options
50-
"
51-
>
52-
<stream-message-actions-box
53-
[isOpen]="isActionBoxOpen"
54-
[isMine]="isSentByCurrentUser"
55-
[enabledActions]="enabledMessageActions"
56-
[message]="message"
57-
(displayedActionsCount)="visibleMessageActionsCount = $event"
58-
></stream-message-actions-box>
59-
<stream-icon
60-
*ngIf="visibleMessageActionsCount > 0"
61-
data-testid="action-icon"
62-
icon="action-icon"
63-
(keyup.enter)="isActionBoxOpen = !isActionBoxOpen"
64-
(click)="isActionBoxOpen = !isActionBoxOpen"
65-
></stream-icon>
66-
</div>
67-
<div
68-
*ngIf="areReactionsEnabled"
69-
class="
70-
str-chat__message-simple__actions__action
71-
str-chat__message-simple__actions__action--reactions
72-
"
73-
data-testid="reaction-icon"
74-
(click)="isReactionSelectorOpen = !isReactionSelectorOpen"
75-
(keyup.enter)="isReactionSelectorOpen = !isReactionSelectorOpen"
76-
>
77-
<stream-icon icon="reaction-icon"></stream-icon>
78-
</div>
79-
</div>
80-
<stream-message-reactions
81-
*ngIf="areReactionsEnabled"
82-
[messageReactionCounts]="message?.reaction_counts || {}"
83-
[latestReactions]="message?.latest_reactions || []"
84-
[(isSelectorOpen)]="isReactionSelectorOpen"
85-
[messageId]="message?.id"
86-
[ownReactions]="message?.own_reactions || []"
87-
></stream-message-reactions>
88-
<stream-attachment-list
89-
*ngIf="hasAttachment"
90-
[attachments]="message!.attachments!"
91-
></stream-attachment-list>
92-
<div class="str-chat__message-text" *ngIf="message?.text">
93-
<div
94-
data-testid="inner-message"
95-
class="
96-
str-chat__message-text-inner str-chat__message-simple-text-inner
97-
"
98-
[class.str-chat__message-light-text-inner--has-attachment]="
99-
hasAttachment
100-
"
101-
(click)="
102-
message?.status === 'failed' && message?.errorStatusCode !== 403
103-
? resendMessage()
104-
: undefined
105-
"
106-
(keyup.enter)="
107-
message?.status === 'failed' && message?.errorStatusCode !== 403
108-
? resendMessage()
109-
: undefined
110-
"
43+
class="str-chat__message-simple__actions"
44+
data-testid="message-options"
45+
*ngIf="areOptionsVisible"
11146
>
11247
<div
113-
data-testid="client-error-message"
114-
*ngIf="message?.type === 'error'"
115-
class="str-chat__simple-message--error-message"
48+
class="
49+
str-chat__message-simple__actions__action
50+
str-chat__message-simple__actions__action--options
51+
"
11652
>
117-
{{ "streamChat.Error · Unsent" | translate }}
53+
<stream-message-actions-box
54+
[isOpen]="isActionBoxOpen"
55+
[isMine]="isSentByCurrentUser"
56+
[enabledActions]="enabledMessageActions"
57+
[message]="message"
58+
(displayedActionsCount)="visibleMessageActionsCount = $event"
59+
></stream-message-actions-box>
60+
<stream-icon
61+
*ngIf="visibleMessageActionsCount > 0"
62+
data-testid="action-icon"
63+
icon="action-icon"
64+
(keyup.enter)="isActionBoxOpen = !isActionBoxOpen"
65+
(click)="isActionBoxOpen = !isActionBoxOpen"
66+
></stream-icon>
11867
</div>
11968
<div
120-
data-testid="error-message"
121-
*ngIf="message?.status === 'failed'"
122-
class="str-chat__simple-message--error-message"
69+
*ngIf="areReactionsEnabled"
70+
class="
71+
str-chat__message-simple__actions__action
72+
str-chat__message-simple__actions__action--reactions
73+
"
74+
data-testid="reaction-icon"
75+
(click)="isReactionSelectorOpen = !isReactionSelectorOpen"
76+
(keyup.enter)="isReactionSelectorOpen = !isReactionSelectorOpen"
12377
>
124-
{{
125-
(message?.errorStatusCode === 403
126-
? "streamChat.Message Failed · Unauthorized"
127-
: "streamChat.Message Failed · Click to try again"
128-
) | translate
129-
}}
78+
<stream-icon icon="reaction-icon"></stream-icon>
13079
</div>
80+
</div>
81+
<stream-message-reactions
82+
*ngIf="areReactionsEnabled"
83+
[messageReactionCounts]="message?.reaction_counts || {}"
84+
[latestReactions]="message?.latest_reactions || []"
85+
[(isSelectorOpen)]="isReactionSelectorOpen"
86+
[messageId]="message?.id"
87+
[ownReactions]="message?.own_reactions || []"
88+
></stream-message-reactions>
89+
<stream-attachment-list
90+
*ngIf="hasAttachment"
91+
[attachments]="message!.attachments!"
92+
></stream-attachment-list>
93+
<div class="str-chat__message-text" *ngIf="message?.text">
13194
<div
132-
(click)="textClicked()"
133-
(keyup.enter)="textClicked()"
134-
data-testid="text"
135-
[innerHTML]="message?.html || message?.text"
136-
></div>
95+
data-testid="inner-message"
96+
class="
97+
str-chat__message-text-inner str-chat__message-simple-text-inner
98+
"
99+
[class.str-chat__message-light-text-inner--has-attachment]="
100+
hasAttachment
101+
"
102+
(click)="
103+
message?.status === 'failed' && message?.errorStatusCode !== 403
104+
? resendMessage()
105+
: undefined
106+
"
107+
(keyup.enter)="
108+
message?.status === 'failed' && message?.errorStatusCode !== 403
109+
? resendMessage()
110+
: undefined
111+
"
112+
>
113+
<div
114+
data-testid="client-error-message"
115+
*ngIf="message?.type === 'error'"
116+
class="str-chat__simple-message--error-message"
117+
>
118+
{{ "streamChat.Error · Unsent" | translate }}
119+
</div>
120+
<div
121+
data-testid="error-message"
122+
*ngIf="message?.status === 'failed'"
123+
class="str-chat__simple-message--error-message"
124+
>
125+
{{
126+
(message?.errorStatusCode === 403
127+
? "streamChat.Message Failed · Unauthorized"
128+
: "streamChat.Message Failed · Click to try again"
129+
) | translate
130+
}}
131+
</div>
132+
<div
133+
(click)="textClicked()"
134+
(keyup.enter)="textClicked()"
135+
data-testid="text"
136+
[innerHTML]="message?.html || message?.text"
137+
></div>
138+
</div>
139+
</div>
140+
<div class="str-chat__message-data str-chat__message-simple-data">
141+
<span
142+
data-testid="sender"
143+
*ngIf="!isSentByCurrentUser"
144+
class="str-chat__message-simple-name"
145+
>
146+
{{ message?.user?.name ? message?.user?.name : message?.user?.id }}
147+
</span>
148+
<span data-testid="date" class="str-chat__message-simple-timestamp">
149+
{{ parsedDate }}
150+
</span>
137151
</div>
138152
</div>
139-
<div class="str-chat__message-data str-chat__message-simple-data">
140-
<span
141-
data-testid="sender"
142-
*ngIf="!isSentByCurrentUser"
143-
class="str-chat__message-simple-name"
144-
>
145-
{{ message?.user?.name ? message?.user?.name : message?.user?.id }}
146-
</span>
147-
<span data-testid="date" class="str-chat__message-simple-timestamp">
148-
{{ parsedDate }}
149-
</span>
150-
</div>
151-
</div>
153+
</ng-container>
152154
</ng-container>
153155
</div>
154156

@@ -205,3 +207,16 @@
205207
</div>
206208
</div>
207209
</ng-template>
210+
211+
<ng-template #systemMessage>
212+
<div data-testid="system-message" class="str-chat__message--system">
213+
<div class="str-chat__message--system__text">
214+
<div class="str-chat__message--system__line"></div>
215+
<p>{{ message?.text }}</p>
216+
<div class="str-chat__message--system__line"></div>
217+
</div>
218+
<div class="str-chat__message--system__date">
219+
{{ parsedDate }}
220+
</div>
221+
</div>
222+
</ng-template>

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe('MessageComponent', () => {
4040
let queryMessageInner: () => HTMLElement | null;
4141
let queryLoadingIndicator: () => HTMLElement | null;
4242
let queryDeletedMessageContainer: () => HTMLElement | null;
43+
let querySystemMessageContainer: () => HTMLElement | null;
4344
let resendMessageSpy: jasmine.Spy;
4445

4546
beforeEach(() => {
@@ -110,6 +111,8 @@ describe('MessageComponent', () => {
110111
?.componentInstance as MessageReactionsComponent;
111112
queryDeletedMessageContainer = () =>
112113
nativeElement.querySelector('[data-testid="message-deleted-component"]');
114+
querySystemMessageContainer = () =>
115+
nativeElement.querySelector('[data-testid="system-message"]');
113116
});
114117

115118
it('should apply the correct CSS classes based on #message', () => {
@@ -649,4 +652,14 @@ describe('MessageComponent', () => {
649652
expect(queryAvatar()).toBeNull();
650653
expect(queryMessageOptions()).toBeNull();
651654
});
655+
656+
it('should display system message', () => {
657+
expect(querySystemMessageContainer()).toBeNull();
658+
659+
component.message = { ...message, type: 'system' };
660+
fixture.detectChanges();
661+
const systemMessage = querySystemMessageContainer();
662+
663+
expect(systemMessage?.innerHTML).toContain(message.text);
664+
});
652665
});

0 commit comments

Comments
 (0)