diff --git a/apps/meteor/client/hooks/useMergedRefsV2.ts b/apps/meteor/client/hooks/useMergedRefsV2.ts new file mode 100644 index 0000000000000..beb3e45c28fa7 --- /dev/null +++ b/apps/meteor/client/hooks/useMergedRefsV2.ts @@ -0,0 +1,30 @@ +import type { MutableRefObject, Ref, RefCallback } from 'react'; +import { useCallback } from 'react'; + +const isRefCallback = (x: unknown): x is RefCallback => typeof x === 'function'; +const isMutableRefObject = (x: unknown): x is MutableRefObject => typeof x === 'object'; + +export const setRef = (ref: Ref | undefined, refValue: T) => { + if (isRefCallback(ref)) { + ref(refValue); + return; + } + + if (isMutableRefObject(ref)) { + ref.current = refValue; + } +}; + +// TODO: backport to fuselage-hooks +/** + * Merges multiple refs into a single ref callback + * + * @param refs The refs to merge. + * @returns The merged ref callback. + */ +export const useMergedRefsV2 = (...refs: (Ref | undefined)[]): RefCallback => { + return useCallback((refValue: T) => { + refs.forEach((ref) => setRef(ref, refValue)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, refs); +}; diff --git a/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts b/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts index e5154fcbbe3ec..0be17c5e795b4 100644 --- a/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts +++ b/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts @@ -22,9 +22,11 @@ export const useJumpToMessageImperative = () => { if (!jumpToRef.current || !containerRef.current) { return; } - jumpToRef.current.scrollIntoView({ - block: 'center', - }); + + // calculate the scroll position to center the message + // avoiding scrollIntoView because it will can scroll parent elements + containerRef.current.scrollTop = + jumpToRef.current.offsetTop - containerRef.current.clientHeight / 2 + jumpToRef.current.offsetHeight / 2; }, []); return { diff --git a/apps/meteor/client/views/room/body/RoomBody.tsx b/apps/meteor/client/views/room/body/RoomBody.tsx index 2656064b9ed9f..54aee2f2c1578 100644 --- a/apps/meteor/client/views/room/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/body/RoomBody.tsx @@ -1,5 +1,4 @@ import { Box } from '@rocket.chat/fuselage'; -import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { usePermission, useRole, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; import type { MouseEvent, ReactElement } from 'react'; import { memo, useCallback, useMemo } from 'react'; @@ -16,6 +15,7 @@ import { useReadMessageWindowEvents } from './hooks/useReadMessageWindowEvents'; import { isTruthy } from '../../../../lib/isTruthy'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; +import { useMergedRefsV2 } from '../../../hooks/useMergedRefsV2'; import { BubbleDate } from '../BubbleDate'; import MessageListErrorBoundary from '../MessageList/MessageListErrorBoundary'; import RoomAnnouncement from '../RoomAnnouncement'; @@ -110,7 +110,7 @@ const RoomBody = (): ReactElement => { const { innerRef: restoreScrollPositionInnerRef, jumpToRef: jumpToRefRestoreScrollPosition } = useRestoreScrollPosition(room._id); - const jumpToRef = useMergedRefs( + const jumpToRef = useMergedRefsV2( jumpToRefGetMore, jumpToRefIsAtBottom, jumpToRefRestoreScrollPosition, @@ -135,7 +135,7 @@ const RoomBody = (): ReactElement => { isAtBottom, }); - const innerRef = useMergedRefs( + const innerRef = useMergedRefsV2( dateScrollInnerRef, restoreScrollPositionInnerRef, isAtBottomInnerRef, @@ -147,7 +147,7 @@ const RoomBody = (): ReactElement => { jumpToRefGetMoreImperativeInnerRef, ); - const wrapperBoxRefs = useMergedRefs(unreadBarWrapperRef); + const wrapperBoxRefs = useMergedRefsV2(unreadBarWrapperRef); const handleNavigateToPreviousMessage = useCallback((): void => { chat.messageEditing.toPreviousMessage(); diff --git a/apps/meteor/client/views/room/body/RoomBodyV2.tsx b/apps/meteor/client/views/room/body/RoomBodyV2.tsx index 54f24fd4c4d62..b8d5246ecf543 100644 --- a/apps/meteor/client/views/room/body/RoomBodyV2.tsx +++ b/apps/meteor/client/views/room/body/RoomBodyV2.tsx @@ -1,5 +1,4 @@ import { Box } from '@rocket.chat/fuselage'; -import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { usePermission, useRole, useSetting, useTranslation, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; import type { MouseEvent, ReactElement } from 'react'; import { memo, useCallback, useMemo } from 'react'; @@ -7,6 +6,7 @@ import { memo, useCallback, useMemo } from 'react'; import { isTruthy } from '../../../../lib/isTruthy'; import { CustomScrollbars } from '../../../components/CustomScrollbars'; import { useEmbeddedLayout } from '../../../hooks/useEmbeddedLayout'; +import { useMergedRefsV2 } from '../../../hooks/useMergedRefsV2'; import { BubbleDate } from '../BubbleDate'; import { MessageList } from '../MessageList'; import DropTargetOverlay from './DropTargetOverlay'; @@ -110,7 +110,7 @@ const RoomBody = (): ReactElement => { const { innerRef: restoreScrollPositionInnerRef, jumpToRef: jumpToRefRestoreScrollPosition } = useRestoreScrollPosition(room._id); - const jumpToRef = useMergedRefs( + const jumpToRef = useMergedRefsV2( jumpToRefIsAtBottom, jumpToRefGetMore, jumpToRefRestoreScrollPosition, @@ -135,7 +135,7 @@ const RoomBody = (): ReactElement => { isAtBottom, }); - const innerRef = useMergedRefs( + const innerRef = useMergedRefsV2( dateScrollInnerRef, restoreScrollPositionInnerRef, isAtBottomInnerRef, diff --git a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts index 10bf933ab30d4..a95cbce9f3f9f 100644 --- a/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts +++ b/apps/meteor/client/views/room/body/hooks/useRestoreScrollPosition.ts @@ -19,10 +19,12 @@ export function useRestoreScrollPosition(rid: string, wait = 100) { node.scrollLeft = 30; } const handleWrapperScroll = withThrottling({ wait })((event) => { + const store = RoomManager.getStore(rid); store?.update({ scroll: event.target.scrollTop, atBottom: isAtBottom(event.target, 50) }); }); node.addEventListener('scroll', handleWrapperScroll, { passive: true }); return () => { + handleWrapperScroll.cancel(); node.removeEventListener('scroll', handleWrapperScroll); }; },