diff --git a/projects/stream-chat-angular/src/lib/message/message.component.spec.ts b/projects/stream-chat-angular/src/lib/message/message.component.spec.ts
index 891f35e0..28528047 100644
--- a/projects/stream-chat-angular/src/lib/message/message.component.spec.ts
+++ b/projects/stream-chat-angular/src/lib/message/message.component.spec.ts
@@ -14,7 +14,7 @@ import { AttachmentListComponent } from '../attachment-list/attachment-list.comp
import { MessageReactionsComponent } from '../message-reactions/message-reactions.component';
import { TranslateModule } from '@ngx-translate/core';
import { ChannelService } from '../channel.service';
-import { HighlightMentionsPipe } from './highlight-mentions.pipe';
+import { SimpleChange } from '@angular/core';
describe('MessageComponent', () => {
let component: MessageComponent;
@@ -58,7 +58,6 @@ describe('MessageComponent', () => {
MessageActionsBoxComponent,
AttachmentListComponent,
MessageReactionsComponent,
- HighlightMentionsPipe,
],
providers: [
{
@@ -104,6 +103,7 @@ describe('MessageComponent', () => {
nativeElement.querySelector('[data-testid="message-actions-container"]');
message = mockMessage();
component.message = message;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
fixture.detectChanges();
messageActionsBoxComponent = fixture.debugElement.query(
By.directive(MessageActionsBoxComponent)
@@ -627,9 +627,10 @@ describe('MessageComponent', () => {
const htmlContent =
'
https://getstream.io/';
component.message = { ...component.message!, ...{ html: htmlContent } };
+ component.ngOnChanges({ message: {} as any as SimpleChange });
fixture.detectChanges();
- expect(queryText()?.innerHTML).toEqual(htmlContent);
+ expect(queryText()?.innerHTML).toContain(htmlContent);
});
it('should resend message, if sending is failed', () => {
@@ -711,4 +712,117 @@ describe('MessageComponent', () => {
expect(component.isEditing).toBeTrue();
});
+
+ it('should create message parts', () => {
+ component.message = {
+ text: '',
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([]);
+
+ component.message = {
+ text: 'This is a message without user mentions',
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ { content: 'This is a message without user mentions', type: 'text' },
+ ]);
+
+ component.message = {
+ text: 'This is just an email, not a mention test@test.com',
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ {
+ content: 'This is just an email, not a mention test@test.com',
+ type: 'text',
+ },
+ ]);
+
+ component.message = {
+ html: '
This is just an email, not a mention test@test.com
\n',
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ {
+ content: 'This is just an email, not a mention test@test.com',
+ type: 'text',
+ },
+ ]);
+
+ component.message = {
+ text: 'Hello @Jack',
+ mentioned_users: [{ id: 'jack', name: 'Jack' }],
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ { content: 'Hello ', type: 'text' },
+ {
+ content: '@Jack',
+ type: 'mention',
+ user: { id: 'jack', name: 'Jack' },
+ },
+ ]);
+
+ component.message = {
+ text: 'Hello @Jack, how are you?',
+ mentioned_users: [{ id: 'jack', name: 'Jack' }],
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ { content: 'Hello ', type: 'text' },
+ {
+ content: '@Jack',
+ type: 'mention',
+ user: { id: 'jack', name: 'Jack' },
+ },
+ { content: ', how are you?', type: 'text' },
+ ]);
+
+ component.message = {
+ text: 'Hello @Jack and @Lucie, how are you?',
+ mentioned_users: [
+ { id: 'id2334', name: 'Jack' },
+ { id: 'id3444', name: 'Lucie' },
+ ],
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ { content: 'Hello ', type: 'text' },
+ {
+ content: '@Jack',
+ type: 'mention',
+ user: { id: 'id2334', name: 'Jack' },
+ },
+ { content: ' and ', type: 'text' },
+ {
+ content: '@Lucie',
+ type: 'mention',
+ user: { id: 'id3444', name: 'Lucie' },
+ },
+ { content: ', how are you?', type: 'text' },
+ ]);
+
+ component.message = {
+ html: `
https://getstream.io/ this is the link @sara
\n`,
+ mentioned_users: [{ id: 'sara' }],
+ } as StreamMessage;
+ component.ngOnChanges({ message: {} as any as SimpleChange });
+
+ expect(component.messageTextParts).toEqual([
+ {
+ content:
+ '
https://getstream.io/ this is the link ',
+ type: 'text',
+ },
+ { content: '@sara', type: 'mention', user: { id: 'sara' } },
+ ]);
+ });
});
diff --git a/projects/stream-chat-angular/src/lib/message/message.component.ts b/projects/stream-chat-angular/src/lib/message/message.component.ts
index 693b73ef..125c3554 100644
--- a/projects/stream-chat-angular/src/lib/message/message.component.ts
+++ b/projects/stream-chat-angular/src/lib/message/message.component.ts
@@ -3,6 +3,8 @@ import {
ElementRef,
Input,
TemplateRef,
+ OnChanges,
+ SimpleChanges,
ViewChild,
} from '@angular/core';
import { UserResponse } from 'stream-chat';
@@ -19,8 +21,9 @@ import { getReadByText } from './read-by-text';
templateUrl: './message.component.html',
styles: [],
})
-export class MessageComponent {
+export class MessageComponent implements OnChanges {
@Input() messageInputTemplate: TemplateRef
| undefined;
+ @Input() mentionTemplate: TemplateRef | undefined;
@Input() message: StreamMessage | undefined;
@Input() enabledMessageActions: MessageActions[] = [];
@Input() areReactionsEnabled: boolean | undefined;
@@ -32,6 +35,11 @@ export class MessageComponent {
isReactionSelectorOpen = false;
isPressedOnMobile = false;
visibleMessageActionsCount = 0;
+ messageTextParts: {
+ content: string;
+ type: 'text' | 'mention';
+ user?: UserResponse;
+ }[] = [];
private user: UserResponse | undefined;
@ViewChild('container') private container:
| ElementRef
@@ -44,6 +52,49 @@ export class MessageComponent {
this.user = this.chatClientService.chatClient.user;
}
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.message) {
+ let content = this.message?.html || this.message?.text;
+ if (!content) {
+ this.messageTextParts = [];
+ } else {
+ // Backend will wrap HTML content with \n
+ if (content.startsWith('')) {
+ content = content.replace('
', '');
+ }
+ if (content.endsWith('
\n')) {
+ content = content.replace('\n', '');
+ }
+ if (
+ !this.message!.mentioned_users ||
+ this.message!.mentioned_users.length === 0
+ ) {
+ this.messageTextParts = [{ content, type: 'text' }];
+ } else {
+ this.messageTextParts = [];
+ let text = content;
+ this.message!.mentioned_users.forEach((user) => {
+ const mention = `@${user.name || user.id}`;
+ const precedingText = text.substring(0, text.indexOf(mention));
+ this.messageTextParts.push({
+ content: precedingText,
+ type: 'text',
+ });
+ this.messageTextParts.push({
+ content: mention,
+ type: 'mention',
+ user,
+ });
+ text = text.replace(precedingText + mention, '');
+ });
+ if (text) {
+ this.messageTextParts.push({ content: text, type: 'text' });
+ }
+ }
+ }
+ }
+ }
+
get isSentByCurrentUser() {
return this.message?.user?.id === this.user?.id;
}
diff --git a/projects/stream-chat-angular/src/lib/stream-chat.module.ts b/projects/stream-chat-angular/src/lib/stream-chat.module.ts
index b9adf6e6..3813ba1f 100644
--- a/projects/stream-chat-angular/src/lib/stream-chat.module.ts
+++ b/projects/stream-chat-angular/src/lib/stream-chat.module.ts
@@ -18,7 +18,6 @@ import { TranslateModule } from '@ngx-translate/core';
import { AttachmentPreviewListComponent } from './attachment-preview-list/attachment-preview-list.component';
import { ModalComponent } from './modal/modal.component';
import { TextareaDirective } from './message-input/textarea.directive';
-import { HighlightMentionsPipe } from './message/highlight-mentions.pipe';
import { StreamAvatarModule } from './stream-avatar.module';
@NgModule({
@@ -40,7 +39,6 @@ import { StreamAvatarModule } from './stream-avatar.module';
AttachmentPreviewListComponent,
ModalComponent,
TextareaDirective,
- HighlightMentionsPipe,
],
imports: [CommonModule, TranslateModule, StreamAvatarModule],
exports: [
@@ -60,7 +58,7 @@ import { StreamAvatarModule } from './stream-avatar.module';
NotificationListComponent,
AttachmentPreviewListComponent,
ModalComponent,
- HighlightMentionsPipe,
+ StreamAvatarModule,
],
})
export class StreamChatModule {}
diff --git a/projects/stream-chat-angular/src/public-api.ts b/projects/stream-chat-angular/src/public-api.ts
index 4cc86c16..a67db211 100644
--- a/projects/stream-chat-angular/src/public-api.ts
+++ b/projects/stream-chat-angular/src/public-api.ts
@@ -19,7 +19,6 @@ export * from './lib/channel-list/channel-list-toggle.service';
export * from './lib/message/message.component';
export * from './lib/message/parse-date';
export * from './lib/message/read-by-text';
-export * from './lib/message/highlight-mentions.pipe';
export * from './lib/message-input/message-input.component';
export * from './lib/message-input/textarea/textarea.component';
export * from './lib/message-input/autocomplete-textarea/autocomplete-textarea.component';
@@ -42,6 +41,7 @@ export * from './lib/message-preview';
export * from './lib/notification.service';
export * from './lib/transliteration.service';
export * from './lib/stream-chat.module';
+export * from './lib/stream-avatar.module';
export * from './lib/stream-autocomplete-textarea.module';
export * from './lib/stream-textarea.module';
export * from './lib/injection-tokens';