diff --git a/app/e2e/client/rocketchat.e2e.js b/app/e2e/client/rocketchat.e2e.js index 387e379045cf1..c3a1162cfec95 100644 --- a/app/e2e/client/rocketchat.e2e.js +++ b/app/e2e/client/rocketchat.e2e.js @@ -26,8 +26,8 @@ import './tabbar'; import { log, logError } from './logger'; import { waitUntilFind } from '../../../client/lib/utils/waitUntilFind'; import { imperativeModal } from '../../../client/lib/imperativeModal'; -import SaveE2EPasswordModal from './SaveE2EPasswordModal'; -import EnterE2EPasswordModal from './EnterE2EPasswordModal'; +import SaveE2EPasswordModal from '../../../client/views/e2e/SaveE2EPasswordModal'; +import EnterE2EPasswordModal from '../../../client/views/e2e/EnterE2EPasswordModal'; import { call } from '../../../client/lib/utils/call'; let failedToDecodeKey = false; diff --git a/app/threads/client/components/ThreadSkeleton.tsx b/app/threads/client/components/ThreadSkeleton.tsx deleted file mode 100644 index 7d0cd9e1d1cfa..0000000000000 --- a/app/threads/client/components/ThreadSkeleton.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { FC, useMemo } from 'react'; -import { Modal, Box } from '@rocket.chat/fuselage'; - -import VerticalBar from '../../../../client/components/VerticalBar'; - -type ThreadSkeletonProps = { - expanded: boolean; - onClose: () => void; -}; - -const ThreadSkeleton: FC = ({ expanded, onClose }) => { - const style = useMemo(() => (document.dir === 'rtl' - ? { - left: 0, - borderTopRightRadius: 4, - } - : { - right: 0, - borderTopLeftRadius: 4, - }), []); - - return <> - {expanded && } - - - - ; -}; - -export default ThreadSkeleton; diff --git a/app/threads/client/components/ThreadView.tsx b/app/threads/client/components/ThreadView.tsx deleted file mode 100644 index e0735257d1c4b..0000000000000 --- a/app/threads/client/components/ThreadView.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { ComponentProps, useCallback, useMemo, forwardRef } from 'react'; -import { Modal, Box } from '@rocket.chat/fuselage'; - -import { useTranslation } from '../../../../client/contexts/TranslationContext'; -import { useLayoutContextualBarExpanded } from '../../../../client/providers/LayoutProvider'; -import VerticalBar from '../../../../client/components/VerticalBar'; - -type ThreadViewProps = ComponentProps & { - title: string; - expanded: boolean; - following: boolean; - onToggleExpand: (expanded: boolean) => void; - onToggleFollow: (following: boolean) => void; - onClose: () => void; - onClickBack: (e: unknown) => void; -}; - -const ThreadView = forwardRef(({ - title, - expanded, - following, - onToggleExpand, - onToggleFollow, - onClose, - onClickBack, -}, ref) => { - const hasExpand = useLayoutContextualBarExpanded(); - - const style = useMemo(() => (document.dir === 'rtl' - ? { - left: 0, - borderTopRightRadius: 4, - } - : { - right: 0, - borderTopLeftRadius: 4, - }), []); - - const t = useTranslation(); - - const expandLabel = expanded ? t('Collapse') : t('Expand'); - const expandIcon = expanded ? 'arrow-collapse' : 'arrow-expand'; - - const handleExpandActionClick = useCallback(() => { - onToggleExpand(expanded); - }, [expanded, onToggleExpand]); - - const followLabel = following ? t('Following') : t('Not_Following'); - const followIcon = following ? 'bell' : 'bell-off'; - - const handleFollowActionClick = useCallback(() => { - onToggleFollow(following); - }, [following, onToggleFollow]); - - return <> - {hasExpand && expanded && } - - - - - {onClickBack && } - - {hasExpand && } - - - - - - - - - ; -}); - -export default ThreadView; diff --git a/app/ui/client/lib/Tooltip.js b/app/ui/client/lib/Tooltip.js index eba4f5ddd894f..18156144270f1 100644 --- a/app/ui/client/lib/Tooltip.js +++ b/app/ui/client/lib/Tooltip.js @@ -28,7 +28,7 @@ export const openToolTip = (title, anchor) => { anchor, }; Dep.changed(); - unregister = unregister || createEphemeralPortal(() => import('./TooltipComponent'), props, dom); + unregister = unregister || createEphemeralPortal(() => import('../../../../client/components/TooltipComponent'), props, dom); }; window.matchMedia('(hover: none)').matches || document.body.addEventListener('mouseover', (() => { diff --git a/app/ui/client/lib/TooltipComponent.js b/app/ui/client/lib/TooltipComponent.js deleted file mode 100644 index b5707e6011531..0000000000000 --- a/app/ui/client/lib/TooltipComponent.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, { useRef } from 'react'; -import { Tooltip, PositionAnimated, AnimatedVisibility } from '@rocket.chat/fuselage'; - -export const TooltipComponent = ({ title, anchor }) => { - const ref = useRef(anchor); - - return {title}; -}; - -export default TooltipComponent; diff --git a/client/components/TooltipComponent.tsx b/client/components/TooltipComponent.tsx new file mode 100644 index 0000000000000..0b5ead39ff975 --- /dev/null +++ b/client/components/TooltipComponent.tsx @@ -0,0 +1,24 @@ +import { Tooltip, PositionAnimated, AnimatedVisibility } from '@rocket.chat/fuselage'; +import React, { ReactElement, ReactNode, useRef } from 'react'; + +type TooltipComponentProps = { + title: ReactNode; + anchor: Element; +}; + +export const TooltipComponent = ({ title, anchor }: TooltipComponentProps): ReactElement => { + const ref = useRef(anchor); + + return ( + + {title} + + ); +}; + +export default TooltipComponent; diff --git a/app/e2e/client/EnterE2EPasswordModal.tsx b/client/views/e2e/EnterE2EPasswordModal.tsx similarity index 80% rename from app/e2e/client/EnterE2EPasswordModal.tsx rename to client/views/e2e/EnterE2EPasswordModal.tsx index 9f27bb0203081..c667b73ed0bb8 100644 --- a/app/e2e/client/EnterE2EPasswordModal.tsx +++ b/client/views/e2e/EnterE2EPasswordModal.tsx @@ -1,9 +1,9 @@ import { Box, PasswordInput, Field, FieldGroup } from '@rocket.chat/fuselage'; -import React, { ReactElement, useState, useCallback } from 'react'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import React, { ReactElement, useState, useCallback } from 'react'; -import GenericModal from '../../../client/components/GenericModal'; -import { useTranslation } from '../../../client/contexts/TranslationContext'; +import GenericModal from '../../components/GenericModal'; +import { useTranslation } from '../../contexts/TranslationContext'; const EnterE2EPasswordModal = ({ onConfirm, @@ -18,11 +18,13 @@ const EnterE2EPasswordModal = ({ const [password, setPassword] = useState(''); const [passwordError, setPasswordError] = useState(); - const handleChange = useCallback((e: React.ChangeEvent) => { - e.target.value !== '' && setPasswordError(undefined); - setPassword(e.currentTarget.value); - }, [setPassword]); - + const handleChange = useCallback( + (e: React.ChangeEvent) => { + e.target.value !== '' && setPasswordError(undefined); + setPassword(e.currentTarget.value); + }, + [setPassword], + ); const handleConfirm = useMutableCallback((): void => { if (password === '') { diff --git a/app/e2e/client/SaveE2EPasswordModal.tsx b/client/views/e2e/SaveE2EPasswordModal.tsx similarity index 72% rename from app/e2e/client/SaveE2EPasswordModal.tsx rename to client/views/e2e/SaveE2EPasswordModal.tsx index d397f04d8e15c..f45f47a6849eb 100644 --- a/app/e2e/client/SaveE2EPasswordModal.tsx +++ b/client/views/e2e/SaveE2EPasswordModal.tsx @@ -1,8 +1,8 @@ import { Box } from '@rocket.chat/fuselage'; import React, { ReactElement } from 'react'; -import GenericModal from '../../../client/components/GenericModal'; -import { useTranslation } from '../../../client/contexts/TranslationContext'; +import GenericModal from '../../components/GenericModal'; +import { useTranslation } from '../../contexts/TranslationContext'; const SaveE2EPasswordModal = ({ passwordRevealText, @@ -26,7 +26,9 @@ const SaveE2EPasswordModal = ({ confirmText={t('I_Saved_My_Password')} variant='warning' title={t('Save_Your_Encryption_Password')} - > + > + + ); }; diff --git a/client/views/room/contextualBar/Threads/ThreadList.tsx b/client/views/room/contextualBar/Threads/ThreadList.tsx index 5190d4d8eaec3..93605e4bb4371 100644 --- a/client/views/room/contextualBar/Threads/ThreadList.tsx +++ b/client/views/room/contextualBar/Threads/ThreadList.tsx @@ -3,7 +3,6 @@ import { useResizeObserver, useMutableCallback, useAutoFocus } from '@rocket.cha import React, { FC, useMemo } from 'react'; import { Virtuoso } from 'react-virtuoso'; -import ThreadComponent from '../../../../../app/threads/client/components/ThreadComponent'; import { IMessage } from '../../../../../definition/IMessage'; import { IRoom } from '../../../../../definition/IRoom'; import { IUser } from '../../../../../definition/IUser'; @@ -17,6 +16,7 @@ import { import { useSetting } from '../../../../contexts/SettingsContext'; import { useTranslation } from '../../../../contexts/TranslationContext'; import { useTabContext } from '../../providers/ToolboxProvider'; +import ThreadComponent from '../../threads/ThreadComponent'; import Row from './Row'; import { withData } from './withData'; diff --git a/app/threads/client/components/ThreadComponent.tsx b/client/views/room/threads/ThreadComponent.tsx similarity index 59% rename from app/threads/client/components/ThreadComponent.tsx rename to client/views/room/threads/ThreadComponent.tsx index c7d86130da1fd..9f72949c025a8 100644 --- a/app/threads/client/components/ThreadComponent.tsx +++ b/client/views/room/threads/ThreadComponent.tsx @@ -1,43 +1,52 @@ -import React, { useEffect, useRef, useState, useCallback, useMemo, FC } from 'react'; -import { Template } from 'meteor/templating'; +import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; import { Blaze } from 'meteor/blaze'; +import { Template } from 'meteor/templating'; import { Tracker } from 'meteor/tracker'; -import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; +import React, { useEffect, useRef, useState, useCallback, useMemo, FC } from 'react'; -import { ChatMessage } from '../../../models/client'; -import { useRoute } from '../../../../client/contexts/RouterContext'; -import { roomTypes } from '../../../utils/client'; -import { normalizeThreadTitle } from '../lib/normalizeThreadTitle'; -import { useUserId, useUserSubscription } from '../../../../client/contexts/UserContext'; -import { useEndpoint, useMethod } from '../../../../client/contexts/ServerContext'; -import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext'; -import ThreadSkeleton from './ThreadSkeleton'; -import ThreadView from './ThreadView'; +import { ChatMessage } from '../../../../app/models/client'; +import { normalizeThreadTitle } from '../../../../app/threads/client/lib/normalizeThreadTitle'; +import { roomTypes } from '../../../../app/utils/client'; import { IMessage } from '../../../../definition/IMessage'; import { IRoom } from '../../../../definition/IRoom'; -import { useTabBarOpenUserInfo } from '../../../../client/views/room/providers/ToolboxProvider'; -import { mapMessageFromApi } from '../../../../client/lib/utils/mapMessageFromApi'; +import { useRoute } from '../../../contexts/RouterContext'; +import { useEndpoint, useMethod } from '../../../contexts/ServerContext'; +import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext'; +import { useUserId, useUserSubscription } from '../../../contexts/UserContext'; +import { mapMessageFromApi } from '../../../lib/utils/mapMessageFromApi'; +import { useTabBarOpenUserInfo } from '../providers/ToolboxProvider'; +import ThreadSkeleton from './ThreadSkeleton'; +import ThreadView from './ThreadView'; const subscriptionFields = {}; const useThreadMessage = (tmid: string): IMessage => { - const [message, setMessage] = useState(() => Tracker.nonreactive(() => ChatMessage.findOne({ _id: tmid }))); + const [message, setMessage] = useState(() => + Tracker.nonreactive(() => ChatMessage.findOne({ _id: tmid })), + ); const getMessage = useEndpoint('GET', 'chat.getMessage'); - const getMessageParsed = useCallback<(params: { msgId: IMessage['_id'] }) => Promise>(async (params) => { - const { message } = await getMessage(params); - return mapMessageFromApi(message); - }, [getMessage]); + const getMessageParsed = useCallback<(params: { msgId: IMessage['_id'] }) => Promise>( + async (params) => { + const { message } = await getMessage(params); + return mapMessageFromApi(message); + }, + [getMessage], + ); useEffect(() => { const computation = Tracker.autorun(async (computation) => { - const msg = ChatMessage.findOne({ _id: tmid }) || await getMessageParsed({ msgId: tmid }); + const msg = ChatMessage.findOne({ _id: tmid }) || (await getMessageParsed({ msgId: tmid })); if (!msg || computation.stopped) { return; } setMessage((prevMsg) => { - if (!prevMsg || prevMsg._id !== msg._id || prevMsg._updatedAt?.getTime() !== msg._updatedAt?.getTime()) { + if ( + !prevMsg || + prevMsg._id !== msg._id || + prevMsg._updatedAt?.getTime() !== msg._updatedAt?.getTime() + ) { return msg; } @@ -58,12 +67,7 @@ const ThreadComponent: FC<{ jump: unknown; room: IRoom; onClickBack: (e: unknown) => void; -}> = ({ - mid, - jump, - room, - onClickBack, -}) => { +}> = ({ mid, jump, room, onClickBack }) => { const subscription = useUserSubscription(room._id, subscriptionFields); const channelRoute = useRoute(roomTypes.getConfig(room.t).route.name); const threadMessage = useThreadMessage(mid); @@ -73,7 +77,10 @@ const ThreadComponent: FC<{ const ref = useRef(null); const uid = useUserId(); - const headerTitle = useMemo(() => (threadMessage ? normalizeThreadTitle(threadMessage) : null), [threadMessage]); + const headerTitle = useMemo( + () => (threadMessage ? normalizeThreadTitle(threadMessage) : null), + [threadMessage], + ); const [expanded, setExpand] = useLocalStorage('expand-threads', false); const following = !uid ? false : threadMessage?.replies?.includes(uid) ?? false; @@ -81,21 +88,24 @@ const ThreadComponent: FC<{ const followMessage = useMethod('followMessage'); const unfollowMessage = useMethod('unfollowMessage'); - const setFollowing = useCallback<(following: boolean) => void>(async (following) => { - try { - if (following) { - await followMessage({ mid }); - return; - } + const setFollowing = useCallback<(following: boolean) => void>( + async (following) => { + try { + if (following) { + await followMessage({ mid }); + return; + } - await unfollowMessage({ mid }); - } catch (error) { - dispatchToastMessage({ - type: 'error', - message: error, - }); - } - }, [dispatchToastMessage, followMessage, unfollowMessage, mid]); + await unfollowMessage({ mid }); + } catch (error) { + dispatchToastMessage({ + type: 'error', + message: error, + }); + } + }, + [dispatchToastMessage, followMessage, unfollowMessage, mid], + ); const handleClose = useCallback(() => { channelRoute.push(room.t === 'd' ? { rid: room._id } : { name: room.name || room._id }); @@ -142,16 +152,18 @@ const ThreadComponent: FC<{ return ; } - return setExpand(!expanded)} - onToggleFollow={(following): void => setFollowing(!following)} - onClose={handleClose} - onClickBack={onClickBack} - />; + return ( + setExpand(!expanded)} + onToggleFollow={(following): void => setFollowing(!following)} + onClose={handleClose} + onClickBack={onClickBack} + /> + ); }; export default ThreadComponent; diff --git a/client/views/room/threads/ThreadSkeleton.tsx b/client/views/room/threads/ThreadSkeleton.tsx new file mode 100644 index 0000000000000..1b554a2bce5b6 --- /dev/null +++ b/client/views/room/threads/ThreadSkeleton.tsx @@ -0,0 +1,49 @@ +import { Modal, Box } from '@rocket.chat/fuselage'; +import React, { FC, useMemo } from 'react'; + +import VerticalBar from '../../../components/VerticalBar'; + +type ThreadSkeletonProps = { + expanded: boolean; + onClose: () => void; +}; + +const ThreadSkeleton: FC = ({ expanded, onClose }) => { + const style = useMemo( + () => + document.dir === 'rtl' + ? { + left: 0, + borderTopRightRadius: 4, + } + : { + right: 0, + borderTopLeftRadius: 4, + }, + [], + ); + + return ( + <> + {expanded && } + + + + + ); +}; + +export default ThreadSkeleton; diff --git a/client/views/room/threads/ThreadView.tsx b/client/views/room/threads/ThreadView.tsx new file mode 100644 index 0000000000000..bf77b6bb938a8 --- /dev/null +++ b/client/views/room/threads/ThreadView.tsx @@ -0,0 +1,110 @@ +import { Modal, Box } from '@rocket.chat/fuselage'; +import React, { ComponentProps, useCallback, useMemo, forwardRef } from 'react'; + +import VerticalBar from '../../../components/VerticalBar'; +import { useTranslation } from '../../../contexts/TranslationContext'; +import { useLayoutContextualBarExpanded } from '../../../providers/LayoutProvider'; + +type ThreadViewProps = ComponentProps & { + title: string; + expanded: boolean; + following: boolean; + onToggleExpand: (expanded: boolean) => void; + onToggleFollow: (following: boolean) => void; + onClose: () => void; + onClickBack: (e: unknown) => void; +}; + +const ThreadView = forwardRef(function ThreadView( + { title, expanded, following, onToggleExpand, onToggleFollow, onClose, onClickBack }, + ref, +) { + const hasExpand = useLayoutContextualBarExpanded(); + + const style = useMemo( + () => + document.dir === 'rtl' + ? { + left: 0, + borderTopRightRadius: 4, + } + : { + right: 0, + borderTopLeftRadius: 4, + }, + [], + ); + + const t = useTranslation(); + + const expandLabel = expanded ? t('Collapse') : t('Expand'); + const expandIcon = expanded ? 'arrow-collapse' : 'arrow-expand'; + + const handleExpandActionClick = useCallback(() => { + onToggleExpand(expanded); + }, [expanded, onToggleExpand]); + + const followLabel = following ? t('Following') : t('Not_Following'); + const followIcon = following ? 'bell' : 'bell-off'; + + const handleFollowActionClick = useCallback(() => { + onToggleFollow(following); + }, [following, onToggleFollow]); + + return ( + <> + {hasExpand && expanded && } + + + + + {onClickBack && ( + + )} + + {hasExpand && ( + + )} + + + + + + + + + + ); +}); + +export default ThreadView;