diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html index 00ca6e61f587..4ac1318232fd 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.html @@ -84,7 +84,12 @@ @for (group of groupedPosts; track postsGroupTrackByFn($index, group)) {
@for (post of group.posts; track postsTrackByFn($index, post)) { -
+
+ @if (isFirstUnreadMarker(post, group)) { + + + + } (); canStartSaving = false; createdNewMessage = false; + unreadCount = 0; @Output() openThread = new EventEmitter(); @@ -140,6 +141,7 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD private subscribeToActiveConversation() { this.metisConversationService.activeConversation$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((conversation: ConversationDTO) => { + this.unreadCount = conversation?.unreadMessagesCount || 0; // This statement avoids a bug that reloads the messages when the conversation is already displayed if (conversation && this._activeConversation?.id === conversation.id) { return; @@ -149,6 +151,45 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD }); } + /** + * Determines whether a post is the "first unread message" in the conversation. + */ + isFirstUnreadMarker(post: Post, group: PostGroup): boolean { + let remainingUnread = this.unreadCount; + let firstUnreadFound = false; + + const groupIndex = this.groupedPosts.findIndex((g) => g === group); + if (groupIndex === -1) { + return false; + } + + for (let i = this.groupedPosts.length - 1; i >= 0; i--) { + const currentGroup = this.groupedPosts[i]; + const groupMessageCount = currentGroup.posts.length; + + if (i > groupIndex) { + remainingUnread -= groupMessageCount; + } else if (i === groupIndex) { + const postIndexInGroup = currentGroup.posts.indexOf(post); + + if (!firstUnreadFound && postIndexInGroup === groupMessageCount - remainingUnread) { + if (groupIndex === 0 && postIndexInGroup === 0) { + remainingUnread--; + return false; + } + firstUnreadFound = true; + return true; + } + + remainingUnread--; + } else { + return false; + } + } + + return false; + } + private subscribeToSearch() { this.search$ .pipe( diff --git a/src/main/webapp/app/shared/metis/metis.service.ts b/src/main/webapp/app/shared/metis/metis.service.ts index a4415a2d1cee..ae9e16acdb07 100644 --- a/src/main/webapp/app/shared/metis/metis.service.ts +++ b/src/main/webapp/app/shared/metis/metis.service.ts @@ -255,6 +255,7 @@ export class MetisService implements OnDestroy { const indexToUpdate = this.cachedPosts.findIndex((cachedPost) => cachedPost.id === updatedPost.id); if (indexToUpdate > -1) { updatedPost.answers = [...(this.cachedPosts[indexToUpdate].answers ?? [])]; + updatedPost.authorRole = this.cachedPosts[indexToUpdate].authorRole; this.cachedPosts[indexToUpdate] = updatedPost; this.posts$.next(this.cachedPosts); this.totalNumberOfPosts$.next(this.cachedTotalNumberOfPosts); diff --git a/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html b/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html index deadcb377cdd..3294f887382b 100644 --- a/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html +++ b/src/main/webapp/app/shared/metis/posting-header/post-header/post-header.component.html @@ -1,4 +1,4 @@ -
+