mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
fix(app): refetch the tail when a channel window falls behind (#2565)
This commit is contained in:
1 parent
f00c6ee47a
commit
977b6767cd
10 files changed
+475
-27
No files matched your search
@@ -29,6 +29,8 @@ import {
|
||||
resolveChannelMessagesWindowStatus,
|
||||
selectChannelMessagesFillerVisible,
|
||||
selectChannelMessagesSpacerHeight,
|
||||
selectChannelMessagesTailGapId,
|
||||
selectChannelMessagesTailProbeId,
|
||||
selectChannelMessagesWindowBar,
|
||||
} from '@app/features/messaging/state/ChannelMessagesLoadStateMachine';
|
||||
import MessageEdit from '@app/features/messaging/state/MessageEdit';
|
||||
@@ -66,7 +68,9 @@ import {clsx} from 'clsx';
|
||||
import {runInAction} from 'mobx';
|
||||
import {observer, useLocalObservable} from 'mobx-react-lite';
|
||||
import type React from 'react';
|
||||
import {useCallback, useEffect, useMemo, useRef} from 'react';
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
|
||||
const TAIL_PROBE_MAX_ATTEMPTS = 2;
|
||||
|
||||
const MESSAGE_LIST_FOR_DESCRIPTOR = msg({
|
||||
message: 'Message list for {channelName}',
|
||||
@@ -167,6 +171,9 @@ export const Messages = observer(function Messages({
|
||||
const scrollerContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const lastStateSnapshotRef = useRef<MessagesStateSnapshot | null>(null);
|
||||
const recoveryFetchChannelIdRef = useRef<string | null>(null);
|
||||
const tailProbeKeyRef = useRef<string | null>(null);
|
||||
const tailProbeAttemptsRef = useRef<{key: string; attempts: number} | null>(null);
|
||||
const [settledTailProbeKey, setSettledTailProbeKey] = useState<string | null>(null);
|
||||
interface MessageState extends MessagesStateSnapshot {
|
||||
highlightedMessageId: string | null;
|
||||
isAtBottom: boolean;
|
||||
@@ -202,6 +209,17 @@ export const Messages = observer(function Messages({
|
||||
});
|
||||
const windowBar = selectChannelMessagesWindowBar(windowStatus);
|
||||
const windowNeedsPage = windowStatus.needsPage;
|
||||
const tailInput = {
|
||||
status: windowStatus,
|
||||
loading: safeMessages.loadingMore || safeMessages.probeLoading,
|
||||
newestLoadedMessageId: safeMessages.last()?.id ?? null,
|
||||
knownLatestMessageId: state.lastReadStateMessageId,
|
||||
};
|
||||
const tailWatermarkMessageId = state.lastReadStateMessageId;
|
||||
const tailGapMessageId = selectChannelMessagesTailGapId(tailInput);
|
||||
const tailProbeMessageId = selectChannelMessagesTailProbeId(tailInput);
|
||||
const tailGapKey = tailGapMessageId == null ? null : `${tailGapMessageId}:${tailWatermarkMessageId}`;
|
||||
const tailProbeKey = tailProbeMessageId == null ? null : `${tailProbeMessageId}:${tailWatermarkMessageId}`;
|
||||
const canAutoAck = shouldAutoAck({
|
||||
channelActive: allowAutoAck,
|
||||
windowFocused: isWindowFocused,
|
||||
@@ -454,6 +472,42 @@ export const Messages = observer(function Messages({
|
||||
}
|
||||
});
|
||||
}, [channel.id, isGatewayConnected, selectedChannelId, windowNeedsPage, state.messageVersion]);
|
||||
useEffect(() => {
|
||||
if (!isGatewayConnected) {
|
||||
tailProbeKeyRef.current = null;
|
||||
return;
|
||||
}
|
||||
if (tailProbeMessageId == null || tailProbeKey == null || tailWatermarkMessageId == null) {
|
||||
return;
|
||||
}
|
||||
if (selectedChannelId !== channel.id || tailProbeKeyRef.current === tailProbeKey) {
|
||||
return;
|
||||
}
|
||||
tailProbeKeyRef.current = tailProbeKey;
|
||||
void MessageCommands.fetchMessages(channel.id, null, tailProbeMessageId, MAX_MESSAGES_PER_CHANNEL, undefined, {
|
||||
tailProbe: {
|
||||
watermarkMessageId: tailWatermarkMessageId,
|
||||
onSettled: (settlement) => {
|
||||
if (settlement === 'applied') {
|
||||
setSettledTailProbeKey(tailProbeKey);
|
||||
return;
|
||||
}
|
||||
if (settlement === 'failed') {
|
||||
const attempts =
|
||||
tailProbeAttemptsRef.current?.key === tailProbeKey ? tailProbeAttemptsRef.current.attempts : 0;
|
||||
if (attempts >= TAIL_PROBE_MAX_ATTEMPTS) {
|
||||
setSettledTailProbeKey(tailProbeKey);
|
||||
return;
|
||||
}
|
||||
tailProbeAttemptsRef.current = {key: tailProbeKey, attempts: attempts + 1};
|
||||
}
|
||||
if (tailProbeKeyRef.current === tailProbeKey) {
|
||||
tailProbeKeyRef.current = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [channel.id, isGatewayConnected, selectedChannelId, tailProbeKey, tailProbeMessageId, tailWatermarkMessageId]);
|
||||
useMessageListKeyboardNavigation({
|
||||
containerRef: scrollManager.ref,
|
||||
channelId: channel.id,
|
||||
@@ -488,10 +542,11 @@ export const Messages = observer(function Messages({
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!canAutoAck || !state.isAtBottom || !state.messages?.ready) return;
|
||||
if (tailGapKey != null && settledTailProbeKey !== tailGapKey) return;
|
||||
if (ReadStates.hasUnread(channel.id)) {
|
||||
ReadStateCommands.ackWithStickyUnread(channel.id);
|
||||
}
|
||||
}, [canAutoAck, state.isAtBottom, state.messages?.ready, channel.id]);
|
||||
}, [canAutoAck, state.isAtBottom, state.messages?.ready, tailGapKey, settledTailProbeKey, channel.id]);
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
const readState = ReadStates.getIfExists(channel.id);
|
||||
|
||||
Reference in new issue
Block a user