improve timeline behaviour - WIP
This commit is contained in:
parent
f979a994e0
commit
ef83de5c23
4 changed files with 52 additions and 62 deletions
|
|
@ -273,8 +273,7 @@ export function OutboxView({
|
|||
!!prevMsg &&
|
||||
!dayDivider &&
|
||||
prevMsg.original.type === sender.localEvent.original.type &&
|
||||
minuteDifference(prevMsg.ts, sender.localEvent.ts) < 2 &&
|
||||
!replyEventId;
|
||||
minuteDifference(prevMsg.ts, sender.localEvent.ts) < 2;
|
||||
|
||||
prevMsg = sender.localEvent;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import { RoomIntro } from '../../components/room-intro';
|
|||
import { usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||
import { MessageEvent, StateEvent } from '../../../types/matrix/room';
|
||||
import { RenderMessageContent } from '../../components/RenderMessageContent';
|
||||
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
|
||||
import { useIgnoredUsers } from '../../hooks/useIgnoredUsers';
|
||||
import { useImagePackRooms } from '../../hooks/useImagePackRooms';
|
||||
import { useIsDirectRoom, useRoom } from '../../hooks/useRoom';
|
||||
|
|
@ -52,6 +51,7 @@ import {
|
|||
useEditorResizeStickBottom,
|
||||
useEventCollapse,
|
||||
useEventDivider,
|
||||
useMessageEditorOutOfViewFocus,
|
||||
useReplyClickHandler,
|
||||
useRestoreScrollToBottom,
|
||||
useTimelinePaginate,
|
||||
|
|
@ -139,18 +139,8 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
const canPinEvent = permissions.stateEvent(StateEvent.RoomPinnedEvents, ct.session.userId);
|
||||
const [editId, setEditId] = useState<string>();
|
||||
|
||||
// const roomToParents = useAtomValue(roomToParentsAtom);
|
||||
// const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||
const { navigateRoom } = useRoomNavigate();
|
||||
|
||||
const imagePackRooms = useImagePackRooms(roomId);
|
||||
|
||||
// const [unreadInfo, setUnreadInfo] = useState(() => getRoomUnreadInfo(room, true));
|
||||
const readUptoEventIdRef = useRef<string>();
|
||||
// if (unreadInfo) {
|
||||
// readUptoEventIdRef.current = unreadInfo.readUptoEventId;
|
||||
// }
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [focusItem, setFocusItem] = useState<
|
||||
|
|
@ -182,6 +172,8 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
|
||||
const getScrollElement = useCallback(() => scrollRef.current, []);
|
||||
|
||||
const readReceipt = useUserReadReceipt();
|
||||
|
||||
useTimelinePrefetch(timeline, PAGINATION_LIMIT);
|
||||
const handlePaginate = useTimelinePaginate(timeline, PAGINATION_LIMIT);
|
||||
useTimelineScrollRestore(timeline, scrollRef);
|
||||
|
|
@ -204,7 +196,7 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
});
|
||||
|
||||
const tryAutoMarkAsRead = useCallback(() => {
|
||||
console.log('mark as read.');
|
||||
console.log('%cmark as read.', 'background: blue; color: white; padding: 5px 10px;');
|
||||
}, []);
|
||||
|
||||
const newDividerRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -254,21 +246,7 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
}, 2000);
|
||||
}, [alive, focusItem, scrollToItem]);
|
||||
|
||||
// scroll out of view msg editor in view.
|
||||
useEffect(() => {
|
||||
if (editId) {
|
||||
const editMsgElement =
|
||||
(scrollRef.current?.querySelector(`[data-message-id="${editId}"]`) as HTMLElement) ??
|
||||
undefined;
|
||||
if (editMsgElement) {
|
||||
scrollToElement(editMsgElement, {
|
||||
align: 'center',
|
||||
behavior: 'smooth',
|
||||
stopInView: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [scrollToElement, editId]);
|
||||
useMessageEditorOutOfViewFocus(scrollRef, scrollToElement, editId);
|
||||
|
||||
const stickToBottom = useCallback(() => {
|
||||
if (!atBottom) return;
|
||||
|
|
@ -282,7 +260,15 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
}, [getScrollElement, atBottom]);
|
||||
|
||||
useObservableValueListener(timeline.outbox.senders, stickToBottom);
|
||||
useObservableValueListener(timeline.range.info, stickToBottom);
|
||||
useObservableValueListener(
|
||||
timeline.range.info,
|
||||
useCallback(() => {
|
||||
const batchInfo = timeline.batch.read;
|
||||
if (batchInfo.change.dir === 'f' && batchInfo.next.end) {
|
||||
stickToBottom();
|
||||
}
|
||||
}, [stickToBottom, timeline])
|
||||
);
|
||||
|
||||
const handleOpenReply = useCallback(
|
||||
(replyId: string) => {
|
||||
|
|
@ -503,7 +489,6 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
);
|
||||
};
|
||||
|
||||
const readReceipt = useUserReadReceipt();
|
||||
const { getDayDivider, getNewDivider, updateDivider, clearDayDivider, clearNewDivider } =
|
||||
useEventDivider(readReceipt?.ts);
|
||||
const { getCollapsed, updateCollapse } = useEventCollapse();
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ import { useRoomMembersAPI } from '../../../hooks/useRoomMembers';
|
|||
import { useClient } from '../../../hooks/useClient';
|
||||
import { createMentionElement, moveCursor } from '../../../components/editor';
|
||||
import { inSameDay, minuteDifference } from '../../../utils/time';
|
||||
import { fallbackReply, getReplyId, getThreadId } from '../../../utils/room';
|
||||
import { getThreadId } from '../../../utils/room';
|
||||
import { ScrollToElement } from '../../../hooks/useVirtualPaginator';
|
||||
|
||||
const TimelineViewContext = createContext<TimelineView | null>(null);
|
||||
export const TimelineViewProvider = TimelineViewContext.Provider;
|
||||
|
|
@ -77,6 +78,9 @@ export const useTimelinePaginate = (timeline: TimelineView, limit: number) =>
|
|||
[timeline, limit]
|
||||
);
|
||||
|
||||
/**
|
||||
* Restore timeline scrolling position on room open.
|
||||
*/
|
||||
export const useTimelineScrollRestore = (
|
||||
timeline: TimelineView,
|
||||
scrollRef: RefObject<HTMLDivElement>
|
||||
|
|
@ -88,7 +92,7 @@ export const useTimelineScrollRestore = (
|
|||
if (scrollTop) {
|
||||
scrollEl.scrollTo({ top: scrollTop, behavior: 'instant' });
|
||||
} else {
|
||||
scrollToBottom(scrollEl);
|
||||
scrollToBottom(scrollEl, 'instant');
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
|
|
@ -160,7 +164,7 @@ export const useAtBottomToggle = (
|
|||
onBottom?: (bottom: boolean) => void
|
||||
): [boolean, RefObject<HTMLElement>] => {
|
||||
const atBottomAnchorRef = useRef<HTMLElement>(null);
|
||||
const [atBottom, setAtBottom] = useState<boolean>(true);
|
||||
const [atBottom, setAtBottom] = useState<boolean>(atLiveEnd);
|
||||
|
||||
const debounceSetAtBottom = useDebounce(
|
||||
useCallback(
|
||||
|
|
@ -180,7 +184,11 @@ export const useAtBottomToggle = (
|
|||
const target = atBottomAnchorRef.current;
|
||||
if (!target) return;
|
||||
const targetEntry = getIntersectionObserverEntry(target, entries);
|
||||
|
||||
// set to false with delay
|
||||
if (targetEntry) debounceSetAtBottom(targetEntry);
|
||||
|
||||
// set to true instantly
|
||||
if (targetEntry?.isIntersecting && atLiveEnd) {
|
||||
setAtBottom(true);
|
||||
onBottom?.(true);
|
||||
|
|
@ -344,8 +352,6 @@ export const useEventCollapse = (threadId?: string) => {
|
|||
newDivider: boolean,
|
||||
dayDivider: boolean
|
||||
) => {
|
||||
const replyEventId = fallbackReply(event.content) ? undefined : getReplyId(event.content);
|
||||
|
||||
const collapsed =
|
||||
isPrevRendered &&
|
||||
!dayDivider &&
|
||||
|
|
@ -354,8 +360,7 @@ export const useEventCollapse = (threadId?: string) => {
|
|||
prevEvent.sender === event.sender &&
|
||||
prevEvent.type === event.type &&
|
||||
(threadId ? prevEvent.event_id !== threadId : !getThreadId(prevEvent)) &&
|
||||
minuteDifference(prevEvent.origin_server_ts, event.origin_server_ts) < 2 &&
|
||||
!replyEventId;
|
||||
minuteDifference(prevEvent.origin_server_ts, event.origin_server_ts) < 2;
|
||||
|
||||
return collapsed;
|
||||
};
|
||||
|
|
@ -367,3 +372,25 @@ export const useEventCollapse = (threadId?: string) => {
|
|||
|
||||
return { getCollapsed, updateCollapse };
|
||||
};
|
||||
|
||||
export const useMessageEditorOutOfViewFocus = (
|
||||
scrollRef: RefObject<HTMLDivElement>,
|
||||
scrollToElement: ScrollToElement,
|
||||
editId?: string
|
||||
) => {
|
||||
// scroll out of view msg editor in view.
|
||||
useEffect(() => {
|
||||
if (editId) {
|
||||
const editMsgElement =
|
||||
(scrollRef.current?.querySelector(`[data-message-id="${editId}"]`) as HTMLElement) ??
|
||||
undefined;
|
||||
if (editMsgElement) {
|
||||
scrollToElement(editMsgElement, {
|
||||
align: 'center',
|
||||
behavior: 'smooth',
|
||||
stopInView: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [scrollRef, scrollToElement, editId]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
import React, {
|
||||
RefObject,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import React, { RefObject, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ReactEditor } from 'slate-react';
|
||||
import { Editor } from 'slate';
|
||||
import { Box, Scroll, Text, config } from 'folds';
|
||||
|
|
@ -52,6 +44,7 @@ import {
|
|||
useEditorResizeStickBottom,
|
||||
useEventCollapse,
|
||||
useEventDivider,
|
||||
useMessageEditorOutOfViewFocus,
|
||||
useReplyClickHandler,
|
||||
useRestoreScrollToBottom,
|
||||
useTimelinePaginate,
|
||||
|
|
@ -242,21 +235,7 @@ export function TimelineView({ threadTimeline, threadInputRef, editor }: Timelin
|
|||
}
|
||||
}, [scrollToBottomCount]);
|
||||
|
||||
// scroll out of view msg editor in view.
|
||||
useEffect(() => {
|
||||
if (editId) {
|
||||
const editMsgElement =
|
||||
(scrollRef.current?.querySelector(`[data-message-id="${editId}"]`) as HTMLElement) ??
|
||||
undefined;
|
||||
if (editMsgElement) {
|
||||
scrollToElement(editMsgElement, {
|
||||
align: 'center',
|
||||
behavior: 'smooth',
|
||||
stopInView: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [scrollToElement, editId]);
|
||||
useMessageEditorOutOfViewFocus(scrollRef, scrollToElement, editId);
|
||||
|
||||
const handleOpenReply = useCallback((replyId: string) => {
|
||||
console.log(replyId);
|
||||
|
|
|
|||
Loading…
Reference in a new issue