diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index ec7f52490..9fc1c83d4 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -31,12 +31,13 @@ import React, { MouseEventHandler, ReactNode, useCallback, + useEffect, useRef, useState, } from 'react'; import FocusTrap from 'focus-trap-react'; import { useHover, useFocusWithin } from 'react-aria'; -import { MatrixEvent, Room, EventStatus } from 'matrix-js-sdk'; +import { MatrixEvent, MatrixEventEvent, Room, EventStatus } from 'matrix-js-sdk'; import { Relations } from 'matrix-js-sdk/lib/models/relations'; import classNames from 'classnames'; import { useAtom, useAtomValue } from 'jotai'; @@ -114,6 +115,20 @@ function DeliveryStatus({ }) { const mx = useMatrixClient(); const offline = useAtomValue(sendOfflineAtom); + // [Gitea #306] A local echo's status changes (SENDING → SENT → confirmed, + // NOT_SENT → retried) don't re-render the timeline, so the last message in a + // quiet room kept "Sending..." (or a stale "Failed to send") indefinitely. + // Re-render on the event's own status updates. + const [, setStatusTick] = useState(0); + useEffect(() => { + const bump = () => setStatusTick((t) => t + 1); + mEvent.on(MatrixEventEvent.Status, bump); + mEvent.on(MatrixEventEvent.LocalEventIdReplaced, bump); + return () => { + mEvent.off(MatrixEventEvent.Status, bump); + mEvent.off(MatrixEventEvent.LocalEventIdReplaced, bump); + }; + }, [mEvent]); const { status } = mEvent; if (status === null) return null; // confirmed by server — read receipts take over let iconSrc: IconSrc;