feat: implement useMarkAsRead hook for improved unread state management and update notification handling

This commit is contained in:
2026-02-28 00:10:35 +11:00
parent c890cce37a
commit 29c409fad8
16 changed files with 299 additions and 113 deletions

View File

@@ -94,7 +94,7 @@ import {
getIntersectionObserverEntry,
useIntersectionObserver,
} from '../../hooks/useIntersectionObserver';
import { markAsRead } from '../../utils/notifications';
import { useMarkAsRead } from '../../hooks/useMarkAsRead';
import { useDebounce } from '../../hooks/useDebounce';
import { getResizeObserverEntry, useResizeObserver } from '../../hooks/useResizeObserver';
import * as css from './RoomTimeline.css';
@@ -508,6 +508,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
const spoilerClickHandler = useSpoilerClickHandler();
const openUserRoomProfile = useOpenUserRoomProfile();
const space = useSpaceOptionally();
const markAsRead = useMarkAsRead(mx);
const imagePackRooms: Room[] = useImagePackRooms(room.roomId, roomToParents);
@@ -641,7 +642,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
// Check if the document is in focus (user is actively viewing the app),
// and either there are no unread messages or the latest message is from the current user.
// If either condition is met, trigger the markAsRead function to send a read receipt.
requestAnimationFrame(() => markAsRead(mx, mEvt.getRoomId()!, hideActivity));
requestAnimationFrame(() => markAsRead(mEvt.getRoomId()!, hideActivity));
}
if (!document.hasFocus() && !unreadInfo) {
@@ -665,7 +666,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
setUnreadInfo(getRoomUnreadInfo(room));
}
},
[mx, room, unreadInfo, hideActivity]
[mx, room, unreadInfo, hideActivity, markAsRead]
)
);
@@ -734,15 +735,15 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
const tryAutoMarkAsRead = useCallback(() => {
const readUptoEventId = readUptoEventIdRef.current;
if (!readUptoEventId) {
requestAnimationFrame(() => markAsRead(mx, room.roomId, hideActivity));
requestAnimationFrame(() => markAsRead(room.roomId, hideActivity));
return;
}
const evtTimeline = getEventTimeline(room, readUptoEventId);
const latestTimeline = evtTimeline && getFirstLinkedTimeline(evtTimeline, Direction.Forward);
if (latestTimeline === room.getLiveTimeline()) {
requestAnimationFrame(() => markAsRead(mx, room.roomId, hideActivity));
requestAnimationFrame(() => markAsRead(room.roomId, hideActivity));
}
}, [mx, room, hideActivity]);
}, [room, hideActivity, markAsRead]);
const debounceSetAtBottom = useDebounce(
useCallback((entry: IntersectionObserverEntry) => {
@@ -946,7 +947,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
};
const handleMarkAsRead = () => {
markAsRead(mx, room.roomId, hideActivity);
markAsRead(room.roomId, hideActivity);
};
const handleOpenReply: MouseEventHandler = useCallback(