From 292c9c4bbaa018d65238fc12dab2d543d6151484 Mon Sep 17 00:00:00 2001 From: Zita Szupera Date: Fri, 3 Jan 2025 16:10:49 +0100 Subject: [PATCH] fix: don't jump to last read message when there is a preselected message to jump to when opening the channel --- .../src/lib/channel.service.spec.ts | 10 ++++++++- .../src/lib/channel.service.ts | 9 ++++++++ .../message-list.component.spec.ts | 5 +++-- .../message-list/message-list.component.ts | 21 +++++++++++++------ .../src/lib/mocks/index.ts | 4 +++- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/projects/stream-chat-angular/src/lib/channel.service.spec.ts b/projects/stream-chat-angular/src/lib/channel.service.spec.ts index 88063b30..b3cd3f7a 100644 --- a/projects/stream-chat-angular/src/lib/channel.service.spec.ts +++ b/projects/stream-chat-angular/src/lib/channel.service.spec.ts @@ -357,6 +357,7 @@ describe('ChannelService', () => { service.usersTypingInChannel$.subscribe(typingUsersSpy); const typingUsersInThreadSpy = jasmine.createSpy(); service.usersTypingInThread$.subscribe(typingUsersInThreadSpy); + service.isMessageLoadingInProgress = true; messagesSpy.calls.reset(); activeChannelSpy.calls.reset(); messageToQuoteSpy.calls.reset(); @@ -386,6 +387,7 @@ describe('ChannelService', () => { (activeChannel as MockChannel).handleEvent('message.new', mockMessage()); expect(messagesSpy).not.toHaveBeenCalled(); + expect(service.isMessageLoadingInProgress).toBeFalse(); }); it('should tell if user #hasMoreChannels$', async () => { @@ -2177,7 +2179,11 @@ describe('ChannelService', () => { service.activeChannelMessages$.subscribe(messagesSpy); messagesSpy.calls.reset(); const messageId = '1232121123'; - await service.jumpToMessage(messageId); + const response = service.jumpToMessage(messageId); + + expect(service.isMessageLoadingInProgress).toBeTrue(); + + await response; expect(jumpToMessageIdSpy).toHaveBeenCalledWith({ id: messageId, @@ -2187,6 +2193,7 @@ describe('ChannelService', () => { expect(messagesSpy).toHaveBeenCalledWith( jasmine.arrayContaining([jasmine.objectContaining({ id: messageId })]) ); + expect(service.isMessageLoadingInProgress).toBeFalse(); }); it(`should display error notification if message couldn't be loaded`, async () => { @@ -2211,6 +2218,7 @@ describe('ChannelService', () => { expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith( 'streamChat.Message not found' ); + expect(service.isMessageLoadingInProgress).toBeFalse(); }); it('should pin message', async () => { diff --git a/projects/stream-chat-angular/src/lib/channel.service.ts b/projects/stream-chat-angular/src/lib/channel.service.ts index 723e8664..50631233 100644 --- a/projects/stream-chat-angular/src/lib/channel.service.ts +++ b/projects/stream-chat-angular/src/lib/channel.service.ts @@ -315,6 +315,10 @@ export class ChannelService< * @internal */ static readonly MAX_MESSAGE_REACTIONS_TO_FETCH = 1200; + /** + * @internal + */ + isMessageLoadingInProgress = false; messagePageSize = 25; private channelsSubject = new BehaviorSubject[] | undefined>( undefined @@ -568,6 +572,7 @@ export class ChannelService< this.stopWatchForActiveChannelEvents(prevActiveChannel); this.flushMarkReadQueue(); this.areReadEventsPaused = false; + this.isMessageLoadingInProgress = false; const readState = channel.state.read[this.chatClientService.chatClient.user?.id || '']; this.activeChannelLastReadMessageId = readState?.last_read_message_id; @@ -613,6 +618,7 @@ export class ChannelService< this.activeChannelLastReadMessageId = undefined; this.activeChannelUnreadCount = undefined; this.areReadEventsPaused = false; + this.isMessageLoadingInProgress = false; } /** @@ -1268,6 +1274,7 @@ export class ChannelService< * @param parentMessageId The ID of the parent message if we want to load a thread message */ async jumpToMessage(messageId: string, parentMessageId?: string) { + this.isMessageLoadingInProgress = true; const activeChannel = this.activeChannelSubject.getValue(); try { await activeChannel?.state.loadMessageIntoState( @@ -1289,6 +1296,8 @@ export class ChannelService< 'streamChat.Message not found' ); throw error; + } finally { + this.isMessageLoadingInProgress = false; } } diff --git a/projects/stream-chat-angular/src/lib/message-list/message-list.component.spec.ts b/projects/stream-chat-angular/src/lib/message-list/message-list.component.spec.ts index 79a42db1..f3bead5a 100644 --- a/projects/stream-chat-angular/src/lib/message-list/message-list.component.spec.ts +++ b/projects/stream-chat-angular/src/lib/message-list/message-list.component.spec.ts @@ -1087,7 +1087,7 @@ describe('MessageListComponent', () => { expect(component['resetScrollState']).not.toHaveBeenCalled(); }); - it('should jump to first unread message if openMessageListAt specifies', () => { + it('should jump to first unread message if openMessageListAt specifies', fakeAsync(() => { component.openMessageListAt = 'last-read-message'; const channel = generateMockChannels()[0]; @@ -1097,6 +1097,7 @@ describe('MessageListComponent', () => { messages[messages.length - 2].id; channelServiceMock.activeChannel$.next(channel); channelServiceMock.activeChannelMessages$.next(messages); + tick(); expect(component.lastReadMessageId).toBe(messages[messages.length - 2].id); @@ -1105,7 +1106,7 @@ describe('MessageListComponent', () => { ); expect(component.isJumpingToLatestUnreadMessage).toBeTrue(); - }); + })); it('should display new message indicator - new mesage is the first on the given day', () => { component.openMessageListAt = 'last-read-message'; diff --git a/projects/stream-chat-angular/src/lib/message-list/message-list.component.ts b/projects/stream-chat-angular/src/lib/message-list/message-list.component.ts index 3ca00963..0aad1211 100644 --- a/projects/stream-chat-angular/src/lib/message-list/message-list.component.ts +++ b/projects/stream-chat-angular/src/lib/message-list/message-list.component.ts @@ -197,9 +197,9 @@ export class MessageListComponent ); this.subscriptions.push( this.channelService.activeChannel$.subscribe((channel) => { - let isNewChannel = false; + let wasChannelSwitch = false; if (this.channelId !== channel?.id) { - isNewChannel = true; + wasChannelSwitch = true; if (this.checkIfUnreadNotificationIsVisibleTimeout) { clearTimeout(this.checkIfUnreadNotificationIsVisibleTimeout); } @@ -227,9 +227,17 @@ export class MessageListComponent ) { this.lastReadMessageId = lastReadMessageId; this.unreadCount = unreadCount || 0; - if (isNewChannel && this.lastReadMessageId) { + if (wasChannelSwitch && this.lastReadMessageId) { if (this.openMessageListAt === 'last-read-message') { - this.jumpToFirstUnreadMessage(); + setTimeout(() => { + // Don't jump if we already have a message jump in progress + if ( + !this.isJumpingToMessage && + !this.channelService.isMessageLoadingInProgress + ) { + this.jumpToFirstUnreadMessage(); + } + }, 0); } else { // Wait till messages and the unread banner is rendered // If unread banner isn't visible on the screen, we display the unread notificaion @@ -702,8 +710,9 @@ export class MessageListComponent const lastReadIndex = messages.findIndex( (m) => m.id === this.lastReadMessageId ); - this.firstUnreadMessageId = - messages[lastReadIndex + 1]?.id || this.lastReadMessageId; + if (lastReadIndex !== -1) { + this.firstUnreadMessageId = messages[lastReadIndex + 1]?.id; + } } }), tap( diff --git a/projects/stream-chat-angular/src/lib/mocks/index.ts b/projects/stream-chat-angular/src/lib/mocks/index.ts index 34b9ecd6..3f55054f 100644 --- a/projects/stream-chat-angular/src/lib/mocks/index.ts +++ b/projects/stream-chat-angular/src/lib/mocks/index.ts @@ -310,7 +310,9 @@ export const mockChannelService = (): MockChannelService => { channel; }; - const jumpToMessage = () => {}; + const jumpToMessage = () => { + activeChannelMessages$.next(activeChannelMessages$.getValue()); + }; const clearMessageJump = () => {};