Skip to content

Commit f0599dc

Browse files
committed
fix: jump to first unread message based on last_read timestamp
1 parent cf1ca4e commit f0599dc

File tree

3 files changed

+358
-317
lines changed

3 files changed

+358
-317
lines changed

Diff for: src/components/Channel/Channel.tsx

+87-28
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import {
7070
import type { UnreadMessagesNotificationProps } from '../MessageList';
7171
import { hasMoreMessagesProbably, UnreadMessagesSeparator } from '../MessageList';
7272
import { useChannelContainerClasses } from './hooks/useChannelContainerClasses';
73-
import { makeAddNotifications } from './utils';
73+
import { findInMsgSetByDate, findInMsgSetById, makeAddNotifications } from './utils';
7474
import { getChannel } from '../../utils';
7575

7676
import type { MessageProps } from '../Message/types';
@@ -770,54 +770,113 @@ const ChannelInner = <
770770
const jumpToFirstUnreadMessage = useCallback(
771771
async (queryMessageLimit = 100) => {
772772
if (!(client.user && channelUnreadUiState?.unread_messages)) return;
773-
if (!channelUnreadUiState?.last_read_message_id) {
774-
addNotification(t('Failed to jump to the first unread message'), 'error');
775-
return;
773+
let lastReadMessageId = channelUnreadUiState?.last_read_message_id;
774+
let firstUnreadMessageId = channelUnreadUiState?.first_unread_message_id;
775+
let isInCurrentMessageSet = false;
776+
let hasMoreMessages = true;
777+
778+
if (firstUnreadMessageId) {
779+
const result = findInMsgSetById(firstUnreadMessageId, channel.state.messages);
780+
isInCurrentMessageSet = result.index !== -1;
781+
} else if (lastReadMessageId) {
782+
const result = findInMsgSetById(lastReadMessageId, channel.state.messages);
783+
isInCurrentMessageSet = !!result.target;
784+
firstUnreadMessageId =
785+
result.index > -1 ? channel.state.messages[result.index + 1]?.id : undefined;
786+
} else {
787+
const lastReadTimestamp = channelUnreadUiState.last_read.getTime();
788+
const { index: lastReadMessageIndex, target: lastReadMessage } = findInMsgSetByDate(
789+
channelUnreadUiState.last_read,
790+
channel.state.messages,
791+
true,
792+
);
793+
794+
if (lastReadMessage) {
795+
firstUnreadMessageId = channel.state.messages[lastReadMessageIndex + 1]?.id;
796+
isInCurrentMessageSet = !!firstUnreadMessageId;
797+
lastReadMessageId = lastReadMessage.id;
798+
} else {
799+
dispatch({ loadingMore: true, type: 'setLoadingMore' });
800+
let messages;
801+
try {
802+
messages = (
803+
await channel.query(
804+
{
805+
messages: {
806+
created_at_around: channelUnreadUiState.last_read.toISOString(),
807+
limit: queryMessageLimit,
808+
},
809+
},
810+
'new',
811+
)
812+
).messages;
813+
} catch (e) {
814+
addNotification(t('Failed to jump to the first unread message'), 'error');
815+
loadMoreFinished(hasMoreMessages, channel.state.messages);
816+
return;
817+
}
818+
const firstMessageWithCreationDate = messages.find((msg) => msg.created_at);
819+
if (!(messages.length && firstMessageWithCreationDate)) {
820+
addNotification(t('Failed to jump to the first unread message'), 'error');
821+
return;
822+
}
823+
const firstMessageTimestamp = new Date(
824+
firstMessageWithCreationDate.created_at as string,
825+
).getTime();
826+
if (lastReadTimestamp < firstMessageTimestamp) {
827+
// whole channel is unread
828+
firstUnreadMessageId = firstMessageWithCreationDate.id;
829+
hasMoreMessages = false;
830+
} else {
831+
const result = findInMsgSetByDate(channelUnreadUiState.last_read, messages);
832+
lastReadMessageId = result.target?.id;
833+
hasMoreMessages = result.index >= Math.floor(queryMessageLimit / 2);
834+
}
835+
}
776836
}
777837

778-
let indexOfLastReadMessage;
779-
780-
const currentMessageSet = channel.state.messages;
781-
for (let i = currentMessageSet.length - 1; i >= 0; i--) {
782-
const { id } = currentMessageSet[i];
783-
if (id === channelUnreadUiState.last_read_message_id) {
784-
indexOfLastReadMessage = i;
785-
break;
786-
}
838+
if (!firstUnreadMessageId && !lastReadMessageId) {
839+
addNotification(t('Failed to jump to the first unread message'), 'error');
840+
return;
787841
}
788842

789-
if (typeof indexOfLastReadMessage === 'undefined') {
843+
if (!isInCurrentMessageSet) {
790844
dispatch({ loadingMore: true, type: 'setLoadingMore' });
791-
let hasMoreMessages = true;
792845
try {
793-
await channel.state.loadMessageIntoState(
794-
channelUnreadUiState.last_read_message_id,
795-
undefined,
796-
queryMessageLimit,
797-
);
846+
const targetId = (firstUnreadMessageId ?? lastReadMessageId) as string;
847+
await channel.state.loadMessageIntoState(targetId, undefined, queryMessageLimit);
798848
/**
799849
* if the index of the last read message on the page is beyond the half of the page,
800850
* we have arrived to the oldest page of the channel
801851
*/
802-
indexOfLastReadMessage = channel.state.messages.findIndex(
803-
(message) => message.id === channelUnreadUiState.last_read_message_id,
852+
const indexOfTarget = channel.state.messages.findIndex(
853+
(message) => message.id === targetId,
804854
) as number;
805-
hasMoreMessages = indexOfLastReadMessage >= Math.floor(queryMessageLimit / 2);
855+
hasMoreMessages = indexOfTarget >= Math.floor(queryMessageLimit / 2);
856+
loadMoreFinished(hasMoreMessages, channel.state.messages);
857+
firstUnreadMessageId =
858+
firstUnreadMessageId ?? channel.state.messages[indexOfTarget + 1]?.id;
806859
} catch (e) {
807860
addNotification(t('Failed to jump to the first unread message'), 'error');
808861
loadMoreFinished(hasMoreMessages, channel.state.messages);
809862
return;
810863
}
811-
812-
loadMoreFinished(hasMoreMessages, channel.state.messages);
813864
}
814865

815-
const firstUnreadMessage = channel.state.messages[indexOfLastReadMessage + 1];
816-
const jumpToMessageId = firstUnreadMessage?.id ?? channelUnreadUiState.last_read_message_id;
866+
if (!firstUnreadMessageId) {
867+
addNotification(t('Failed to jump to the first unread message'), 'error');
868+
return;
869+
}
870+
if (!channelUnreadUiState.first_unread_message_id)
871+
_setChannelUnreadUiState({
872+
...channelUnreadUiState,
873+
first_unread_message_id: firstUnreadMessageId,
874+
last_read_message_id: lastReadMessageId,
875+
});
817876

818877
dispatch({
819878
hasMoreNewer: channel.state.messages !== channel.state.latestMessages,
820-
highlightedMessageId: jumpToMessageId,
879+
highlightedMessageId: firstUnreadMessageId,
821880
type: 'jumpToMessageFinished',
822881
});
823882

0 commit comments

Comments
 (0)