diff --git a/apps/meteor/app/threads/client/index.ts b/apps/meteor/app/threads/client/index.ts index 160fd43a774b..98c219a8837f 100644 --- a/apps/meteor/app/threads/client/index.ts +++ b/apps/meteor/app/threads/client/index.ts @@ -1,4 +1,3 @@ import './messageAction/follow'; -import './messageAction/unfollow'; import './messageAction/replyInThread'; import './threads.css'; diff --git a/apps/meteor/app/threads/client/messageAction/unfollow.ts b/apps/meteor/app/threads/client/messageAction/unfollow.ts deleted file mode 100644 index 853e2adf535f..000000000000 --- a/apps/meteor/app/threads/client/messageAction/unfollow.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; - -import { dispatchToastMessage } from '../../../../client/lib/toast'; -import { callWithErrorHandling } from '../../../../client/lib/utils/callWithErrorHandling'; -import { Messages } from '../../../models/client'; -import { settings } from '../../../settings/client'; -import { MessageAction } from '../../../ui-utils/client'; -import { t } from '../../../utils/lib/i18n'; - -Meteor.startup(() => { - Tracker.autorun(() => { - if (!settings.get('Threads_enabled')) { - return MessageAction.removeButton('unfollow-message'); - } - MessageAction.addButton({ - id: 'unfollow-message', - icon: 'bell-off', - label: 'Unfollow_message', - type: 'interaction', - context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], - async action(_, { message }) { - if (!message) { - return; - } - - await callWithErrorHandling('unfollowMessage', { mid: message._id }); - dispatchToastMessage({ - type: 'success', - message: t('You_unfollowed_this_message'), - }); - }, - condition({ message: { _id, tmid, replies = [] }, user, context }) { - if (tmid || context) { - const parentMessage = Messages.findOne({ _id: tmid || _id }, { fields: { replies: 1 } }); - if (parentMessage) { - replies = parentMessage.replies || []; - } - } - return user?._id ? replies.includes(user._id) : false; - }, - order: 2, - group: 'menu', - }); - }); -}); diff --git a/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx b/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx index df003d28ee48..3d0fb11bfebb 100644 --- a/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx +++ b/apps/meteor/client/components/message/toolbar/MessageToolbar.tsx @@ -15,6 +15,7 @@ import { useNewDiscussionMessageAction } from './useNewDiscussionMessageAction'; import { usePermalinkStar } from './usePermalinkStar'; import { usePinMessageAction } from './usePinMessageAction'; import { useStarMessageAction } from './useStarMessageAction'; +import { useUnFollowMessageAction } from './useUnFollowMessageAction'; import { useUnstarMessageAction } from './useUnstarMessageAction'; import { useWebDAVMessageAction } from './useWebDAVMessageAction'; import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/MessageAction'; @@ -98,6 +99,7 @@ const MessageToolbar = ({ useStarMessageAction(message, { room, user }); useUnstarMessageAction(message, { room, user }); usePermalinkStar(message, { subscription, user }); + useUnFollowMessageAction(message, { room, user, context }); useJumpToMessageContextAction(message, { id: 'jump-to-message', diff --git a/apps/meteor/client/components/message/toolbar/useUnFollowMessageAction.ts b/apps/meteor/client/components/message/toolbar/useUnFollowMessageAction.ts new file mode 100644 index 000000000000..f54f25a1d00b --- /dev/null +++ b/apps/meteor/client/components/message/toolbar/useUnFollowMessageAction.ts @@ -0,0 +1,77 @@ +import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { useSetting, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { useQueryClient } from '@tanstack/react-query'; +import { useEffect } from 'react'; + +import { Messages } from '../../../../app/models/client'; +import { MessageAction } from '../../../../app/ui-utils/client'; +import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/MessageAction'; +import { t } from '../../../../app/utils/lib/i18n'; +import { useReactiveQuery } from '../../../hooks/useReactiveQuery'; +import { roomsQueryKeys } from '../../../lib/queryKeys'; +import { useToggleFollowingThreadMutation } from '../../../views/room/contextualBar/Threads/hooks/useToggleFollowingThreadMutation'; + +export const useUnFollowMessageAction = ( + message: IMessage, + { room, user, context }: { room: IRoom; user: IUser | undefined; context: MessageActionContext }, +) => { + const threadsEnabled = useSetting('Threads_enabled'); + + const dispatchToastMessage = useToastMessageDispatch(); + + const queryClient = useQueryClient(); + + const { mutate: toggleFollowingThread } = useToggleFollowingThreadMutation({ + onSuccess: () => { + dispatchToastMessage({ + type: 'success', + message: t('You_unfollowed_this_message'), + }); + }, + }); + + const { tmid, _id } = message; + const messageQuery = useReactiveQuery( + roomsQueryKeys.message(message.rid, message._id), + () => Messages.findOne({ _id: tmid || _id }, { fields: { replies: 1 } }) ?? null, + ); + + useEffect(() => { + if (!message || !threadsEnabled || isOmnichannelRoom(room)) { + return; + } + + let { replies } = message; + + if (tmid || context) { + const parentMessage = messageQuery.data; + if (parentMessage) { + replies = parentMessage.replies || []; + } + } + + if (!user?._id) { + return; + } + + if (!replies?.includes(user._id)) { + return; + } + + MessageAction.addButton({ + id: 'unfollow-message', + icon: 'bell-off', + label: 'Unfollow_message', + type: 'interaction', + context: ['message', 'message-mobile', 'threads', 'federated', 'videoconf', 'videoconf-threads'], + action() { + toggleFollowingThread({ tmid: tmid || _id, follow: false, rid: room._id }); + }, + order: 1, + group: 'menu', + }); + + return () => MessageAction.removeButton('unfollow-message'); + }, [_id, context, message, messageQuery.data, queryClient, room, threadsEnabled, tmid, toggleFollowingThread, user]); +}; diff --git a/apps/meteor/client/lib/queryKeys.ts b/apps/meteor/client/lib/queryKeys.ts index 44273b60f48a..40382fcd6bef 100644 --- a/apps/meteor/client/lib/queryKeys.ts +++ b/apps/meteor/client/lib/queryKeys.ts @@ -10,6 +10,7 @@ export const roomsQueryKeys = { messageActions: (rid: IRoom['_id'], mid: IMessage['_id']) => [...roomsQueryKeys.message(rid, mid), 'actions'] as const, messageActionsWithParameters: (rid: IRoom['_id'], message: IMessage | Serialized) => [...roomsQueryKeys.messageActions(rid, message._id), message] as const, + threads: (rid: IRoom['_id']) => [...roomsQueryKeys.room(rid), 'threads'] as const, }; export const subscriptionsQueryKeys = { diff --git a/apps/meteor/client/views/room/contextualBar/Threads/hooks/useToggleFollowingThreadMutation.ts b/apps/meteor/client/views/room/contextualBar/Threads/hooks/useToggleFollowingThreadMutation.ts index c4bac8f81f52..e859b1f8e2a2 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/hooks/useToggleFollowingThreadMutation.ts +++ b/apps/meteor/client/views/room/contextualBar/Threads/hooks/useToggleFollowingThreadMutation.ts @@ -3,6 +3,8 @@ import { useEndpoint } from '@rocket.chat/ui-contexts'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { roomsQueryKeys } from '../../../../../lib/queryKeys'; + // TODO: its core should be moved to the ChatContext type UseToggleFollowingThreadMutationVariables = { @@ -31,7 +33,8 @@ export const useToggleFollowingThreadMutation = ( { ...options, onSuccess: async (data, variables, context) => { - await queryClient.invalidateQueries(['rooms', variables.rid, 'threads']); + await queryClient.invalidateQueries(roomsQueryKeys.threads(variables.rid)); + await queryClient.invalidateQueries(roomsQueryKeys.message(variables.rid, variables.tmid)); return options?.onSuccess?.(data, variables, context); }, }, diff --git a/yarn.lock b/yarn.lock index a2570ccdbfe9..ec79e4275b8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8765,10 +8765,10 @@ __metadata: "@rocket.chat/icons": "*" "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-avatar": 9.0.0-rc.0 - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-avatar": 9.0.0-rc.1 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 "@rocket.chat/ui-kit": 0.37.0 - "@rocket.chat/ui-video-conf": 13.0.0-rc.0 + "@rocket.chat/ui-video-conf": 13.0.0-rc.1 "@tanstack/react-query": "*" react: ~17.0.2 react-dom: "*" @@ -8853,8 +8853,8 @@ __metadata: "@rocket.chat/fuselage-tokens": "*" "@rocket.chat/message-parser": 0.31.31 "@rocket.chat/styled": "*" - "@rocket.chat/ui-client": 13.0.0-rc.0 - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-client": 13.0.0-rc.1 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 katex: "*" react: "*" languageName: unknown @@ -10089,7 +10089,7 @@ __metadata: typescript: "npm:~5.7.2" peerDependencies: "@rocket.chat/fuselage": "*" - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 react: ~17.0.2 languageName: unknown linkType: soft @@ -10139,8 +10139,8 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-avatar": 9.0.0-rc.0 - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-avatar": 9.0.0-rc.1 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 react: "*" react-i18next: "*" languageName: unknown @@ -10310,8 +10310,8 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-avatar": 9.0.0-rc.0 - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-avatar": 9.0.0-rc.1 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 react: ~17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -10367,9 +10367,9 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-avatar": 9.0.0-rc.0 - "@rocket.chat/ui-client": 13.0.0-rc.0 - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-avatar": 9.0.0-rc.1 + "@rocket.chat/ui-client": 13.0.0-rc.1 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 react: ~17.0.2 react-aria: ~3.23.1 react-dom: ^17.0.2 @@ -10457,7 +10457,7 @@ __metadata: peerDependencies: "@rocket.chat/layout": "*" "@rocket.chat/tools": 0.2.2 - "@rocket.chat/ui-contexts": 13.0.0-rc.0 + "@rocket.chat/ui-contexts": 13.0.0-rc.1 "@tanstack/react-query": "*" react: "*" react-hook-form: "*"