diff --git a/src/app/organisms/room/Room.jsx b/src/app/organisms/room/Room.jsx index b8972d61..015fb689 100644 --- a/src/app/organisms/room/Room.jsx +++ b/src/app/organisms/room/Room.jsx @@ -13,33 +13,46 @@ import RoomSettings from './RoomSettings'; import PeopleDrawer from './PeopleDrawer'; function Room() { - const [roomTimeline, setRoomTimeline] = useState(null); - const [eventId, setEventId] = useState(null); + const [roomInfo, setRoomInfo] = useState({ + roomTimeline: null, + eventId: null, + }); const [isDrawer, setIsDrawer] = useState(settings.isPeopleDrawer); const mx = initMatrix.matrixClient; - const handleRoomSelected = (rId, pRoomId, eId) => { - if (mx.getRoom(rId)) { - setRoomTimeline(new RoomTimeline(rId, initMatrix.notifications)); - setEventId(eId); - } else { - // TODO: add ability to join room if roomId is invalid - setRoomTimeline(null); - setEventId(null); - } - }; - const handleDrawerToggling = (visiblity) => setIsDrawer(visiblity); useEffect(() => { + const handleRoomSelected = (rId, pRoomId, eId) => { + roomInfo.roomTimeline?.removeInternalListeners(); + if (mx.getRoom(rId)) { + setRoomInfo({ + roomTimeline: new RoomTimeline(rId, initMatrix.notifications), + eventId: eId ?? null, + }); + } else { + // TODO: add ability to join room if roomId is invalid + setRoomInfo({ + roomTimeline: null, + eventId: null, + }); + } + }; + navigation.on(cons.events.navigation.ROOM_SELECTED, handleRoomSelected); - settings.on(cons.events.settings.PEOPLE_DRAWER_TOGGLED, handleDrawerToggling); return () => { navigation.removeListener(cons.events.navigation.ROOM_SELECTED, handleRoomSelected); + }; + }, [roomInfo]); + + useEffect(() => { + const handleDrawerToggling = (visiblity) => setIsDrawer(visiblity); + settings.on(cons.events.settings.PEOPLE_DRAWER_TOGGLED, handleDrawerToggling); + return () => { settings.removeListener(cons.events.settings.PEOPLE_DRAWER_TOGGLED, handleDrawerToggling); - roomTimeline?.removeInternalListeners(); }; }, []); + const { roomTimeline, eventId } = roomInfo; if (roomTimeline === null) return ; return ( diff --git a/src/app/organisms/room/RoomViewContent.jsx b/src/app/organisms/room/RoomViewContent.jsx index 751e8354..e3f2aba4 100644 --- a/src/app/organisms/room/RoomViewContent.jsx +++ b/src/app/organisms/room/RoomViewContent.jsx @@ -145,13 +145,12 @@ function useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef) { if (isSpecificEvent) { focusEventIndex = roomTimeline.getEventIndex(eId); - } else if (!readUptoEvtStore.getItem()) { + } + if (!readUptoEvtStore.getItem() && roomTimeline.hasEventInTimeline(readUpToId)) { // either opening live timeline or jump to unread. - focusEventIndex = roomTimeline.getUnreadEventIndex(readUpToId); - if (roomTimeline.hasEventInTimeline(readUpToId)) { - readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId)); - } - } else { + readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId)); + } + if (readUptoEvtStore.getItem() && !isSpecificEvent) { focusEventIndex = roomTimeline.getUnreadEventIndex(readUptoEvtStore.getItem().getId()); } @@ -167,7 +166,6 @@ function useTimeline(roomTimeline, eventId, readUptoEvtStore, eventLimitRef) { setEventTimeline(eventId); return () => { roomTimeline.removeListener(cons.events.roomTimeline.READY, initTimeline); - roomTimeline.removeInternalListeners(); limit.setFrom(0); }; }, [roomTimeline, eventId]); @@ -286,44 +284,39 @@ function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, event useEffect(() => { const timelineScroll = timelineScrollRef.current; const limit = eventLimitRef.current; - const sendReadReceipt = (event) => { - if (event.isSending()) return; + const trySendReadReceipt = (event) => { if (myUserId === event.getSender()) { - roomTimeline.markAllAsRead(); + requestAnimationFrame(() => roomTimeline.markAllAsRead()); return; } const readUpToEvent = readUptoEvtStore.getItem(); const readUpToId = roomTimeline.getReadUpToEventId(); - const isUnread = readUpToEvent?.getId() === readUpToId; + const isUnread = readUpToEvent ? readUpToEvent?.getId() === readUpToId : true; - // if user doesn't have focus on app don't mark messages as read. - if (document.visibilityState === 'hidden' || timelineScroll.bottom >= 16) { - if (isUnread) return; - readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId)); + if (isUnread === false) { + if (document.visibilityState === 'visible' && timelineScroll.bottom < 16) { + requestAnimationFrame(() => roomTimeline.markAllAsRead()); + } else { + readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId)); + } return; } - // user has not mark room as read - if (!isUnread) { - roomTimeline.markAllAsRead(); - } const { timeline } = roomTimeline; - const unreadMsgIsLast = timeline[timeline.length - 2].getId() === readUpToEvent?.getId(); + const unreadMsgIsLast = timeline[timeline.length - 2].getId() === readUpToId; if (unreadMsgIsLast) { - roomTimeline.markAllAsRead(); + requestAnimationFrame(() => roomTimeline.markAllAsRead()); } }; const handleEvent = (event) => { const tLength = roomTimeline.timeline.length; - const isUserViewingLive = ( - roomTimeline.isServingLiveTimeline() - && limit.length >= tLength - 1 - && timelineScroll.bottom < SCROLL_TRIGGER_POS - ); - if (isUserViewingLive) { + const isViewingLive = roomTimeline.isServingLiveTimeline() && limit.length >= tLength - 1; + const isAttached = timelineScroll.bottom < SCROLL_TRIGGER_POS; + + if (isViewingLive && isAttached) { limit.setFrom(tLength - limit.maxEvents); - sendReadReceipt(event); + trySendReadReceipt(event); setEvent(event); return; } @@ -332,11 +325,8 @@ function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, event setEvent(event); return; } - const isUserDitchedLive = ( - roomTimeline.isServingLiveTimeline() - && limit.length >= tLength - 1 - ); - if (isUserDitchedLive) { + + if (isViewingLive) { // This stateUpdate will help to put the // loading msg placeholder at bottom setEvent(event); @@ -353,17 +343,7 @@ function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, event }; }, [roomTimeline]); - useEffect(() => { - const timelineScroll = timelineScrollRef.current; - if (!roomTimeline.initialized) return; - if (timelineScroll.bottom < 16 - && !roomTimeline.canPaginateForward() - && document.visibilityState === 'visible') { - timelineScroll.scrollToBottom(); - } else { - timelineScroll.tryRestoringScroll(); - } - }, [newEvent, roomTimeline]); + return newEvent; } let jumpToItemIndex = -1; @@ -394,7 +374,7 @@ function RoomViewContent({ eventId, roomTimeline }) { timelineScrollRef, eventLimitRef, ); - useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef); + const newEvent = useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, eventLimitRef); const { timeline } = roomTimeline; @@ -448,6 +428,16 @@ function RoomViewContent({ eventId, roomTimeline }) { timelineScroll.tryRestoringScroll(); }, [onLimitUpdate]); + useEffect(() => { + const timelineScroll = timelineScrollRef.current; + if (!roomTimeline.initialized) return; + if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward() && document.visibilityState === 'visible') { + timelineScroll.scrollToBottom(); + } else { + timelineScroll.tryRestoringScroll(); + } + }, [newEvent]); + const handleTimelineScroll = (event) => { const timelineScroll = timelineScrollRef.current; if (!event.target) return; diff --git a/src/client/state/RoomTimeline.js b/src/client/state/RoomTimeline.js index f4911864..bccb197d 100644 --- a/src/client/state/RoomTimeline.js +++ b/src/client/state/RoomTimeline.js @@ -230,9 +230,18 @@ class RoomTimeline extends EventEmitter { markAllAsRead() { const readEventId = this.getReadUpToEventId(); + const getLatestValidEvent = () => { + for (let i = this.timeline.length - 1; i >= 0; i -= 1) { + const latestEvent = this.timeline[i]; + if (latestEvent.getId() === readEventId) return null; + if (!latestEvent.isSending()) return latestEvent; + } + return null; + }; this.notifications.deleteNoti(this.roomId); if (this.timeline.length === 0) return; - const latestEvent = this.timeline[this.timeline.length - 1]; + const latestEvent = getLatestValidEvent(); + if (latestEvent === null) return; if (readEventId === latestEvent.getId()) return; this.matrixClient.sendReadReceipt(latestEvent); this.emit(cons.events.roomTimeline.MARKED_AS_READ, latestEvent);