diff --git a/apps/meteor/client/hooks/useHideRoomAction.tsx b/apps/meteor/client/hooks/useHideRoomAction.tsx new file mode 100644 index 00000000000..51b07cdd046 --- /dev/null +++ b/apps/meteor/client/hooks/useHideRoomAction.tsx @@ -0,0 +1,95 @@ +import type { RoomType } from '@rocket.chat/core-typings'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useSetModal, useToastMessageDispatch, useRouter, useUserId } from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useDontAskAgain } from './useDontAskAgain'; +import { UiTextContext } from '../../definition/IRoomTypeConfig'; +import { GenericModalDoNotAskAgain } from '../components/GenericModal'; +import { updateSubscription } from '../lib/mutationEffects/updateSubscription'; +import { roomCoordinator } from '../lib/rooms/roomCoordinator'; + +type HideRoomProps = { + rid: string; + type: RoomType; + name: string; +}; + +type HideRoomOptions = { + redirect?: boolean; +}; + +const CLOSE_ENDPOINTS_BY_ROOM_TYPE = { + p: '/v1/groups.close', // private + c: '/v1/channels.close', // channel + d: '/v1/im.close', // direct message + v: '/v1/channels.close', // omnichannel voip + l: '/v1/channels.close', // livechat +} as const; + +export const useHideRoomAction = ({ rid: roomId, type, name }: HideRoomProps, { redirect = true }: HideRoomOptions = {}) => { + const { t } = useTranslation(); + const setModal = useSetModal(); + const closeModal = useEffectEvent(() => setModal()); + const dispatchToastMessage = useToastMessageDispatch(); + const dontAskHideRoom = useDontAskAgain('hideRoom'); + const router = useRouter(); + const userId = useUserId(); + + const hideRoomEndpoint = useEndpoint('POST', CLOSE_ENDPOINTS_BY_ROOM_TYPE[type]); + + const hideRoom = useMutation({ + mutationFn: () => hideRoomEndpoint({ roomId }), + onMutate: async () => { + closeModal(); + + if (userId) { + return updateSubscription(roomId, userId, { alert: false, open: false }); + } + }, + onSuccess: () => { + if (redirect) { + router.navigate('/home'); + } + }, + onError: async (error, _, rollbackDocument) => { + dispatchToastMessage({ type: 'error', message: error }); + + if (userId && rollbackDocument) { + const { alert, open } = rollbackDocument; + updateSubscription(roomId, userId, { alert, open }); + } + }, + }); + + const handleHide = useEffectEvent(async () => { + const warnText = roomCoordinator.getRoomDirectives(type).getUiText(UiTextContext.HIDE_WARNING); + + if (dontAskHideRoom) { + hideRoom.mutate(); + return; + } + + setModal( + hideRoom.mutate()} + dontAskAgain={{ + action: 'hideRoom', + label: t('Hide_room'), + }} + > + {t(warnText as TranslationKey, { postProcess: 'sprintf', sprintf: [name] })} + , + ); + }); + + return handleHide; +}; diff --git a/apps/meteor/client/lib/mutationEffects/updateSubscription.ts b/apps/meteor/client/lib/mutationEffects/updateSubscription.ts new file mode 100644 index 00000000000..4decdbb4ccb --- /dev/null +++ b/apps/meteor/client/lib/mutationEffects/updateSubscription.ts @@ -0,0 +1,11 @@ +import type { ISubscription } from '@rocket.chat/core-typings'; + +import { Subscriptions } from '../../../app/models/client'; + +export const updateSubscription = (roomId: string, userId: string, data: Partial) => { + const oldDocument = Subscriptions.findOne({ 'rid': roomId, 'u._id': userId }); + + Subscriptions.update({ 'rid': roomId, 'u._id': userId }, { $set: data }); + + return oldDocument; +}; diff --git a/apps/meteor/client/methods/hideRoom.ts b/apps/meteor/client/methods/hideRoom.ts deleted file mode 100644 index 0f45717fd49..00000000000 --- a/apps/meteor/client/methods/hideRoom.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Meteor } from 'meteor/meteor'; - -import { Subscriptions } from '../../app/models/client'; - -Meteor.methods({ - async hideRoom(rid) { - if (!Meteor.userId()) { - return 0; - } - - return Subscriptions.update( - { - rid, - 'u._id': Meteor.userId(), - }, - { - $set: { - alert: false, - open: false, - }, - }, - ); - }, -}); diff --git a/apps/meteor/client/methods/index.ts b/apps/meteor/client/methods/index.ts index 7be75a2707f..0cdd4e54be5 100644 --- a/apps/meteor/client/methods/index.ts +++ b/apps/meteor/client/methods/index.ts @@ -1,4 +1,3 @@ -import './hideRoom'; import './openRoom'; import './pinMessage'; import './unpinMessage'; diff --git a/apps/meteor/client/sidebar/RoomMenu.tsx b/apps/meteor/client/sidebar/RoomMenu.tsx index 4baeb3fb824..4e20baf8929 100644 --- a/apps/meteor/client/sidebar/RoomMenu.tsx +++ b/apps/meteor/client/sidebar/RoomMenu.tsx @@ -19,9 +19,8 @@ import React, { memo, useMemo } from 'react'; import { LegacyRoomManager } from '../../app/ui-utils/client'; import { UiTextContext } from '../../definition/IRoomTypeConfig'; -import { GenericModalDoNotAskAgain } from '../components/GenericModal'; import WarningModal from '../components/WarningModal'; -import { useDontAskAgain } from '../hooks/useDontAskAgain'; +import { useHideRoomAction } from '../hooks/useHideRoomAction'; import { roomCoordinator } from '../lib/rooms/roomCoordinator'; import { useOmnichannelPrioritiesMenu } from '../omnichannel/hooks/useOmnichannelPrioritiesMenu'; @@ -43,15 +42,6 @@ type RoomMenuProps = { hideDefaultOptions: boolean; }; -const closeEndpoints = { - p: '/v1/groups.close', - c: '/v1/channels.close', - d: '/v1/im.close', - - v: '/v1/channels.close', - l: '/v1/groups.close', -} as const; - const leaveEndpoints = { p: '/v1/groups.leave', c: '/v1/channels.leave', @@ -84,9 +74,6 @@ const RoomMenu = ({ const canFavorite = useSetting('Favorite_Rooms'); const isFavorite = Boolean(subscription?.f); - const dontAskHideRoom = useDontAskAgain('hideRoom'); - - const hideRoom = useEndpoint('POST', closeEndpoints[type]); const readMessages = useEndpoint('POST', '/v1/subscriptions.read'); const toggleFavorite = useEndpoint('POST', '/v1/rooms.favorite'); const leaveRoom = useEndpoint('POST', leaveEndpoints[type]); @@ -103,6 +90,8 @@ const RoomMenu = ({ const queryClient = useQueryClient(); + const handleHide = useHideRoomAction({ rid, type, name }, { redirect: false }); + const canLeave = ((): boolean => { if (type === 'c' && !canLeaveChannel) { return false; @@ -140,40 +129,6 @@ const RoomMenu = ({ ); }); - const handleHide = useMutableCallback(async () => { - const hide = async (): Promise => { - try { - await hideRoom({ roomId: rid }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - closeModal(); - }; - - const warnText = roomCoordinator.getRoomDirectives(type).getUiText(UiTextContext.HIDE_WARNING); - - if (dontAskHideRoom) { - return hide(); - } - - setModal( - - {t(warnText as TranslationKey, name)} - , - ); - }); - const handleToggleRead = useMutableCallback(async () => { try { queryClient.invalidateQueries(['sidebar/search/spotlight']); diff --git a/apps/meteor/client/sidebarv2/RoomMenu.tsx b/apps/meteor/client/sidebarv2/RoomMenu.tsx index 8b031e8cfce..0c593afd683 100644 --- a/apps/meteor/client/sidebarv2/RoomMenu.tsx +++ b/apps/meteor/client/sidebarv2/RoomMenu.tsx @@ -19,9 +19,8 @@ import React, { memo, useMemo } from 'react'; import { LegacyRoomManager } from '../../app/ui-utils/client'; import { UiTextContext } from '../../definition/IRoomTypeConfig'; -import { GenericModalDoNotAskAgain } from '../components/GenericModal'; import WarningModal from '../components/WarningModal'; -import { useDontAskAgain } from '../hooks/useDontAskAgain'; +import { useHideRoomAction } from '../hooks/useHideRoomAction'; import { roomCoordinator } from '../lib/rooms/roomCoordinator'; import { useOmnichannelPrioritiesMenu } from '../omnichannel/hooks/useOmnichannelPrioritiesMenu'; @@ -43,15 +42,6 @@ type RoomMenuProps = { hideDefaultOptions: boolean; }; -const closeEndpoints = { - p: '/v1/groups.close', - c: '/v1/channels.close', - d: '/v1/im.close', - - v: '/v1/channels.close', - l: '/v1/groups.close', -} as const; - const leaveEndpoints = { p: '/v1/groups.leave', c: '/v1/channels.leave', @@ -84,9 +74,6 @@ const RoomMenu = ({ const canFavorite = useSetting('Favorite_Rooms'); const isFavorite = Boolean(subscription?.f); - const dontAskHideRoom = useDontAskAgain('hideRoom'); - - const hideRoom = useEndpoint('POST', closeEndpoints[type]); const readMessages = useEndpoint('POST', '/v1/subscriptions.read'); const toggleFavorite = useEndpoint('POST', '/v1/rooms.favorite'); const leaveRoom = useEndpoint('POST', leaveEndpoints[type]); @@ -103,6 +90,8 @@ const RoomMenu = ({ const queryClient = useQueryClient(); + const handleHide = useHideRoomAction({ rid, type, name }, { redirect: false }); + const canLeave = ((): boolean => { if (type === 'c' && !canLeaveChannel) { return false; @@ -140,40 +129,6 @@ const RoomMenu = ({ ); }); - const handleHide = useEffectEvent(async () => { - const hide = async (): Promise => { - try { - await hideRoom({ roomId: rid }); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - closeModal(); - }; - - const warnText = roomCoordinator.getRoomDirectives(type).getUiText(UiTextContext.HIDE_WARNING); - - if (dontAskHideRoom) { - return hide(); - } - - setModal( - - {t(warnText as TranslationKey, name)} - , - ); - }); - const handleToggleRead = useEffectEvent(async () => { try { queryClient.invalidateQueries(['sidebar/search/spotlight']); diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx deleted file mode 100644 index c381359d52c..00000000000 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetModal, useToastMessageDispatch, useMethod, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; -import React from 'react'; - -import { UiTextContext } from '../../../../../../../definition/IRoomTypeConfig'; -import WarningModal from '../../../../../../components/WarningModal'; -import { roomCoordinator } from '../../../../../../lib/rooms/roomCoordinator'; - -export const useRoomHide = (room: IRoom) => { - const t = useTranslation(); - const setModal = useSetModal(); - const dispatchToastMessage = useToastMessageDispatch(); - const hideRoom = useMethod('hideRoom'); - const router = useRouter(); - - const handleHide = useMutableCallback(async () => { - const hide = async () => { - try { - await hideRoom(room._id); - router.navigate('/home'); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - setModal(null); - }; - - const warnText = roomCoordinator.getRoomDirectives(room.t).getUiText(UiTextContext.HIDE_WARNING); - - setModal( - setModal(null)} - cancelText={t('Cancel')} - confirm={hide} - />, - ); - }); - - return handleHide; -}; diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts b/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts index 592093fa1ae..201e0131646 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts +++ b/apps/meteor/client/views/room/contextualBar/Info/hooks/useRoomActions.ts @@ -3,9 +3,9 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useRoomConvertToTeam } from './actions/useRoomConvertToTeam'; -import { useRoomHide } from './actions/useRoomHide'; import { useRoomLeave } from './actions/useRoomLeave'; import { useRoomMoveToTeam } from './actions/useRoomMoveToTeam'; +import { useHideRoomAction } from '../../../../../hooks/useHideRoomAction'; import { useDeleteRoom } from '../../../../hooks/roomActions/useDeleteRoom'; type UseRoomActionsOptions = { @@ -18,11 +18,12 @@ export const useRoomActions = (room: IRoom, options: UseRoomActionsOptions) => { const { onClickEnterRoom, onClickEdit, resetState } = options; const { t } = useTranslation(); - const handleHide = useRoomHide(room); + const handleLeave = useRoomLeave(room); const { handleDelete, canDeleteRoom } = useDeleteRoom(room, { reload: resetState }); const handleMoveToTeam = useRoomMoveToTeam(room); const handleConvertToTeam = useRoomConvertToTeam(room); + const handleHide = useHideRoomAction({ rid: room._id, type: room.t, name: room.name ?? '' }); return useMemo(() => { const memoizedActions = { diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.tsx b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.tsx index a90e5b98a3a..4136ebcea61 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.tsx +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.tsx @@ -1,17 +1,13 @@ import type { IRoom, Serialized } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetModal, useToastMessageDispatch, useUserId, usePermission, useMethod, useRouter } from '@rocket.chat/ui-contexts'; +import { useSetModal, useToastMessageDispatch, useUserId, usePermission, useRouter } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import LeaveTeam from './LeaveTeam'; import TeamsInfo from './TeamsInfo'; -import { UiTextContext } from '../../../../../definition/IRoomTypeConfig'; -import { GenericModalDoNotAskAgain } from '../../../../components/GenericModal'; -import { useDontAskAgain } from '../../../../hooks/useDontAskAgain'; import { useEndpointAction } from '../../../../hooks/useEndpointAction'; -import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator'; +import { useHideRoomAction } from '../../../../hooks/useHideRoomAction'; import { useDeleteRoom } from '../../../hooks/roomActions/useDeleteRoom'; import { useRoom } from '../../../room/contexts/RoomContext'; import { useRoomToolbox } from '../../../room/contexts/RoomToolboxContext'; @@ -27,8 +23,6 @@ const TeamsInfoWithLogic = ({ openEditing }: TeamsInfoWithLogicProps) => { const { t } = useTranslation(); const userId = useUserId(); - const dontAskHideRoom = useDontAskAgain('hideRoom'); - const dispatchToastMessage = useToastMessageDispatch(); const setModal = useSetModal(); const closeModal = useMutableCallback(() => setModal()); @@ -36,7 +30,7 @@ const TeamsInfoWithLogic = ({ openEditing }: TeamsInfoWithLogicProps) => { const leaveTeam = useEndpointAction('POST', '/v1/teams.leave'); const convertTeamToChannel = useEndpointAction('POST', '/v1/teams.convertToChannel'); - const hideTeam = useMethod('hideRoom'); + const hideTeam = useHideRoomAction({ rid: room._id, type: room.t, name: room.name ?? '' }); const router = useRouter(); @@ -68,42 +62,6 @@ const TeamsInfoWithLogic = ({ openEditing }: TeamsInfoWithLogicProps) => { setModal(); }); - const handleHide = useMutableCallback(async () => { - const hide = async () => { - try { - await hideTeam(room._id); - router.navigate('/home'); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } finally { - closeModal(); - } - }; - - const warnText = roomCoordinator.getRoomDirectives(room.t).getUiText(UiTextContext.HIDE_WARNING) as TranslationKey; - - if (dontAskHideRoom) { - return hide(); - } - - setModal( - - {t(warnText, { postProcess: 'sprintf', sprintf: [room.fname] })} - , - ); - }); - const onClickViewChannels = useCallback(() => openTab('team-channels'), [openTab]); const onClickConvertToChannel = useMutableCallback(() => { @@ -134,7 +92,7 @@ const TeamsInfoWithLogic = ({ openEditing }: TeamsInfoWithLogicProps) => { onClickClose={closeTab} onClickDelete={canDeleteRoom ? handleDelete : undefined} onClickLeave={onClickLeave} - onClickHide={handleHide} + onClickHide={hideTeam} onClickViewChannels={onClickViewChannels} onClickConvertToChannel={canEdit ? onClickConvertToChannel : undefined} />