Skip to content

Commit

Permalink
Merge pull request #677 from GetStream/fix-jump-to-message
Browse files Browse the repository at this point in the history
fix: delay jumping to last read message to allow precedence to channelService.jumpToMessage
  • Loading branch information
szuperaz authored Jan 3, 2025
2 parents b00c85b + 5ecc331 commit dc00493
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 10 deletions.
10 changes: 9 additions & 1 deletion projects/stream-chat-angular/src/lib/channel.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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,
Expand All @@ -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 () => {
Expand All @@ -2211,6 +2218,7 @@ describe('ChannelService', () => {
expect(notificationService.addTemporaryNotification).toHaveBeenCalledWith(
'streamChat.Message not found'
);
expect(service.isMessageLoadingInProgress).toBeFalse();
});

it('should pin message', async () => {
Expand Down
9 changes: 9 additions & 0 deletions projects/stream-chat-angular/src/lib/channel.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Channel<T>[] | undefined>(
undefined
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -613,6 +618,7 @@ export class ChannelService<
this.activeChannelLastReadMessageId = undefined;
this.activeChannelUnreadCount = undefined;
this.areReadEventsPaused = false;
this.isMessageLoadingInProgress = false;
}

/**
Expand Down Expand Up @@ -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(
Expand All @@ -1289,6 +1296,8 @@ export class ChannelService<
'streamChat.Message not found'
);
throw error;
} finally {
this.isMessageLoadingInProgress = false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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);

Expand All @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -227,9 +227,18 @@ export class MessageListComponent
) {
this.lastReadMessageId = lastReadMessageId;
this.unreadCount = unreadCount || 0;
if (isNewChannel && this.lastReadMessageId) {
if (wasChannelSwitch && this.lastReadMessageId) {
// Delay jumping to last read message in case we need to give precedence to channelService.jumpToMessage
if (this.openMessageListAt === 'last-read-message') {
this.jumpToFirstUnreadMessage();
setTimeout(() => {
// Don't jump if a jump to a message was already started (using channelService.jumpToMessage)
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
Expand Down Expand Up @@ -702,8 +711,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(
Expand Down
4 changes: 3 additions & 1 deletion projects/stream-chat-angular/src/lib/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,9 @@ export const mockChannelService = (): MockChannelService => {
channel;
};

const jumpToMessage = () => {};
const jumpToMessage = () => {
activeChannelMessages$.next(activeChannelMessages$.getValue());
};

const clearMessageJump = () => {};

Expand Down

0 comments on commit dc00493

Please sign in to comment.