diff --git a/.changeset/easy-laws-talk.md b/.changeset/easy-laws-talk.md new file mode 100644 index 0000000000000..51ff0a2702f2b --- /dev/null +++ b/.changeset/easy-laws-talk.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes non-deterministic comparator in team's channel desertion table diff --git a/apps/meteor/app/api/server/v1/invites.ts b/apps/meteor/app/api/server/v1/invites.ts index 0e0ea055160ee..86a971a1b2d3d 100644 --- a/apps/meteor/app/api/server/v1/invites.ts +++ b/apps/meteor/app/api/server/v1/invites.ts @@ -250,6 +250,7 @@ const invites = API.v1 }, async function action() { const { token } = this.bodyParams; + // eslint-disable-next-line react-hooks/rules-of-hooks return API.v1.success(await useInviteToken(this.userId, token)); }, ) diff --git a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts index bab1ed08213e1..d3207b9205930 100644 --- a/apps/meteor/app/autotranslate/client/lib/autotranslate.ts +++ b/apps/meteor/app/autotranslate/client/lib/autotranslate.ts @@ -69,6 +69,16 @@ export const AutoTranslate = { } } + if (attachment.description && attachment.translations && attachment.translations[language]) { + attachment.translations.original = attachment.description; + + if (autoTranslateShowInverse) { + attachment.description = attachment.translations.original; + } else { + attachment.description = attachment.translations[language]; + } + } + if (attachment.attachments && attachment.attachments.length > 0) { // @ts-expect-error - not sure what to do with this attachment.attachments = this.translateAttachments(attachment.attachments, language); diff --git a/apps/meteor/app/autotranslate/server/autotranslate.ts b/apps/meteor/app/autotranslate/server/autotranslate.ts index 3e04f6d39eb30..2f91e02463d58 100644 --- a/apps/meteor/app/autotranslate/server/autotranslate.ts +++ b/apps/meteor/app/autotranslate/server/autotranslate.ts @@ -320,7 +320,7 @@ export abstract class AutoTranslate { if (message.attachments && message.attachments.length > 0) { setImmediate(async () => { for (const [index, attachment] of message.attachments?.entries() ?? []) { - if (attachment.text) { + if (attachment.description || attachment.text) { // Removes the initial link `[ ](quoterl)` from quote message before translation const translatedText = attachment?.text?.replace(/\[(.*?)\]\(.*?\)/g, '$1') || attachment?.text; const attachmentMessage = { ...attachment, text: translatedText }; diff --git a/apps/meteor/app/autotranslate/server/deeplTranslate.ts b/apps/meteor/app/autotranslate/server/deeplTranslate.ts index 35f73e1755da6..d76a7ea2e4901 100644 --- a/apps/meteor/app/autotranslate/server/deeplTranslate.ts +++ b/apps/meteor/app/autotranslate/server/deeplTranslate.ts @@ -196,7 +196,7 @@ class DeeplAutoTranslate extends AutoTranslate { params: { auth_key: this.apiKey, target_lang: language, - text: attachment.text || '', + text: attachment.description || attachment.text || '', }, }); if (!result.ok) { diff --git a/apps/meteor/app/autotranslate/server/googleTranslate.ts b/apps/meteor/app/autotranslate/server/googleTranslate.ts index 53b9bb7c1d5ea..9667ae53c967a 100644 --- a/apps/meteor/app/autotranslate/server/googleTranslate.ts +++ b/apps/meteor/app/autotranslate/server/googleTranslate.ts @@ -195,7 +195,7 @@ class GoogleAutoTranslate extends AutoTranslate { key: this.apiKey, target: language, format: 'text', - q: attachment.text || '', + q: attachment.description || attachment.text || '', }, }); if (!result.ok) { diff --git a/apps/meteor/app/autotranslate/server/msTranslate.ts b/apps/meteor/app/autotranslate/server/msTranslate.ts index 6508734a1c0da..ddb345d3c895a 100644 --- a/apps/meteor/app/autotranslate/server/msTranslate.ts +++ b/apps/meteor/app/autotranslate/server/msTranslate.ts @@ -192,7 +192,7 @@ class MsAutoTranslate extends AutoTranslate { return this._translate( [ { - Text: attachment.text || '', + Text: attachment.description || attachment.text || '', }, ], targetLanguages, diff --git a/apps/meteor/app/lib/server/functions/notifications/email.js b/apps/meteor/app/lib/server/functions/notifications/email.js index 4de543abb7dd6..c41445fcf55b2 100644 --- a/apps/meteor/app/lib/server/functions/notifications/email.js +++ b/apps/meteor/app/lib/server/functions/notifications/email.js @@ -77,8 +77,13 @@ export async function getEmailContent({ message, user, room }) { } if (hasFiles) { - const fileParts = files.map((file) => { - return escapeHTML(file.name); + const attachments = message.attachments || []; + const fileParts = files.map((file, index) => { + let part = escapeHTML(file.name); + if (attachments[index]?.description) { + part += `

${escapeHTML(attachments[index].description)}`; + } + return part; }); contentParts.push(fileParts.join('

')); } diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts index 498cef1624624..7a089abba0815 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts @@ -195,6 +195,8 @@ export const sendNotification = async ({ const firstAttachment = message.attachments?.length && message.attachments.shift(); if (firstAttachment) { + firstAttachment.description = + typeof firstAttachment.description === 'string' ? emojione.shortnameToUnicode(firstAttachment.description) : undefined; firstAttachment.text = typeof firstAttachment.text === 'string' ? emojione.shortnameToUnicode(firstAttachment.text) : undefined; } diff --git a/apps/meteor/app/lib/server/methods/updateMessage.ts b/apps/meteor/app/lib/server/methods/updateMessage.ts index 833b4403c0eca..45ba42f25f000 100644 --- a/apps/meteor/app/lib/server/methods/updateMessage.ts +++ b/apps/meteor/app/lib/server/methods/updateMessage.ts @@ -34,7 +34,7 @@ export async function executeUpdateMessage( // IF the message has custom fields, always update // Ideally, we'll compare the custom fields to check for change, but since we don't know the shape of // custom fields, as it's user defined, we're gonna update - const msgText = originalMessage.msg; + const msgText = originalMessage?.attachments?.[0]?.description ?? originalMessage.msg; if (msgText === message.msg && !previewUrls && !message.customFields) { return; } @@ -86,6 +86,13 @@ export async function executeUpdateMessage( } await canSendMessageAsync(message.rid, { uid: user._id, username: user.username ?? undefined, ...user }); + // It is possible to have an empty array as the attachments property, so ensure both things exist + if (originalMessage.attachments && originalMessage.attachments.length > 0 && originalMessage.attachments[0].description !== undefined) { + originalMessage.attachments[0].description = message.msg; + message.attachments = originalMessage.attachments; + message.msg = originalMessage.msg; + } + message.u = originalMessage.u; return updateMessage(message, user, originalMessage, previewUrls); diff --git a/apps/meteor/app/livechat/server/lib/sendTranscript.ts b/apps/meteor/app/livechat/server/lib/sendTranscript.ts index f52ac3f516710..199275f6a516b 100644 --- a/apps/meteor/app/livechat/server/lib/sendTranscript.ts +++ b/apps/meteor/app/livechat/server/lib/sendTranscript.ts @@ -108,7 +108,7 @@ export async function sendTranscript({ const messageType = MessageTypes.getType(message); - const messageContent = messageType?.system + let messageContent = messageType?.system ? DOMPurify.sanitize(` ${messageType.text(i18n.cloneInstance({ interpolation: { escapeValue: false } }).t, message)}}`) : escapeHtml(message.msg); @@ -116,6 +116,9 @@ export async function sendTranscript({ let filesHTML = ''; if (message.attachments && message.attachments?.length > 0) { + messageContent = message.attachments[0].description || ''; + escapeHtml(messageContent); + for await (const attachment of message.attachments) { if (!isFileAttachment(attachment)) { continue; diff --git a/apps/meteor/app/slackbridge/server/RocketAdapter.ts b/apps/meteor/app/slackbridge/server/RocketAdapter.ts index 5c46d75368dda..100e6991c3b08 100644 --- a/apps/meteor/app/slackbridge/server/RocketAdapter.ts +++ b/apps/meteor/app/slackbridge/server/RocketAdapter.ts @@ -203,11 +203,14 @@ export default class RocketAdapter { if (rocketMessage.file.name) { let fileName = rocketMessage.file.name; - const text = rocketMessage.msg; + let text = rocketMessage.msg; const attachment = this.getMessageAttachment(rocketMessage); if (attachment) { fileName = Meteor.absoluteUrl(attachment.title_link); + if (!text) { + text = attachment.description; + } } await slack.postMessage(slack.getSlackChannel(rocketMessage.rid), { ...rocketMessage, msg: `${text} ${fileName}` }); diff --git a/apps/meteor/app/ui/client/lib/ChatMessages.ts b/apps/meteor/app/ui/client/lib/ChatMessages.ts index 70b64201979ba..a6febf3fdfef9 100644 --- a/apps/meteor/app/ui/client/lib/ChatMessages.ts +++ b/apps/meteor/app/ui/client/lib/ChatMessages.ts @@ -120,7 +120,7 @@ export class ChatMessages implements ChatAPI { }, editMessage: async (message: IMessage, { cursorAtStart = false }: { cursorAtStart?: boolean } = {}) => { this.composer?.uploads.clear(); - const text = (await this.data.getDraft(message._id)) || message.msg; + const text = (await this.data.getDraft(message._id)) || message.attachments?.[0]?.description || message.msg; await this.currentEditingMessage.stop(); diff --git a/apps/meteor/client/apps/gameCenter/GameCenter.tsx b/apps/meteor/client/apps/gameCenter/GameCenter.tsx index 0955710c0a936..b424ad7515c6c 100644 --- a/apps/meteor/client/apps/gameCenter/GameCenter.tsx +++ b/apps/meteor/client/apps/gameCenter/GameCenter.tsx @@ -1,5 +1,5 @@ import type { IExternalComponent } from '@rocket.chat/apps-engine/definition/externalComponent'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoomToolbox } from '@rocket.chat/ui-contexts'; import { useState } from 'react'; import type { MouseEvent } from 'react'; @@ -18,9 +18,9 @@ const GameCenter = () => { const result = useExternalComponentsQuery(); - const handleClose = useEffectEvent(() => closeTab()); + const handleClose = useStableCallback(() => closeTab()); - const handleBack = useEffectEvent((e: MouseEvent) => { + const handleBack = useStableCallback((e: MouseEvent) => { setOpenedGame(undefined); preventSyntheticEvent(e); }); diff --git a/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx b/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx index 8ff91eb9da725..470d041d16fff 100644 --- a/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx +++ b/apps/meteor/client/components/ConfirmOwnerChangeModal.tsx @@ -1,5 +1,5 @@ import { Box } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import type { ComponentPropsWithoutRef } from 'react'; import { Trans } from 'react-i18next'; @@ -18,7 +18,7 @@ const ConfirmOwnerChangeModal = ({ onConfirm, onCancel, }: ConfirmOwnerChangeModalProps) => { - const getChangeOwnerRooms = useEffectEvent(() => { + const getChangeOwnerRooms = useStableCallback(() => { if (shouldChangeOwner.length === 0) { return ''; } @@ -50,7 +50,7 @@ const ConfirmOwnerChangeModal = ({ ); }); - const getRemovedRooms = useEffectEvent(() => { + const getRemovedRooms = useStableCallback(() => { if (shouldBeRemoved.length === 0) { return ''; } diff --git a/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx b/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx index 93502e17905a2..5557b01bf94ff 100644 --- a/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx +++ b/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx @@ -11,7 +11,7 @@ import { FieldRow, FieldError, } from '@rocket.chat/fuselage-forms'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; @@ -69,7 +69,7 @@ const CreateDiscussion = ({ }, }); - const onParentRoomChange = useEffectEvent((room: IRoom | undefined) => { + const onParentRoomChange = useStableCallback((room: IRoom | undefined) => { if (!room) { return; } diff --git a/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx b/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx index 0f85b447d2fdf..d3c37f9e2cc3a 100644 --- a/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx +++ b/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEmbeddedLayout } from '@rocket.chat/ui-client'; import { useLayout, useSession } from '@rocket.chat/ui-contexts'; import { memo } from 'react'; @@ -10,7 +10,7 @@ const SideBarToggler = () => { const isLayoutEmbedded = useEmbeddedLayout(); const unreadMessagesBadge = useSession('unread') as number | string | undefined; - const toggleSidebar = useEffectEvent(() => sidebar.toggle()); + const toggleSidebar = useStableCallback(() => sidebar.toggle()); return ( { + const handleChangeAvatar = useStableCallback(async (file: File) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onloadend = async (): Promise => { @@ -38,7 +38,7 @@ const RoomAvatarEditor = ({ disabled = false, room, roomAvatar, onChangeAvatar } }); const [clickUpload, reset] = useSingleFileInput(handleChangeAvatar); - const clickReset = useEffectEvent(() => { + const clickReset = useStableCallback(() => { reset(); onChangeAvatar(null); }); diff --git a/apps/meteor/client/components/message/content/MessageActions.tsx b/apps/meteor/client/components/message/content/MessageActions.tsx index b67538962e26c..cd037d97cbf75 100644 --- a/apps/meteor/client/components/message/content/MessageActions.tsx +++ b/apps/meteor/client/components/message/content/MessageActions.tsx @@ -1,6 +1,6 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Box, ButtonGroup } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; @@ -22,7 +22,7 @@ type MessageActionsProps = { }; const MessageActions = ({ message, actions }: MessageActionsProps) => { - const runAction = useEffectEvent((action: string) => () => { + const runAction = useStableCallback((action: string) => () => { actionLinks.run(action, message); }); diff --git a/apps/meteor/client/components/message/content/attachments/file/AudioAttachment.tsx b/apps/meteor/client/components/message/content/attachments/file/AudioAttachment.tsx index 9fa94126127e8..227874cfb8eaf 100644 --- a/apps/meteor/client/components/message/content/attachments/file/AudioAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/AudioAttachment.tsx @@ -4,13 +4,17 @@ import { useMediaUrl } from '@rocket.chat/ui-contexts'; import { useMemo } from 'react'; import { useReloadOnError } from './hooks/useReloadOnError'; +import MarkdownText from '../../../../MarkdownText'; import MessageCollapsible from '../../../MessageCollapsible'; +import MessageContentBody from '../../../MessageContentBody'; const AudioAttachment = ({ title, audio_url: url, audio_type: type, audio_size: size, + description, + descriptionMd, title_link: link, title_link_download: hasDownload, collapsed, @@ -21,6 +25,7 @@ const AudioAttachment = ({ return ( <> + {descriptionMd ? : } diff --git a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx index d57325334098e..f46b24d8d5634 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -13,7 +13,9 @@ import { useTranslation } from 'react-i18next'; import { getFileExtension } from '../../../../../../lib/utils/getFileExtension'; import { forAttachmentDownload, registerDownloadForUid } from '../../../../../hooks/useDownloadFromServiceWorker'; +import MarkdownText from '../../../../MarkdownText'; import MessageCollapsible from '../../../MessageCollapsible'; +import MessageContentBody from '../../../MessageContentBody'; import AttachmentSize from '../structure/AttachmentSize'; import { useOpenEncryptedPdf } from './hooks/useOpenEncryptedPdf'; @@ -23,6 +25,8 @@ type GenericFileAttachmentProps = MessageAttachmentBase; const GenericFileAttachment = ({ title, + description, + descriptionMd, title_link: link, title_link_download: hasDownload, size, @@ -81,6 +85,7 @@ const GenericFileAttachment = ({ return ( <> + {descriptionMd ? : } + {descriptionMd ? : } + {descriptionMd ? : } diff --git a/apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx b/apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx index a826fb81c3dd9..720580d8aa30e 100644 --- a/apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent, useSafeRefCallback } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useSafeRefCallback } from '@rocket.chat/fuselage-hooks'; import { useCallback, useRef, useState } from 'react'; const events = ['error', 'stalled', 'play']; @@ -52,7 +52,7 @@ export const useReloadOnError = (url: string, type: 'video' | 'audio') => { const isRecovering = useRef(false); const firstRecoveryAttempted = useRef(false); - const handleMediaURLRecovery = useEffectEvent(async (event: Event) => { + const handleMediaURLRecovery = useStableCallback(async (event: Event) => { if (isRecovering.current) { console.debug(`Media URL recovery already in progress, skipping ${event.type} event`); return; diff --git a/apps/meteor/client/components/message/toolbar/useCopyAction.ts b/apps/meteor/client/components/message/toolbar/useCopyAction.ts index b8275144abca1..1a03dac99936d 100644 --- a/apps/meteor/client/components/message/toolbar/useCopyAction.ts +++ b/apps/meteor/client/components/message/toolbar/useCopyAction.ts @@ -6,8 +6,8 @@ import type { MessageActionConfig } from '../../../../app/ui-utils/client/lib/Me const getMainMessageText = (message: IMessage): IMessage => { const newMessage = { ...message }; - newMessage.msg = newMessage.msg || newMessage.attachments?.[0]?.title || ''; - newMessage.md = newMessage.md || undefined; + newMessage.msg = newMessage.msg || newMessage.attachments?.[0]?.description || newMessage.attachments?.[0]?.title || ''; + newMessage.md = newMessage.md || newMessage.attachments?.[0]?.descriptionMd || undefined; return { ...newMessage }; }; diff --git a/apps/meteor/client/components/message/toolbar/useReportMessageAction.tsx b/apps/meteor/client/components/message/toolbar/useReportMessageAction.tsx index dbbb962032907..ba281c5a1f5a2 100644 --- a/apps/meteor/client/components/message/toolbar/useReportMessageAction.tsx +++ b/apps/meteor/client/components/message/toolbar/useReportMessageAction.tsx @@ -7,8 +7,8 @@ import ReportMessageModal from '../../../views/room/modals/ReportMessageModal'; const getMainMessageText = (message: IMessage): IMessage => { const newMessage = { ...message }; - newMessage.msg = newMessage.msg || newMessage.attachments?.[0]?.title || ''; - newMessage.md = newMessage.md || undefined; + newMessage.msg = newMessage.msg || newMessage.attachments?.[0]?.description || newMessage.attachments?.[0]?.title || ''; + newMessage.md = newMessage.md || newMessage.attachments?.[0]?.descriptionMd || undefined; return { ...newMessage }; }; diff --git a/apps/meteor/client/components/message/variants/RoomMessage.tsx b/apps/meteor/client/components/message/variants/RoomMessage.tsx index 157856ce3b087..114b8405a9e57 100644 --- a/apps/meteor/client/components/message/variants/RoomMessage.tsx +++ b/apps/meteor/client/components/message/variants/RoomMessage.tsx @@ -3,7 +3,7 @@ import { Message, MessageLeftContainer, MessageContainer, CheckBox } from '@rock import { useToggle } from '@rocket.chat/fuselage-hooks'; import { MessageAvatar } from '@rocket.chat/ui-avatar'; import { useUserId, useUserCard } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; +import type { ComponentProps, KeyboardEvent } from 'react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -88,7 +88,7 @@ const RoomMessage = ({ useCountSelected(); - const handleKeyDown = (e: React.KeyboardEvent) => { + const handleKeyDown = (e: KeyboardEvent) => { if (!selecting) return; if (!(e.code === 'Space' || e.code === 'Enter')) return; diff --git a/apps/meteor/client/components/message/variants/SystemMessage.tsx b/apps/meteor/client/components/message/variants/SystemMessage.tsx index 1ca0d4f8b2925..e78117e1552b4 100644 --- a/apps/meteor/client/components/message/variants/SystemMessage.tsx +++ b/apps/meteor/client/components/message/variants/SystemMessage.tsx @@ -17,7 +17,7 @@ import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useUserDisplayName } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPresence, useUserCard } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; +import type { ComponentProps, KeyboardEvent } from 'react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -64,7 +64,7 @@ const SystemMessage = ({ message, showUserAvatar, ...props }: SystemMessageProps useCountSelected(); const buttonProps = useButtonPattern((e) => openUserCard(e, user.username)); - const handleKeyDown = (e: React.KeyboardEvent) => { + const handleKeyDown = (e: KeyboardEvent) => { if (!isSelecting) return; if (!(e.code === 'Space' || e.code === 'Enter')) return; diff --git a/apps/meteor/client/hooks/menuActions/useLeaveRoom.tsx b/apps/meteor/client/hooks/menuActions/useLeaveRoom.tsx index 2700bb676a1a4..021ee36b49b53 100644 --- a/apps/meteor/client/hooks/menuActions/useLeaveRoom.tsx +++ b/apps/meteor/client/hooks/menuActions/useLeaveRoom.tsx @@ -1,5 +1,5 @@ import type { RoomType } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useEndpoint, useRouter, useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -33,7 +33,7 @@ export const useLeaveRoomAction = ({ rid, type, name, roomOpen }: LeaveRoomProps const leaveRoom = useEndpoint('POST', leaveEndpoints[type]); - const handleLeave = useEffectEvent(() => { + const handleLeave = useStableCallback(() => { const leave = async (): Promise => { try { await leaveRoom({ roomId: rid }); diff --git a/apps/meteor/client/hooks/menuActions/useToggleFavoriteAction.ts b/apps/meteor/client/hooks/menuActions/useToggleFavoriteAction.ts index 70284057aee78..ece0eef82a08f 100644 --- a/apps/meteor/client/hooks/menuActions/useToggleFavoriteAction.ts +++ b/apps/meteor/client/hooks/menuActions/useToggleFavoriteAction.ts @@ -1,12 +1,12 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; export const useToggleFavoriteAction = ({ rid, isFavorite }: { rid: IRoom['_id']; isFavorite: boolean }) => { const toggleFavorite = useEndpoint('POST', '/v1/rooms.favorite'); const dispatchToastMessage = useToastMessageDispatch(); - const handleToggleFavorite = useEffectEvent(async () => { + const handleToggleFavorite = useStableCallback(async () => { try { await toggleFavorite({ roomId: rid, favorite: !isFavorite }); } catch (error) { diff --git a/apps/meteor/client/hooks/menuActions/useToggleNotificationsAction.ts b/apps/meteor/client/hooks/menuActions/useToggleNotificationsAction.ts index c99e6720fa2fe..9e51b765d20b2 100644 --- a/apps/meteor/client/hooks/menuActions/useToggleNotificationsAction.ts +++ b/apps/meteor/client/hooks/menuActions/useToggleNotificationsAction.ts @@ -1,5 +1,5 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -14,7 +14,7 @@ export const useToggleNotificationAction = ({ rid, isNotificationEnabled, roomNa const dispatchToastMessage = useToastMessageDispatch(); const { t } = useTranslation(); - const handleToggleNotification = useEffectEvent(async () => { + const handleToggleNotification = useStableCallback(async () => { try { await toggleNotification({ roomId: rid, notifications: { disableNotifications: isNotificationEnabled ? '1' : '0' } }); dispatchToastMessage({ diff --git a/apps/meteor/client/hooks/menuActions/useToggleReadAction.ts b/apps/meteor/client/hooks/menuActions/useToggleReadAction.ts index d2bdfec8fe305..cfea5815112da 100644 --- a/apps/meteor/client/hooks/menuActions/useToggleReadAction.ts +++ b/apps/meteor/client/hooks/menuActions/useToggleReadAction.ts @@ -1,5 +1,5 @@ import type { ISubscription } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useRouter, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -21,7 +21,7 @@ export const useToggleReadAction = ({ rid, isUnread, subscription }: ToggleReadA const unreadMessages = useMarkAsUnreadMutation(); - const handleToggleRead = useEffectEvent(async () => { + const handleToggleRead = useStableCallback(async () => { try { queryClient.invalidateQueries({ queryKey: ['sidebar/search/spotlight'], diff --git a/apps/meteor/client/hooks/notification/useDesktopNotification.ts b/apps/meteor/client/hooks/notification/useDesktopNotification.ts index 45b30baaeb791..b03dbf5b3e7f7 100644 --- a/apps/meteor/client/hooks/notification/useDesktopNotification.ts +++ b/apps/meteor/client/hooks/notification/useDesktopNotification.ts @@ -1,5 +1,5 @@ import type { INotificationDesktop } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useUser } from '@rocket.chat/ui-contexts'; import { useNotification } from './useNotification'; @@ -11,7 +11,7 @@ export const useDesktopNotification = () => { const user = useUser(); const notify = useNotification(); - const notifyDesktop = useEffectEvent(async (notification: INotificationDesktop) => { + const notifyDesktop = useStableCallback(async (notification: INotificationDesktop) => { if ( notification.payload.rid === RoomManager.opened && (typeof window.document.hasFocus === 'function' ? window.document.hasFocus() : undefined) diff --git a/apps/meteor/client/hooks/notification/useNewMessageNotification.ts b/apps/meteor/client/hooks/notification/useNewMessageNotification.ts index 96d0045afa94e..9b3eb7ab190de 100644 --- a/apps/meteor/client/hooks/notification/useNewMessageNotification.ts +++ b/apps/meteor/client/hooks/notification/useNewMessageNotification.ts @@ -1,11 +1,11 @@ import type { AtLeast, ISubscription } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useCustomSound } from '@rocket.chat/ui-contexts'; export const useNewMessageNotification = () => { const { notificationSounds } = useCustomSound(); - const notifyNewMessage = useEffectEvent((sub: AtLeast) => { + const notifyNewMessage = useStableCallback((sub: AtLeast) => { if (!sub || sub.audioNotificationValue === 'none') { return; } diff --git a/apps/meteor/client/hooks/notification/useNotification.ts b/apps/meteor/client/hooks/notification/useNotification.ts index 5f2f9ea6891ab..0ef377703fcd6 100644 --- a/apps/meteor/client/hooks/notification/useNotification.ts +++ b/apps/meteor/client/hooks/notification/useNotification.ts @@ -1,5 +1,5 @@ import type { INotificationDesktop } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { Random } from '@rocket.chat/random'; import { useRouter, useUserPreference } from '@rocket.chat/ui-contexts'; @@ -14,7 +14,7 @@ export const useNotification = () => { const router = useRouter(); const notificationAllowed = useNotificationAllowed(); - const notify = useEffectEvent(async (notification: INotificationDesktop) => { + const notify = useStableCallback(async (notification: INotificationDesktop) => { if (!notificationAllowed) { return; } diff --git a/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts b/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts index 6bce502e6d384..603ac9c2a02d0 100644 --- a/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts +++ b/apps/meteor/client/hooks/roomActions/useE2EERoomAction.ts @@ -1,5 +1,5 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { imperativeModal } from '@rocket.chat/ui-client'; import { useSetting, usePermission, useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { RoomToolboxActionConfig } from '@rocket.chat/ui-contexts'; @@ -46,7 +46,7 @@ export const useE2EERoomAction = () => { const canResetRoomKey = enabled && isE2EEReady && (room.t === 'd' || permittedToToggleEncryption) && isE2EERoomNotReady(); - const action = useEffectEvent(async () => { + const action = useStableCallback(async () => { if (enabledOnRoom) { imperativeModal.open({ component: BaseDisableE2EEModal, diff --git a/apps/meteor/client/hooks/roomActions/useVideoCallRoomAction.tsx b/apps/meteor/client/hooks/roomActions/useVideoCallRoomAction.tsx index 7a0f456d68a73..c4e4fdb0d3589 100644 --- a/apps/meteor/client/hooks/roomActions/useVideoCallRoomAction.tsx +++ b/apps/meteor/client/hooks/roomActions/useVideoCallRoomAction.tsx @@ -1,5 +1,5 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useEffectEvent, useStableArray } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useStableArray } from '@rocket.chat/fuselage-hooks'; import { usePermission, useSetting, useUser } from '@rocket.chat/ui-contexts'; import type { RoomToolboxActionConfig } from '@rocket.chat/ui-contexts'; import { @@ -53,7 +53,7 @@ export const useVideoCallRoomAction = () => { const disabled = federated || (!!room.ro && !permittedToPostReadonly) || room.archived; const tooltip = disabled ? t('core.Video_Call_unavailable_for_this_type_of_room') : undefined; - const handleOpenVideoConf = useEffectEvent(async () => { + const handleOpenVideoConf = useStableCallback(async () => { if (isCalling || isRinging) { return; } diff --git a/apps/meteor/client/hooks/useAppUiKitInteraction.ts b/apps/meteor/client/hooks/useAppUiKitInteraction.ts index 56d984c709e69..aac3a737ddcad 100644 --- a/apps/meteor/client/hooks/useAppUiKitInteraction.ts +++ b/apps/meteor/client/hooks/useAppUiKitInteraction.ts @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useStream, useUserId } from '@rocket.chat/ui-contexts'; import type * as UiKit from '@rocket.chat/ui-kit'; import { useEffect } from 'react'; @@ -7,7 +7,7 @@ export const useAppUiKitInteraction = (handleServerInteraction: (interaction: Ui const notifyUser = useStream('notify-user'); const uid = useUserId(); - const handle = useEffectEvent(handleServerInteraction); + const handle = useStableCallback(handleServerInteraction); useEffect(() => { if (!uid) { return; diff --git a/apps/meteor/client/hooks/useClipboardWithToast.ts b/apps/meteor/client/hooks/useClipboardWithToast.ts index 67e33d4d4b3cf..0a53767dc701a 100644 --- a/apps/meteor/client/hooks/useClipboardWithToast.ts +++ b/apps/meteor/client/hooks/useClipboardWithToast.ts @@ -1,5 +1,5 @@ import type { UseClipboardReturn } from '@rocket.chat/fuselage-hooks'; -import { useClipboard, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useClipboard, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -8,7 +8,7 @@ export default function useClipboardWithToast(text: string): UseClipboardReturn const dispatchToastMessage = useToastMessageDispatch(); return useClipboard(text, { - onCopySuccess: useEffectEvent(() => dispatchToastMessage({ type: 'success', message: t('Copied') })), - onCopyError: useEffectEvent((e?: Error) => dispatchToastMessage({ type: 'error', message: e })), + onCopySuccess: useStableCallback(() => dispatchToastMessage({ type: 'success', message: t('Copied') })), + onCopyError: useStableCallback((e?: Error) => dispatchToastMessage({ type: 'error', message: e })), }); } diff --git a/apps/meteor/client/hooks/useDecryptedMessage.spec.ts b/apps/meteor/client/hooks/useDecryptedMessage.spec.ts index 3103e708910f6..5b35e8d6e3352 100644 --- a/apps/meteor/client/hooks/useDecryptedMessage.spec.ts +++ b/apps/meteor/client/hooks/useDecryptedMessage.spec.ts @@ -53,7 +53,7 @@ describe('useDecryptedMessage', () => { it('should handle E2EE messages with attachments', async () => { (isE2EEMessage as jest.MockedFunction).mockReturnValue(true); (e2e.decryptMessage as jest.Mock).mockResolvedValue({ - attachments: [{ title: 'Attachment title' }], + attachments: [{ description: 'Attachment description' }], }); const message = { msg: 'Encrypted message with attachment' }; @@ -63,6 +63,7 @@ describe('useDecryptedMessage', () => { expect(result.current).toBe('E2E_message_encrypted_placeholder'); }); + expect(result.current).toBe('Attachment description'); expect(e2e.decryptMessage).toHaveBeenCalledWith(message); }); diff --git a/apps/meteor/client/hooks/useDecryptedMessage.ts b/apps/meteor/client/hooks/useDecryptedMessage.ts index 771665dc0b631..e560aacc5b111 100644 --- a/apps/meteor/client/hooks/useDecryptedMessage.ts +++ b/apps/meteor/client/hooks/useDecryptedMessage.ts @@ -18,11 +18,14 @@ export const useDecryptedMessage = (message: IMessage): string => { e2e.decryptMessage(message).then((decryptedMsg) => { if (decryptedMsg.msg) { setDecryptedMessage(decryptedMsg.msg); - return; } - if (decryptedMsg.attachments && decryptedMsg.attachments.length > 0) { - setDecryptedMessage(t('Message_with_attachment')); + if (decryptedMsg.attachments && decryptedMsg.attachments?.length > 0) { + if (decryptedMsg.attachments[0].description) { + setDecryptedMessage(decryptedMsg.attachments[0].description); + } else { + setDecryptedMessage(t('Message_with_attachment')); + } } }); }, [message, t, setDecryptedMessage]); diff --git a/apps/meteor/client/hooks/useHideRoomAction.tsx b/apps/meteor/client/hooks/useHideRoomAction.tsx index 7c9fbb3bea6b8..d68fac71ee34b 100644 --- a/apps/meteor/client/hooks/useHideRoomAction.tsx +++ b/apps/meteor/client/hooks/useHideRoomAction.tsx @@ -1,5 +1,5 @@ import type { RoomType } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModalDoNotAskAgain, useDontAskAgain } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useEndpoint, useSetModal, useToastMessageDispatch, useRouter, useUserId } from '@rocket.chat/ui-contexts'; @@ -30,7 +30,7 @@ const CLOSE_ENDPOINTS_BY_ROOM_TYPE = { export const useHideRoomAction = ({ rid: roomId, type, name }: HideRoomProps, { redirect = true }: HideRoomOptions = {}) => { const { t } = useTranslation(); const setModal = useSetModal(); - const closeModal = useEffectEvent(() => setModal()); + const closeModal = useStableCallback(() => setModal()); const dispatchToastMessage = useToastMessageDispatch(); const dontAskHideRoom = useDontAskAgain('hideRoom'); const router = useRouter(); @@ -62,7 +62,7 @@ export const useHideRoomAction = ({ rid: roomId, type, name }: HideRoomProps, { }, }); - const handleHide = useEffectEvent(async () => { + const handleHide = useStableCallback(async () => { const warnText = roomCoordinator.getRoomDirectives(type).getUiText(UiTextContext.HIDE_WARNING); if (dontAskHideRoom) { diff --git a/apps/meteor/client/hooks/useIdleActiveEvents.ts b/apps/meteor/client/hooks/useIdleActiveEvents.ts index 441c74d3308c5..6d64ba45015a5 100644 --- a/apps/meteor/client/hooks/useIdleActiveEvents.ts +++ b/apps/meteor/client/hooks/useIdleActiveEvents.ts @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEffect } from 'react'; import { useIdleDetection } from './useIdleDetection'; @@ -29,8 +29,8 @@ export const useIdleActiveEvents = ( onIdleCallback: () => void, onActiveCallback?: () => void, ) => { - const stableIdleCallback = useEffectEvent(onIdleCallback); - const stableActiveCallback = useEffectEvent(onActiveCallback || (() => undefined)); + const stableIdleCallback = useStableCallback(onIdleCallback); + const stableActiveCallback = useStableCallback(onActiveCallback || (() => undefined)); useEffect(() => { document.addEventListener(`${id}_idle`, stableIdleCallback); diff --git a/apps/meteor/client/hooks/useIdleConnection.ts b/apps/meteor/client/hooks/useIdleConnection.ts index 4ae667237a55b..6e89b80e4baca 100644 --- a/apps/meteor/client/hooks/useIdleConnection.ts +++ b/apps/meteor/client/hooks/useIdleConnection.ts @@ -1,5 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useConnectionStatus, useSetting } from '@rocket.chat/ui-contexts'; import { useIdleActiveEvents } from './useIdleActiveEvents'; @@ -9,7 +9,7 @@ export const useIdleConnection = (uid: IUser['_id'] | undefined) => { const allowAnonymousRead = useSetting('Accounts_AllowAnonymousRead'); const { disconnect: disconnectServer, reconnect: reconnectServer } = useConnectionStatus(); - const disconnect = useEffectEvent(() => { + const disconnect = useStableCallback(() => { if (status !== 'offline') { if (!uid && allowAnonymousRead !== true) { disconnectServer(); @@ -17,7 +17,7 @@ export const useIdleConnection = (uid: IUser['_id'] | undefined) => { } }); - const reconnect = useEffectEvent(() => { + const reconnect = useStableCallback(() => { if (status === 'offline') { reconnectServer(); } diff --git a/apps/meteor/client/hooks/useIdleDetection.ts b/apps/meteor/client/hooks/useIdleDetection.ts index af7eb75f210fb..31242a4518ab8 100644 --- a/apps/meteor/client/hooks/useIdleDetection.ts +++ b/apps/meteor/client/hooks/useIdleDetection.ts @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEffect, useRef } from 'react'; const events = ['mousemove', 'mousedown', 'touchend', 'touchstart', 'keypress']; @@ -34,7 +34,7 @@ export const useIdleDetection = ({ }: UseIdleDetectionOptions = {}) => { const idleRef = useRef(false); - const dispatchIdle = useEffectEvent(() => { + const dispatchIdle = useStableCallback(() => { if (idleRef.current) return; document.dispatchEvent(new Event(`${id}_idle`)); @@ -46,7 +46,7 @@ export const useIdleDetection = ({ idleRef.current = true; }); - const dispatchActive = useEffectEvent(() => { + const dispatchActive = useStableCallback(() => { if (!idleRef.current) return; document.dispatchEvent(new Event(`${id}_active`)); diff --git a/apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts b/apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts index 3e778a7334915..5daf7990d15e9 100644 --- a/apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts +++ b/apps/meteor/client/hooks/useInfiniteMessageQueryUpdates.ts @@ -1,5 +1,5 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useStream, useUserId } from '@rocket.chat/ui-contexts'; import type { InfiniteData } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query'; @@ -21,11 +21,11 @@ export const useInfiniteMessageQueryUpdates = queryKey); - const doFilter = useEffectEvent(filter); - const doCompare = useEffectEvent(compare); + const getQueryKey = useStableCallback(() => queryKey); + const doFilter = useStableCallback(filter); + const doCompare = useStableCallback(compare); - const mutateQueryData = useEffectEvent((mutation: (items: T[]) => void) => { + const mutateQueryData = useStableCallback((mutation: (items: T[]) => void) => { const queryData = queryClient.getQueryData< InfiniteData< { diff --git a/apps/meteor/client/hooks/usePreventPropagation.ts b/apps/meteor/client/hooks/usePreventPropagation.ts index 19382bec8d100..6d3662681ad3d 100644 --- a/apps/meteor/client/hooks/usePreventPropagation.ts +++ b/apps/meteor/client/hooks/usePreventPropagation.ts @@ -1,8 +1,8 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { UIEvent } from 'react'; export const usePreventPropagation = (fn?: (e: UIEvent) => void): ((e: UIEvent) => void) => { - const preventClickPropagation = useEffectEvent((e: UIEvent): void => { + const preventClickPropagation = useStableCallback((e: UIEvent): void => { e.stopPropagation(); fn?.(e); }); diff --git a/apps/meteor/client/hooks/useSingleFileInput.ts b/apps/meteor/client/hooks/useSingleFileInput.ts index 75fef0e0f3df9..c6bc957983088 100644 --- a/apps/meteor/client/hooks/useSingleFileInput.ts +++ b/apps/meteor/client/hooks/useSingleFileInput.ts @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRef, useEffect } from 'react'; export const useSingleFileInput = ( @@ -63,8 +63,8 @@ export const useSingleFileInput = ( }; }, [fileField, fileType, onSetFile, maxSize, onError]); - const onClick = useEffectEvent(() => ref?.current?.click()); - const reset = useEffectEvent(() => { + const onClick = useStableCallback(() => ref?.current?.click()); + const reset = useStableCallback(() => { if (ref.current) { ref.current.value = ''; } diff --git a/apps/meteor/client/lib/normalizeThreadMessage.tsx b/apps/meteor/client/lib/normalizeThreadMessage.tsx index 067a23f6aedfe..f2ee47688a59e 100644 --- a/apps/meteor/client/lib/normalizeThreadMessage.tsx +++ b/apps/meteor/client/lib/normalizeThreadMessage.tsx @@ -24,7 +24,11 @@ export function normalizeThreadMessage({ ...message }: Readonly attachment.title); + const attachment = message.attachments.find((attachment) => attachment.title || attachment.description); + + if (attachment?.description) { + return <>{attachment.description}; + } if (attachment?.title) { return <>{attachment.title}; diff --git a/apps/meteor/client/lib/parseMessageTextToAstMarkdown.spec.ts b/apps/meteor/client/lib/parseMessageTextToAstMarkdown.spec.ts index 0105608949b7f..e48eb15f885cf 100644 --- a/apps/meteor/client/lib/parseMessageTextToAstMarkdown.spec.ts +++ b/apps/meteor/client/lib/parseMessageTextToAstMarkdown.spec.ts @@ -178,6 +178,47 @@ describe('parseMessageTextToAstMarkdown', () => { }); it('should return correct attachment translated parsed md when translate is active', () => { + const attachmentTranslatedMessage = { + ...translatedMessage, + attachments: [ + { + description: 'description', + translations: { + en: 'description translated', + }, + }, + ], + }; + const attachmentTranslatedMessageParsed = { + ...translatedMessage, + md: translatedMessageParsed, + attachments: [ + { + description: 'description', + translations: { + en: 'description translated', + }, + md: [ + { + type: 'PARAGRAPH', + value: [ + { + type: 'PLAIN_TEXT', + value: 'description translated', + }, + ], + }, + ], + }, + ], + }; + + expect(parseMessageTextToAstMarkdown(attachmentTranslatedMessage, parseOptions, enabledAutoTranslatedOptions)).toStrictEqual( + attachmentTranslatedMessageParsed, + ); + }); + + it('should return correct attachment quote translated parsed md when translate is active', () => { const attachmentTranslatedMessage = { ...translatedMessage, attachments: [ @@ -337,7 +378,7 @@ describe('parseMessageAttachments', () => { const attachmentMessage = [ { - text: 'message **bold** _italic_ and ~strike~', + description: 'message **bold** _italic_ and ~strike~', md: messageParserTokenMessage, }, ]; @@ -359,18 +400,46 @@ describe('parseMessageAttachments', () => { autoTranslateLanguage: 'en', }; - it('should return correct attachment text parsed md when translate is active and auto translate language is undefined', () => { - const textAttachment = [ + it('should return correct attachment description translated parsed md when translate is active', () => { + const descriptionAttachment = [ { ...attachmentMessage[0], - text: 'attachment not translated', + description: 'attachment not translated', translationProvider: 'provider', translations: { en: 'attachment translated', }, }, ]; - const textAttachmentParsed: Root = [ + const descriptionAttachmentParsed: Root = [ + { + type: 'PARAGRAPH', + value: [ + { + type: 'PLAIN_TEXT', + value: 'attachment translated', + }, + ], + }, + ]; + + expect(parseMessageAttachments(descriptionAttachment, parseOptions, enabledAutoTranslatedOptions)[0].md).toStrictEqual( + descriptionAttachmentParsed, + ); + }); + + it('should return correct attachment description parsed md when translate is active and auto translate language is undefined', () => { + const descriptionAttachment = [ + { + ...attachmentMessage[0], + description: 'attachment not translated', + translationProvider: 'provider', + translations: { + en: 'attachment translated', + }, + }, + ]; + const descriptionAttachmentParsed: Root = [ { type: 'PARAGRAPH', value: [ @@ -383,11 +452,39 @@ describe('parseMessageAttachments', () => { ]; expect( - parseMessageAttachments(textAttachment, parseOptions, { + parseMessageAttachments(descriptionAttachment, parseOptions, { ...enabledAutoTranslatedOptions, autoTranslateLanguage: undefined, })[0].md, - ).toStrictEqual(textAttachmentParsed); + ).toStrictEqual(descriptionAttachmentParsed); + }); + + it('should return correct attachment text translated parsed md when translate is active', () => { + const textAttachment = [ + { + ...attachmentMessage[0], + text: 'attachment not translated', + translationProvider: 'provider', + translations: { + en: 'attachment translated', + }, + }, + ]; + const textAttachmentParsed: Root = [ + { + type: 'PARAGRAPH', + value: [ + { + type: 'PLAIN_TEXT', + value: 'attachment translated', + }, + ], + }, + ]; + + expect(parseMessageAttachments(textAttachment, parseOptions, enabledAutoTranslatedOptions)[0].md).toStrictEqual( + textAttachmentParsed, + ); }); it('should return correct attachment text translated parsed md when translate is active and has multiple texts', () => { diff --git a/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts b/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts index 55d393cee38ed..df84785e26351 100644 --- a/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts +++ b/apps/meteor/client/lib/parseMessageTextToAstMarkdown.ts @@ -1,5 +1,12 @@ import type { IMessage, ITranslatedMessage, MessageAttachment } from '@rocket.chat/core-typings'; -import { isE2EEMessage, isQuoteAttachment, isTranslatedAttachment, isTranslatedMessage } from '@rocket.chat/core-typings'; +import { + isFileAttachment, + isE2EEMessage, + isQuoteAttachment, + isTranslatedAttachment, + isTranslatedMessage, + isEncryptedMessageAttachment, +} from '@rocket.chat/core-typings'; import type { Options, Root } from '@rocket.chat/message-parser'; import { parse } from '@rocket.chat/message-parser'; @@ -51,7 +58,7 @@ export const parseMessageAttachment = ( autoTranslateOptions: { autoTranslateLanguage?: string; translated: boolean }, ): T => { const { translated, autoTranslateLanguage } = autoTranslateOptions; - if (!attachment.text) { + if (!attachment.text && !attachment.description) { return attachment; } @@ -62,8 +69,16 @@ export const parseMessageAttachment = ( const text = (isTranslatedAttachment(attachment) && autoTranslateLanguage && attachment?.translations?.[autoTranslateLanguage]) || attachment.text || + attachment.description || ''; + if (isFileAttachment(attachment) && attachment.description) { + attachment.descriptionMd = + translated || isEncryptedMessageAttachment(attachment) + ? textToMessageToken(text, parseOptions) + : (attachment.descriptionMd ?? textToMessageToken(text, parseOptions)); + } + return { ...attachment, md: translated ? textToMessageToken(text, parseOptions) : (attachment.md ?? textToMessageToken(text, parseOptions)), diff --git a/apps/meteor/client/lib/userPresence.ts b/apps/meteor/client/lib/userPresence.ts index 835c3230781e7..af989f09ac8e3 100644 --- a/apps/meteor/client/lib/userPresence.ts +++ b/apps/meteor/client/lib/userPresence.ts @@ -1,3 +1,4 @@ +/* eslint-disable react-hooks/rules-of-hooks */ import type { IUser } from '@rocket.chat/core-typings'; import { UserStatus } from '@rocket.chat/core-typings'; import { useConnectionStatus, useIsLoggingIn, useMethod, useUser, useUserPreference } from '@rocket.chat/ui-contexts'; diff --git a/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.spec.ts b/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.spec.ts index 9584c13531439..184145a6c4506 100644 --- a/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.spec.ts +++ b/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.spec.ts @@ -48,7 +48,7 @@ describe('normalizeMessagePreview', () => { }); describe('when message has attachments', () => { - it('should return attachment title when description is available', () => { + it('should return attachment description when available', () => { const message = createFakeMessageWithAttachment({ msg: '', attachments: [ @@ -60,10 +60,10 @@ describe('normalizeMessagePreview', () => { }); const result = normalizeMessagePreview(message, mockT); - expect(result).toBe('Attachment title'); + expect(result).toBe('Attachment description'); }); - it('should return attachment title when message is not provided', () => { + it('should return attachment title when description is not available', () => { const message = createFakeMessageWithAttachment({ msg: '', attachments: [ @@ -112,7 +112,7 @@ describe('normalizeMessagePreview', () => { expect(result).toBe('Second attachment title'); }); - it('should find first attachment title', () => { + it('should find first attachment description', () => { const message = createFakeMessageWithAttachment({ msg: '', attachments: [ @@ -129,7 +129,21 @@ describe('normalizeMessagePreview', () => { }); const result = normalizeMessagePreview(message, mockT); - expect(result).toBe('Third attachment title'); + expect(result).toBe('Second attachment description'); + }); + + it('should escape HTML in attachment description', () => { + const message = createFakeMessageWithAttachment({ + msg: '', + attachments: [ + { + description: '', + }, + ], + }); + const result = normalizeMessagePreview(message, mockT); + + expect(result).toBe('<script>alert("xss")</script>'); }); it('should escape HTML in attachment title', () => { diff --git a/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.ts b/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.ts index 0fbefea5fa48d..53bf5bf2d4056 100644 --- a/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.ts +++ b/apps/meteor/client/lib/utils/normalizeMessagePreview/normalizeMessagePreview.ts @@ -11,7 +11,11 @@ export const normalizeMessagePreview = (message: IMessage, t: TFunction): string } if (message.attachments) { - const attachment = message.attachments.find((attachment) => attachment.title); + const attachment = message.attachments.find((attachment) => attachment.title || attachment.description); + + if (attachment?.description) { + return escapeHTML(attachment.description); + } if (attachment?.title) { return escapeHTML(attachment.title); diff --git a/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts b/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts index 89a46c6eab2bc..372c53d50890a 100644 --- a/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts +++ b/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { Keys } from '@rocket.chat/icons'; import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -11,7 +11,7 @@ export const useOmnichannelLivechatToggle = () => { const changeAgentStatus = useEndpoint('POST', '/v1/livechat/agent.status'); const dispatchToastMessage = useToastMessageDispatch(); - const handleAvailableStatusChange = useEffectEvent(async () => { + const handleAvailableStatusChange = useStableCallback(async () => { try { await changeAgentStatus({}); } catch (error: unknown) { diff --git a/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemDirectoryPage.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemDirectoryPage.tsx index 3b28f53d4282b..65f1b6edee44d 100644 --- a/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemDirectoryPage.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemDirectoryPage.tsx @@ -1,5 +1,5 @@ import { NavBarItem } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRouter, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes } from 'react'; @@ -7,7 +7,7 @@ type NavBarItemDirectoryPageProps = Omit, 'is'>; const NavBarItemDirectoryPage = (props: NavBarItemDirectoryPageProps) => { const router = useRouter(); - const handleDirectory = useEffectEvent(() => { + const handleDirectory = useStableCallback(() => { router.navigate('/directory'); }); const currentRoute = useCurrentRoutePath(); diff --git a/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemHomePage.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemHomePage.tsx index 061dbf10c3c8a..920e424a6c43e 100644 --- a/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemHomePage.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemHomePage.tsx @@ -1,5 +1,5 @@ import { NavBarItem } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRouter, useLayout, useSetting, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes } from 'react'; @@ -9,7 +9,7 @@ const NavBarItemHomePage = (props: NavBarItemHomePageProps) => { const router = useRouter(); const { sidebar } = useLayout(); const showHome = useSetting('Layout_Show_Home_Button'); - const handleHome = useEffectEvent(() => { + const handleHome = useStableCallback(() => { sidebar.toggle(); router.navigate('/home'); }); diff --git a/apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesStackMenu.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesStackMenu.tsx index 16e7b81e4378e..05180536d360e 100644 --- a/apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesStackMenu.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesStackMenu.tsx @@ -1,5 +1,5 @@ import { NavBarItem } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { GenericMenu } from '@rocket.chat/ui-client'; import { useCurrentRoutePath, useLayout, useRouter, useSetting } from '@rocket.chat/ui-contexts'; @@ -15,7 +15,7 @@ const NavBarPagesStackMenu = (props: NavBarPagesStackMenuProps) => { const { sidebar } = useLayout(); const router = useRouter(); - const handleGoToHome = useEffectEvent(() => { + const handleGoToHome = useStableCallback(() => { sidebar.toggle(); router.navigate('/home'); }); diff --git a/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateRoomModal.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateRoomModal.tsx index d1af9eb355990..8b602a6a639ba 100644 --- a/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateRoomModal.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateRoomModal.tsx @@ -1,11 +1,11 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; export const useCreateRoomModal = (Component: FC): (() => void) => { const setModal = useSetModal(); - return useEffectEvent(() => { + return useStableCallback(() => { const handleClose = (): void => { setModal(null); }; diff --git a/apps/meteor/client/navbar/NavBarSearch/NavBarSearch.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearch.tsx index 9fc12fafa368f..82dcfecf2af41 100644 --- a/apps/meteor/client/navbar/NavBarSearch/NavBarSearch.tsx +++ b/apps/meteor/client/navbar/NavBarSearch/NavBarSearch.tsx @@ -2,7 +2,7 @@ import { useFocusManager } from '@react-aria/focus'; import { useOverlayTrigger } from '@react-aria/overlays'; import { useOverlayTriggerState } from '@react-stately/overlays'; import { Box, Icon, IconButton, TextInput } from '@rocket.chat/fuselage'; -import { useEffectEvent, useMergedRefs } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { useCallback, useEffect, useRef } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -47,7 +47,7 @@ const NavBarSearch = () => { state.close(); }, [resetField, state]); - const handleClearText = useEffectEvent(() => { + const handleClearText = useStableCallback(() => { resetField('filterText'); setFocus('filterText'); }); diff --git a/apps/meteor/client/navbar/NavBarSearch/NavBarSearchListbox.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchListbox.tsx index 481081a82e14a..1b3ef415fa414 100644 --- a/apps/meteor/client/navbar/NavBarSearch/NavBarSearchListbox.tsx +++ b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchListbox.tsx @@ -1,7 +1,7 @@ import type { OverlayTriggerAria } from '@react-aria/overlays'; import type { OverlayTriggerState } from '@react-stately/overlays'; import { Box, Tile } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent, useOutsideClick } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback, useOutsideClick } from '@rocket.chat/fuselage-hooks'; import { CustomScrollbars } from '@rocket.chat/ui-client'; import { useRef } from 'react'; import { useFormContext } from 'react-hook-form'; @@ -30,7 +30,7 @@ const NavBarSearchListBox = ({ state, overlayProps }: NavBarSearchListBoxProps) const debouncedFilter = useDebouncedValue(filterText, 500); - const handleSelect = useEffectEvent(() => { + const handleSelect = useStableCallback(() => { state.close(); resetField('filterText'); }); diff --git a/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx index e5cc289312bda..5616da39011d1 100644 --- a/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx +++ b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx @@ -18,9 +18,9 @@ import { ModalFooter, ModalFooterControllers, } from '@rocket.chat/fuselage'; -import { useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useSetting, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { ChangeEvent, ComponentProps, FormEvent } from 'react'; +import type { ChangeEvent, ComponentProps } from 'react'; import { useState, useCallback, useId } from 'react'; import UserStatusMenu from '../../../components/UserStatusMenu'; @@ -46,7 +46,7 @@ const EditStatusModal = ({ onClose, userStatus, userStatusText }: EditStatusModa const setUserStatus = useEndpoint('POST', '/v1/users.setStatus'); - const handleStatusText = useEffectEvent((e: ChangeEvent): void => { + const handleStatusText = useStableCallback((e: ChangeEvent): void => { setStatusText(e.currentTarget.value); if (statusText && statusText.length > USER_STATUS_TEXT_MAX_LENGTH) { @@ -76,7 +76,7 @@ const EditStatusModal = ({ onClose, userStatus, userStatusText }: EditStatusModa wrapperFunction={(props: ComponentProps) => ( { + onSubmit={(e) => { e.preventDefault(); handleSaveStatus(); }} diff --git a/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx index 99100fdad2b56..21dfafd684eae 100644 --- a/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx +++ b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx @@ -1,5 +1,5 @@ import { Badge } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { defaultFeaturesPreview, usePreferenceFeaturePreviewList } from '@rocket.chat/ui-client'; import { useRouter } from '@rocket.chat/ui-contexts'; @@ -11,16 +11,16 @@ export const useAccountItems = (): GenericMenuItemProps[] => { const { unseenFeatures, featurePreviewEnabled } = usePreferenceFeaturePreviewList(); - const handleMyAccount = useEffectEvent(() => { + const handleMyAccount = useStableCallback(() => { router.navigate('/account'); }); - const handlePreferences = useEffectEvent(() => { + const handlePreferences = useStableCallback(() => { router.navigate('/account/preferences'); }); - const handleFeaturePreview = useEffectEvent(() => { + const handleFeaturePreview = useStableCallback(() => { router.navigate('/account/feature-preview'); }); - const handleAccessibility = useEffectEvent(() => { + const handleAccessibility = useStableCallback(() => { router.navigate('/account/accessibility-and-appearance'); }); diff --git a/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx index c2e16f22cb1a1..b29aafb769842 100644 --- a/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx +++ b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx @@ -1,5 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { useLogout } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -19,7 +19,7 @@ export const useUserMenu = (user: IUser) => { const handleKeyboardShortcuts = useKeyboardShortcutsModalHandler(); const logout = useLogout(); - const handleLogout = useEffectEvent(() => { + const handleLogout = useStableCallback(() => { logout(); }); diff --git a/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx b/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx index 4dfcedaa1752f..9cda0dc4cb08f 100644 --- a/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx +++ b/apps/meteor/client/providers/CustomSoundProvider/CustomSoundProvider.tsx @@ -1,5 +1,5 @@ import type { ICustomSound } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { CustomSoundContext, useStream, useUserPreference } from '@rocket.chat/ui-contexts'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useEffect, useMemo, useRef, type ReactNode } from 'react'; @@ -34,7 +34,7 @@ const CustomSoundProvider = ({ children }: CustomSoundProviderProps) => { initialData: defaultSounds, }); - const play = useEffectEvent((soundId: ICustomSound['_id'], { volume = 1, loop = false } = {}) => { + const play = useStableCallback((soundId: ICustomSound['_id'], { volume = 1, loop = false } = {}) => { stop(soundId); const item = list?.find(({ _id }) => _id === soundId); @@ -56,7 +56,7 @@ const CustomSoundProvider = ({ children }: CustomSoundProviderProps) => { }; }); - const pause = useEffectEvent((soundId: ICustomSound['_id']) => { + const pause = useStableCallback((soundId: ICustomSound['_id']) => { const current = audioRefs.current?.find(({ id }) => id === soundId); if (current) { current.pause(); @@ -64,7 +64,7 @@ const CustomSoundProvider = ({ children }: CustomSoundProviderProps) => { } }); - const stop = useEffectEvent((soundId: ICustomSound['_id']) => { + const stop = useStableCallback((soundId: ICustomSound['_id']) => { const current = audioRefs.current?.find(({ id }) => id === soundId); if (current) { current.load(); diff --git a/apps/meteor/client/providers/DeviceProvider/DeviceProvider.tsx b/apps/meteor/client/providers/DeviceProvider/DeviceProvider.tsx index 686931813ce61..c6411d78e3d13 100644 --- a/apps/meteor/client/providers/DeviceProvider/DeviceProvider.tsx +++ b/apps/meteor/client/providers/DeviceProvider/DeviceProvider.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { Device, DeviceContextValue } from '@rocket.chat/ui-contexts'; import { DeviceContext } from '@rocket.chat/ui-contexts'; import { useQuery, useQueryClient, keepPreviousData } from '@tanstack/react-query'; @@ -40,7 +40,7 @@ export const DeviceProvider = ({ children }: DeviceProviderProps) => { setSelectedAudioInputDevice(device); }; - const setAudioOutputDevice = useEffectEvent( + const setAudioOutputDevice = useStableCallback( ({ outputDevice, HTMLAudioElement }: { outputDevice: Device; HTMLAudioElement: HTMLAudioElement }): void => { if (!isSetSinkIdAvailable()) { throw new Error('setSinkId is not available in this browser'); diff --git a/apps/meteor/client/providers/EmojiPickerProvider/EmojiPickerProvider.tsx b/apps/meteor/client/providers/EmojiPickerProvider/EmojiPickerProvider.tsx index 1de2c278d65b0..64538ef28df8c 100644 --- a/apps/meteor/client/providers/EmojiPickerProvider/EmojiPickerProvider.tsx +++ b/apps/meteor/client/providers/EmojiPickerProvider/EmojiPickerProvider.tsx @@ -1,4 +1,4 @@ -import { useDebouncedState, useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedState, useStableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks'; import type { ReactNode, ContextType } from 'react'; import { useState, useCallback, useMemo, useSyncExternalStore } from 'react'; @@ -27,7 +27,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }) => { getFrequentEmoji(frequentEmojis.map(([emoji]) => emoji)), ); - const setQuickReactions = useEffectEvent(() => _setQuickReactions(getFrequentEmoji(frequentEmojis.map(([emoji]) => emoji)))); + const setQuickReactions = useStableCallback(() => _setQuickReactions(getFrequentEmoji(frequentEmojis.map(([emoji]) => emoji)))); const [sub, getSnapshot] = useMemo(() => { return createEmojiListByCategorySubscription(customItemsLimit, actualTone, recentEmojis, setRecentEmojis, setQuickReactions); }, [customItemsLimit, actualTone, recentEmojis, setRecentEmojis, setQuickReactions]); diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx index 2baaeac8753bd..c96b5d966bb5f 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx @@ -18,7 +18,7 @@ import { import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useSetModal, useTranslation, useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { FormEvent } from 'react'; +import type { ChangeEvent } from 'react'; import { useState } from 'react'; import MatrixFederationRemoveServerList from './MatrixFederationRemoveServerList'; @@ -90,7 +90,7 @@ const MatrixFederationAddServerModal = ({ onClickClose }: MatrixFederationAddSer ) => { + onChange={(e: ChangeEvent) => { setServerName(e.currentTarget.value); if (errorKey) { setErrorKey(undefined); diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationSearchModalContent.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationSearchModalContent.tsx index 853df819ddbb5..ff5f5ab94fc15 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationSearchModalContent.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationSearchModalContent.tsx @@ -2,7 +2,7 @@ import type { SelectOption } from '@rocket.chat/fuselage'; import { Box, Select, TextInput } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { useSetModal } from '@rocket.chat/ui-contexts'; -import type { FormEvent } from 'react'; +import type { ChangeEvent } from 'react'; import { useCallback, useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -53,7 +53,7 @@ const MatrixFederationSearchModalContent = ({ defaultSelectedServer, servers }: flexGrow={4} flexShrink={0} value={roomName} - onChange={(e: FormEvent) => setRoomName(e.currentTarget.value)} + onChange={(e: ChangeEvent) => setRoomName(e.currentTarget.value)} /> diff --git a/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts b/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts index f70c06d8ee13f..0fe1ae17256e4 100644 --- a/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts +++ b/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts @@ -1,5 +1,5 @@ import type { IRoom, IMessage } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { UiKitContext } from '@rocket.chat/fuselage-ui-kit'; import { useRoomToolbox } from '@rocket.chat/ui-contexts'; import { @@ -24,7 +24,7 @@ export const useMessageBlockContextValue = (rid: IRoom['_id'], mid: IMessage['_i const dispatchPopup = useVideoConfDispatchOutgoing(); const loadVideoConfCapabilities = useVideoConfLoadCapabilities(); - const handleOpenVideoConf = useEffectEvent(async (rid: IRoom['_id']) => { + const handleOpenVideoConf = useStableCallback(async (rid: IRoom['_id']) => { if (isCalling || isRinging) { return; } diff --git a/apps/meteor/client/views/account/security/TwoFactorEmail.tsx b/apps/meteor/client/views/account/security/TwoFactorEmail.tsx index 17c261e8df5a0..5929662999cb2 100644 --- a/apps/meteor/client/views/account/security/TwoFactorEmail.tsx +++ b/apps/meteor/client/views/account/security/TwoFactorEmail.tsx @@ -1,6 +1,6 @@ import { Box, Field, FieldLabel, FieldRow, Margins, ToggleSwitch } from '@rocket.chat/fuselage'; import { useToastMessageDispatch, useUser } from '@rocket.chat/ui-contexts'; -import type { ComponentProps, FormEvent } from 'react'; +import type { ComponentProps, ChangeEvent } from 'react'; import { useCallback, useId } from 'react'; import { useTranslation } from 'react-i18next'; @@ -27,7 +27,7 @@ const TwoFactorEmail = (props: ComponentProps) => { }); const handleEnable = useCallback( - async (e: FormEvent) => { + async (e: ChangeEvent) => { if (e.currentTarget.checked) { await enable2faAction(); } else { diff --git a/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx b/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx index 26c51e32702b5..31d56dc2a4d20 100644 --- a/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx +++ b/apps/meteor/client/views/account/security/TwoFactorTOTP.tsx @@ -1,7 +1,7 @@ import { Box, Button, TextInput, Margins, Field, FieldRow, FieldLabel, ToggleSwitch } from '@rocket.chat/fuselage'; -import { useEffectEvent, useSafely } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useSafely } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useUser, useMethod } from '@rocket.chat/ui-contexts'; -import type { ComponentPropsWithoutRef, FormEvent } from 'react'; +import type { ComponentPropsWithoutRef, ChangeEvent } from 'react'; import { useState, useCallback, useEffect, useId } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -51,7 +51,7 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps) => { updateCodesRemaining(); }, [checkCodesRemainingFn, setCodesRemaining, totpEnabled]); - const enableTotp = useEffectEvent(async () => { + const enableTotp = useStableCallback(async () => { try { const result = await enableTotpFn(); @@ -64,7 +64,7 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps) => { } }); - const disableTotp = useEffectEvent(async () => { + const disableTotp = useStableCallback(async () => { if (!totpEnabled) { setRegisteringTotp(false); @@ -92,7 +92,7 @@ const TwoFactorTOTP = (props: TwoFactorTOTPProps) => { setModal(); }); - const handleToggleTotp = useEffectEvent(async (e: FormEvent) => { + const handleToggleTotp = useStableCallback(async (e: ChangeEvent) => { if (e.currentTarget?.checked) { void enableTotp(); } else { diff --git a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx index a9b067e52fe61..f6e08aeb530f3 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx @@ -10,7 +10,7 @@ import { IconButton, TextInput, } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarScrollableContent } from '@rocket.chat/ui-client'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useCallback, useId, useMemo, Fragment, useState } from 'react'; @@ -74,7 +74,7 @@ const AttributesForm = ({ onSave, onCancel, description }: AttributesFormProps) const [showDisclaimer, setShowDisclaimer] = useState([]); const viewRoomsAction = useViewRoomsAction(); - const removeLockedAttribute = useEffectEvent(async (index: number) => { + const removeLockedAttribute = useStableCallback(async (index: number) => { const isInUse = await isAttributeUsed(); if (showDisclaimer.includes(index)) { return; diff --git a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx index 23401eb150b61..9838e5d1811ef 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx @@ -1,5 +1,5 @@ import { Box, Button, Icon, Margins, Pagination, TextInput } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTable, GenericTableBody, @@ -31,7 +31,7 @@ const AttributesPage = () => { const isABACAvailable = useIsABACAvailable(); const router = useRouter(); - const handleNewAttribute = useEffectEvent(() => { + const handleNewAttribute = useStableCallback(() => { router.navigate({ name: 'admin-ABAC', params: { diff --git a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomForm.tsx b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomForm.tsx index e08218bd40376..854919a86b153 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomForm.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomForm.tsx @@ -1,5 +1,5 @@ import { Box, Callout, Field, FieldLabel, FieldRow, FieldError, ButtonGroup, Button, ContextualbarFooter } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal, ContextualbarScrollableContent } from '@rocket.chat/ui-client'; import { useSetModal } from '@rocket.chat/ui-contexts'; import type { Dispatch, SetStateAction } from 'react'; @@ -42,7 +42,7 @@ const RoomForm = ({ onClose, onSave, roomInfo, setSelectedRoomLabel, redacted = const setModal = useSetModal(); - const updateAction = useEffectEvent(async (action: () => void) => { + const updateAction = useStableCallback(async (action: () => void) => { setModal( { + const handleSave = useStableCallback(() => { if (roomInfo) { updateAction(handleSubmit(onSave)); } else { diff --git a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx index dc627292ca31f..bfcb2df9b3900 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx @@ -1,5 +1,5 @@ import { Box, Button, Icon, Margins, Pagination, Select, TextInput } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTable, GenericTableBody, @@ -36,7 +36,7 @@ const RoomsPage = () => { const isABACAvailable = useIsABACAvailable(); const isExternalStore = useIsExternalAttributeStore(); - const handleNewAttribute = useEffectEvent(() => { + const handleNewAttribute = useStableCallback(() => { router.navigate({ name: 'admin-ABAC', params: { diff --git a/apps/meteor/client/views/admin/ABAC/AdminABACPage.tsx b/apps/meteor/client/views/admin/ABAC/AdminABACPage.tsx index a791f07a01d37..46f97254495ff 100644 --- a/apps/meteor/client/views/admin/ABAC/AdminABACPage.tsx +++ b/apps/meteor/client/views/admin/ABAC/AdminABACPage.tsx @@ -1,5 +1,5 @@ import { Box, Button, ButtonGroup, Callout } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarDialog, Page, PageContent, PageHeader } from '@rocket.chat/ui-client'; import { useSetting, useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import { Trans, useTranslation } from 'react-i18next'; @@ -37,7 +37,7 @@ const AdminABACPage = ({ shouldShowWarning }: AdminABACPageProps) => { const isSyncDisabled = !ldapEnabled || !abacEnabled; const tabPermissions = useABACTabPermissions(); - const handleCloseContextualbar = useEffectEvent((): void => { + const handleCloseContextualbar = useStableCallback((): void => { if (!context) { return; } diff --git a/apps/meteor/client/views/admin/ABAC/hooks/useAttributeOptions.tsx b/apps/meteor/client/views/admin/ABAC/hooks/useAttributeOptions.tsx index dc1dcf3101883..c0186a0bb8e78 100644 --- a/apps/meteor/client/views/admin/ABAC/hooks/useAttributeOptions.tsx +++ b/apps/meteor/client/views/admin/ABAC/hooks/useAttributeOptions.tsx @@ -1,5 +1,5 @@ import { Box } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { GenericModal } from '@rocket.chat/ui-client'; import { useRouter, useSetModal, useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; @@ -21,7 +21,7 @@ export const useAttributeOptions = (attribute: { _id: string; key: string }): Ge const isABACAvailable = useIsABACAvailable(); const viewRoomsAction = useViewRoomsAction(); - const editAction = useEffectEvent(() => { + const editAction = useStableCallback(() => { return router.navigate( { name: 'admin-ABAC', @@ -49,7 +49,7 @@ export const useAttributeOptions = (attribute: { _id: string; key: string }): Ge }, }); - const deleteAction = useEffectEvent(async () => { + const deleteAction = useStableCallback(async () => { const isUsed = await isAttributeUsed(); if (isUsed.inUse) { return setModal( diff --git a/apps/meteor/client/views/admin/ABAC/hooks/useRoomItems.tsx b/apps/meteor/client/views/admin/ABAC/hooks/useRoomItems.tsx index da0a91fe390de..05883606b0a07 100644 --- a/apps/meteor/client/views/admin/ABAC/hooks/useRoomItems.tsx +++ b/apps/meteor/client/views/admin/ABAC/hooks/useRoomItems.tsx @@ -1,5 +1,5 @@ import { Box } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { useRouter } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -13,7 +13,7 @@ export const useRoomItems = (room: { rid: string; name: string }): GenericMenuIt const setDeleteRoomModal = useDeleteRoomModal(room); const isABACAvailable = useIsABACAvailable(); - const editAction = useEffectEvent(() => { + const editAction = useStableCallback(() => { return router.navigate( { name: 'admin-ABAC', diff --git a/apps/meteor/client/views/admin/ABAC/hooks/useViewRoomsAction.ts b/apps/meteor/client/views/admin/ABAC/hooks/useViewRoomsAction.ts index 7edcbbbc97c89..d5faedfc3cfb1 100644 --- a/apps/meteor/client/views/admin/ABAC/hooks/useViewRoomsAction.ts +++ b/apps/meteor/client/views/admin/ABAC/hooks/useViewRoomsAction.ts @@ -1,9 +1,9 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRouter } from '@rocket.chat/ui-contexts'; export const useViewRoomsAction = () => { const router = useRouter(); - return useEffectEvent((key: string) => { + return useStableCallback((key: string) => { return router.navigate( { name: 'admin-ABAC', diff --git a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx index 187b4fb317bd5..e35ac9df9db23 100644 --- a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx @@ -2,7 +2,7 @@ import { Field, FieldLabel, FieldRow, TextInput, Box, Margins, Button, ButtonGro import { ContextualbarScrollableContent, ContextualbarFooter } from '@rocket.chat/ui-client'; import { useToastMessageDispatch, type UploadResult } from '@rocket.chat/ui-contexts'; import fileSize from 'filesize'; -import type { FormEvent } from 'react'; +import type { ChangeEvent } from 'react'; import { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -86,7 +86,7 @@ const AddCustomSound = ({ goToNew, close, onChange, ...props }: AddCustomSoundPr ): void => setName(e.currentTarget.value)} + onChange={(e: ChangeEvent): void => setName(e.currentTarget.value)} placeholder={t('Name')} /> diff --git a/apps/meteor/client/views/admin/customSounds/EditSound.tsx b/apps/meteor/client/views/admin/customSounds/EditSound.tsx index 54c338e444362..9fa3043b80ee8 100644 --- a/apps/meteor/client/views/admin/customSounds/EditSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/EditSound.tsx @@ -2,7 +2,7 @@ import { Box, Button, ButtonGroup, Margins, TextInput, Field, FieldLabel, FieldR import { GenericModal, ContextualbarScrollableContent, ContextualbarFooter } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import fileSize from 'filesize'; -import type { SyntheticEvent } from 'react'; +import type { ChangeEvent } from 'react'; import { useCallback, useState, useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -117,7 +117,7 @@ function EditSound({ close, onChange, data, ...props }: EditSoundProps) { ): void => setName(e.currentTarget.value)} + onChange={(e: ChangeEvent): void => setName(e.currentTarget.value)} placeholder={t('Name')} /> diff --git a/apps/meteor/client/views/admin/customUserStatus/hooks/useStatusDisabledModal.tsx b/apps/meteor/client/views/admin/customUserStatus/hooks/useStatusDisabledModal.tsx index 7cb32da3cd29a..4847051fc89e0 100644 --- a/apps/meteor/client/views/admin/customUserStatus/hooks/useStatusDisabledModal.tsx +++ b/apps/meteor/client/views/admin/customUserStatus/hooks/useStatusDisabledModal.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRole, useRoute, useSetModal } from '@rocket.chat/ui-contexts'; import CustomUserStatusDisabledModal from '../CustomUserStatusDisabledModal'; @@ -6,8 +6,8 @@ import CustomUserStatusDisabledModal from '../CustomUserStatusDisabledModal'; export const useStatusDisabledModal = () => { const userStatusRoute = useRoute('user-status'); const setModal = useSetModal(); - const closeModal = useEffectEvent(() => setModal()); - const handleGoToSettings = useEffectEvent(() => { + const closeModal = useStableCallback(() => setModal()); + const handleGoToSettings = useStableCallback(() => { userStatusRoute.push({ context: 'presence-service' }); closeModal(); }); diff --git a/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx b/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx index da0733645f0cc..70eb894b2d185 100644 --- a/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx +++ b/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx @@ -1,5 +1,5 @@ import { Box } from '@rocket.chat/fuselage'; -import { useMediaQuery, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useMediaQuery, useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { GenericMenu, GenericTableRow, GenericTableCell } from '@rocket.chat/ui-client'; import { useRoute } from '@rocket.chat/ui-contexts'; @@ -39,7 +39,7 @@ const DeviceManagementAdminRow = ({ const handleDeviceLogout = useDeviceLogout(_id, '/v1/sessions/logout'); - const handleClick = useEffectEvent((): void => { + const handleClick = useStableCallback((): void => { deviceManagementRouter.push({ context: 'info', id: _id, diff --git a/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx b/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx index 70c17b13fb3fc..37ef35b2e1dda 100644 --- a/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx +++ b/apps/meteor/client/views/admin/emailInbox/EmailInboxForm.tsx @@ -18,7 +18,7 @@ import { FieldError, FieldHint, } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { validateEmail } from '@rocket.chat/tools'; import { GenericModal, PageScrollableContentWithShadow } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useRoute, useEndpoint } from '@rocket.chat/ui-contexts'; @@ -93,7 +93,7 @@ const EmailInboxForm = ({ inboxData }: EmailInboxFormProps) => { mode: 'all', }); - const handleDelete = useEffectEvent(() => { + const handleDelete = useStableCallback(() => { const deleteInbox = async (): Promise => { try { await deleteInboxAction(); @@ -113,7 +113,7 @@ const EmailInboxForm = ({ inboxData }: EmailInboxFormProps) => { ); }); - const handleSave = useEffectEvent( + const handleSave = useStableCallback( async ({ active, name, @@ -172,7 +172,7 @@ const EmailInboxForm = ({ inboxData }: EmailInboxFormProps) => { }, ); - const checkEmailExists = useEffectEvent(async (email: string) => { + const checkEmailExists = useStableCallback(async (email: string) => { if (!email) { return; } diff --git a/apps/meteor/client/views/admin/import/ImportProgressPage.tsx b/apps/meteor/client/views/admin/import/ImportProgressPage.tsx index 2f80739b4b8d1..3a7cbf4df3552 100644 --- a/apps/meteor/client/views/admin/import/ImportProgressPage.tsx +++ b/apps/meteor/client/views/admin/import/ImportProgressPage.tsx @@ -1,6 +1,6 @@ import type { ProgressStep } from '@rocket.chat/core-typings'; import { Box, Margins, ProgressBar, Throbber } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { Page, PageHeader, PageScrollableContentWithShadow } from '@rocket.chat/ui-client'; import { useToastMessageDispatch, useEndpoint, useStream, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; @@ -43,7 +43,7 @@ const ImportProgressPage = function ImportProgressPage() { refetchInterval: 1000, }); - const handleProgressUpdated = useEffectEvent( + const handleProgressUpdated = useStableCallback( ({ key, step, completed, total }: { key: string; step: ProgressStep; completed: number; total: number }) => { if (!currentOperation.isSuccess) { return; diff --git a/apps/meteor/client/views/admin/import/NewImportPage.tsx b/apps/meteor/client/views/admin/import/NewImportPage.tsx index 8cd03322ed509..7fcc67e42cc33 100644 --- a/apps/meteor/client/views/admin/import/NewImportPage.tsx +++ b/apps/meteor/client/views/admin/import/NewImportPage.tsx @@ -19,7 +19,7 @@ import { Page, PageHeader, PageScrollableContentWithShadow } from '@rocket.chat/ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useToastMessageDispatch, useRouter, useRouteParameter, useSetting, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import type { ChangeEvent, DragEvent, FormEvent, Key, SyntheticEvent } from 'react'; +import type { ChangeEvent, DragEvent, Key, SyntheticEvent } from 'react'; import { useState, useMemo, useEffect, useId } from 'react'; import { useTranslation } from 'react-i18next'; @@ -146,7 +146,7 @@ function NewImportPage() { const [fileUrl, setFileUrl] = useSafely(useState('')); - const handleFileUrlChange = (event: FormEvent) => { + const handleFileUrlChange = (event: ChangeEvent) => { setFileUrl(event.currentTarget.value); }; @@ -170,7 +170,7 @@ function NewImportPage() { const [filePath, setFilePath] = useSafely(useState('')); - const handleFilePathChange = (event: FormEvent) => { + const handleFilePathChange = (event: ChangeEvent) => { setFilePath(event.currentTarget.value); }; diff --git a/apps/meteor/client/views/admin/import/useErrorHandler.ts b/apps/meteor/client/views/admin/import/useErrorHandler.ts index ce0f9e81f8736..5befa13c6103d 100644 --- a/apps/meteor/client/views/admin/import/useErrorHandler.ts +++ b/apps/meteor/client/views/admin/import/useErrorHandler.ts @@ -1,10 +1,10 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; export const useErrorHandler = () => { const dispatchToastMessage = useToastMessageDispatch(); - return useEffectEvent((error: unknown, defaultMessage?: unknown) => { + return useStableCallback((error: unknown, defaultMessage?: unknown) => { console.error(error); dispatchToastMessage({ type: 'error', message: error ?? defaultMessage }); diff --git a/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx b/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx index 74327d4968bf3..513f60429469b 100644 --- a/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx +++ b/apps/meteor/client/views/admin/integrations/outgoing/history/HistoryItem.tsx @@ -1,6 +1,6 @@ import type { IIntegrationHistory, Serialized } from '@rocket.chat/core-typings'; import { Button, Icon, Box, AccordionItem, Field, FieldGroup, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useMethod } from '@rocket.chat/ui-contexts'; import DOMPurify from 'dompurify'; import type { MouseEvent } from 'react'; @@ -36,7 +36,7 @@ const HistoryItem = ({ data }: { data: Serialized }) => { const createdAt = typeof _createdAt === 'string' ? _createdAt : (_createdAt as Date).toISOString(); const updatedAt = typeof _updatedAt === 'string' ? _updatedAt : (_updatedAt as Date).toISOString(); - const handleClickReplay = useEffectEvent((e: MouseEvent) => { + const handleClickReplay = useStableCallback((e: MouseEvent) => { e.stopPropagation(); replayOutgoingIntegration({ integrationId, historyId: _id }); }); diff --git a/apps/meteor/client/views/admin/moderation/ModConsoleReportDetails.tsx b/apps/meteor/client/views/admin/moderation/ModConsoleReportDetails.tsx index 490cff666bf53..92b4837a304bc 100644 --- a/apps/meteor/client/views/admin/moderation/ModConsoleReportDetails.tsx +++ b/apps/meteor/client/views/admin/moderation/ModConsoleReportDetails.tsx @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Tabs, TabsItem, ContextualbarHeader, ContextualbarTitle } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarClose, ContextualbarDialog } from '@rocket.chat/ui-client'; import { useTranslation, useRouter, useRouteParameter } from '@rocket.chat/ui-contexts'; import { useState } from 'react'; @@ -21,7 +21,7 @@ const ModConsoleReportDetails = ({ userId, default: defaultTab, onRedirect }: Mo const activeTab = useRouteParameter('tab'); - const handleCloseContextualbar = useEffectEvent(() => moderationRoute.navigate(`/admin/moderation/${activeTab}`, { replace: true })); + const handleCloseContextualbar = useStableCallback(() => moderationRoute.navigate(`/admin/moderation/${activeTab}`, { replace: true })); return ( diff --git a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx index 51f646d7ab0af..03208f1ca154e 100644 --- a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx +++ b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Pagination } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useMediaQuery, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useMediaQuery, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTable, GenericTableLoadingTable, @@ -63,7 +63,7 @@ const ModerationConsoleTable = () => { placeholderData: keepPreviousData, }); - const handleClick = useEffectEvent((id: IUser['_id']): void => { + const handleClick = useStableCallback((id: IUser['_id']): void => { router.navigate({ pattern: '/admin/moderation/:tab?/:context?/:id?', params: { diff --git a/apps/meteor/client/views/admin/moderation/UserMessages.tsx b/apps/meteor/client/views/admin/moderation/UserMessages.tsx index b0b9d9b50edf0..d8db785def5c1 100644 --- a/apps/meteor/client/views/admin/moderation/UserMessages.tsx +++ b/apps/meteor/client/views/admin/moderation/UserMessages.tsx @@ -1,5 +1,5 @@ import { Box, Callout, Message, StatesAction, StatesActions, StatesIcon, StatesTitle } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarFooter } from '@rocket.chat/ui-client'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; @@ -32,7 +32,7 @@ const UserMessages = ({ userId, onRedirect }: { userId: string; onRedirect: (mid }, }); - const handleChange = useEffectEvent(() => { + const handleChange = useStableCallback(() => { reloadUserMessages(); }); diff --git a/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx b/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx index 6a21d7d1ed85c..1b2491ca6d3b8 100644 --- a/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx +++ b/apps/meteor/client/views/admin/moderation/UserReports/ModConsoleUsersTable.tsx @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Pagination, States, StatesAction, StatesActions, StatesIcon, StatesTitle } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useMediaQuery, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useMediaQuery, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTable, GenericTableLoadingTable, @@ -59,7 +59,7 @@ const ModConsoleUsersTable = () => { placeholderData: keepPreviousData, }); - const handleClick = useEffectEvent((id: IUser['_id']): void => { + const handleClick = useStableCallback((id: IUser['_id']): void => { router.navigate({ pattern: '/admin/moderation/:tab?/:context?/:id?', params: { tab: 'users', context: 'info', id }, diff --git a/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx b/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx index 08419042dfebd..eda5a5abd43a3 100644 --- a/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx +++ b/apps/meteor/client/views/admin/moderation/helpers/DateRangePicker.tsx @@ -1,5 +1,5 @@ import { Select, Box, type SelectOption } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { subDays, subMonths, startOfMonth, endOfMonth, format } from 'date-fns'; import type { Key } from 'react'; import { useMemo, useEffect } from 'react'; @@ -33,7 +33,7 @@ const getWeekRange = (daysToSubtractFromStart: number, daysToSubtractFromEnd: nu const DateRangePicker = ({ onChange, defaultSelectedKey = 'alldates' }: DateRangePickerProps) => { const { t } = useTranslation(); - const handleRange = useEffectEvent((range: { start: string; end: string }) => { + const handleRange = useStableCallback((range: { start: string; end: string }) => { onChange(range); }); @@ -48,7 +48,7 @@ const DateRangePicker = ({ onChange, defaultSelectedKey = 'alldates' }: DateRang ].map(([value, label]) => [value, label] as SelectOption); }, [t]); - const handleOptionClick = useEffectEvent((action: Key) => { + const handleOptionClick = useStableCallback((action: Key) => { switch (action) { case 'today': handleRange(getWeekRange(0, 0)); diff --git a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx index 8a73018431fb6..281a394df6517 100644 --- a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx +++ b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx @@ -1,6 +1,6 @@ import type { IRole } from '@rocket.chat/core-typings'; import { Box, ButtonGroup, Button, Margins } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal, ContextualbarFooter, ContextualbarScrollableContent } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useRoute, useEndpoint } from '@rocket.chat/ui-contexts'; import { FormProvider, useForm } from 'react-hook-form'; @@ -37,7 +37,7 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool }, }); - const handleManageUsers = useEffectEvent(() => { + const handleManageUsers = useStableCallback(() => { if (role?._id) { usersInRoleRouter.push({ context: 'users-in-role', @@ -46,7 +46,7 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool } }); - const handleSave = useEffectEvent(async (data: EditRolePageFormData) => { + const handleSave = useStableCallback(async (data: EditRolePageFormData) => { try { if (data.roleId) { await updateRole(data); @@ -62,7 +62,7 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool } }); - const handleDelete = useEffectEvent(async () => { + const handleDelete = useStableCallback(async () => { if (!role?._id) { return; } diff --git a/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx b/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx index af1176e7ef23d..800db56b1e7a1 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarHeader, ContextualbarTitle, ContextualbarClose, ContextualbarDialog } from '@rocket.chat/ui-client'; import { useRouteParameter, useRoute, useTranslation, useSetModal } from '@rocket.chat/ui-contexts'; import { useEffect } from 'react'; @@ -15,7 +15,7 @@ const PermissionsContextBar = () => { const setModal = useSetModal(); const { isPending, data: hasCustomRolesModule = false } = useHasLicenseModule('custom-roles'); - const handleCloseContextualbar = useEffectEvent(() => { + const handleCloseContextualbar = useStableCallback(() => { router.push({}); }); diff --git a/apps/meteor/client/views/admin/permissions/PermissionsPage.tsx b/apps/meteor/client/views/admin/permissions/PermissionsPage.tsx index 8c4061dc00d75..dbe69ffb5cea1 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsPage.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsPage.tsx @@ -1,5 +1,5 @@ import { Margins, Tabs, Button } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { usePagination, Page, PageHeader, PageContent } from '@rocket.chat/ui-client'; import { useRoute, usePermission, useSetModal } from '@rocket.chat/ui-contexts'; import { useState } from 'react'; @@ -23,21 +23,21 @@ const PermissionsPage = ({ isEnterprise }: { isEnterprise: boolean }) => { const paginationData = usePagination(); const { permissions, total, roleList } = usePermissionsAndRoles(type, filter, paginationData.itemsPerPage, paginationData.current); - const handlePermissionsTab = useEffectEvent(() => { + const handlePermissionsTab = useStableCallback(() => { if (type === 'permissions') { return; } setType('permissions'); }); - const handleSettingsTab = useEffectEvent(() => { + const handleSettingsTab = useStableCallback(() => { if (type === 'settings') { return; } setType('settings'); }); - const handleAdd = useEffectEvent(() => { + const handleAdd = useStableCallback(() => { if (!isEnterprise) { setModal( setModal(null)} />); return; diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx index 0137b18a47c95..fe371671a816b 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTableFilter.tsx @@ -1,6 +1,6 @@ import { TextInput } from '@rocket.chat/fuselage'; -import { useEffectEvent, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; -import type { FormEvent } from 'react'; +import { useStableCallback, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; +import type { ChangeEvent } from 'react'; import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -13,7 +13,7 @@ const PermissionsTableFilter = ({ onChange }: { onChange: (debouncedFilter: stri onChange(debouncedFilter); }, [debouncedFilter, onChange]); - const handleFilter = useEffectEvent(({ currentTarget: { value } }: FormEvent) => { + const handleFilter = useStableCallback(({ currentTarget: { value } }: ChangeEvent) => { setFilter(value); }); diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleCell.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleCell.tsx index 87714561fb523..54b71f6e8e47e 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleCell.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleCell.tsx @@ -1,6 +1,6 @@ import type { IRole } from '@rocket.chat/core-typings'; import { Margins, Box, CheckBox, Throbber } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal, GenericTableCell } from '@rocket.chat/ui-client'; import { useSetModal } from '@rocket.chat/ui-contexts'; import { useState, memo } from 'react'; @@ -27,7 +27,7 @@ const RoleCell = ({ _id, name, description, onChange, permissionId, permissionNa const isRestrictedForRole = AuthorizationUtils.isPermissionRestrictedForRole(permissionId, _id); const shouldDisplayConfirmation = confirmationRequiredPermissions.includes(permissionId) && grantedRoles.length === 1 && granted; - const handleChange = useEffectEvent(() => { + const handleChange = useStableCallback(() => { if (shouldDisplayConfirmation) { const handleSubmit = () => { handleConfirmChange(); @@ -44,7 +44,7 @@ const RoleCell = ({ _id, name, description, onChange, permissionId, permissionNa return handleConfirmChange(); }); - const handleConfirmChange = useEffectEvent(async () => { + const handleConfirmChange = useStableCallback(async () => { setLoading(true); const result = await onChange(_id, granted); setGranted(result); diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx index 257a6b43a5cc4..c7447ad589347 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx @@ -1,6 +1,6 @@ import type { IRole } from '@rocket.chat/core-typings'; import { Button } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTableHeaderCell } from '@rocket.chat/ui-client'; import { useRoute } from '@rocket.chat/ui-contexts'; import { memo } from 'react'; @@ -14,7 +14,7 @@ type RoleHeaderProps = { const RoleHeader = ({ _id, name, description }: RoleHeaderProps) => { const router = useRoute('admin-permissions'); - const handleEditRole = useEffectEvent(() => { + const handleEditRole = useStableCallback(() => { router.push({ context: 'edit', _id, diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx index 26ec249dcdd06..17c7801b9941d 100644 --- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx +++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRolePage.tsx @@ -1,6 +1,6 @@ import type { IRole, IRoom } from '@rocket.chat/core-typings'; import { Box, Field, FieldLabel, FieldRow, Margins, ButtonGroup, Button, Callout, FieldError } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { usePagination, Page, PageHeader, PageContent } from '@rocket.chat/ui-client'; import { useToastMessageDispatch, useEndpoint, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery, useQueryClient } from '@tanstack/react-query'; @@ -38,7 +38,7 @@ const UsersInRolePage = ({ role }: { role: IRole }) => { const roomFieldId = useId(); const usersFieldId = useId(); - const handleAdd = useEffectEvent(async ({ users, rid }: UsersInRolePayload) => { + const handleAdd = useStableCallback(async ({ users, rid }: UsersInRolePayload) => { try { await Promise.all( users.map(async (user) => { diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx index a306d58f8f3be..7c10c9ec72382 100644 --- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx +++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx @@ -1,6 +1,6 @@ import type { IUserInRole, Serialized } from '@rocket.chat/core-typings'; import { Box, IconButton } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { GenericTableRow, GenericTableCell } from '@rocket.chat/ui-client'; import { memo } from 'react'; @@ -18,7 +18,7 @@ const UsersInRoleTableRow = ({ user, onRemove }: UsersInRoleTableRowProps) => { const { _id, name, username, avatarETag } = user; const email = getUserEmailAddress(user); - const handleRemove = useEffectEvent(() => { + const handleRemove = useStableCallback(() => { onRemove(username); }); diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/hooks/useRemoveUserFromRole.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/hooks/useRemoveUserFromRole.tsx index 14d188058246a..debb28f5e9e77 100644 --- a/apps/meteor/client/views/admin/permissions/UsersInRole/hooks/useRemoveUserFromRole.tsx +++ b/apps/meteor/client/views/admin/permissions/UsersInRole/hooks/useRemoveUserFromRole.tsx @@ -1,5 +1,5 @@ import type { IRole, IRoom, IUserInRole } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -24,7 +24,7 @@ export const useRemoveUserFromRole = ({ const removeUserFromRoleEndpoint = useEndpoint('POST', '/v1/roles.removeUserFromRole'); - const handleRemove = useEffectEvent((username: IUserInRole['username']) => { + const handleRemove = useStableCallback((username: IUserInRole['username']) => { const remove = async () => { try { if (!username) throw new Error('Username is required'); diff --git a/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts b/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts index 0537131c46d96..a42f811533955 100644 --- a/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts +++ b/apps/meteor/client/views/admin/permissions/hooks/useChangeRole.ts @@ -1,5 +1,5 @@ import type { IRole, IPermission } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; export const useChangeRole = ({ @@ -13,7 +13,7 @@ export const useChangeRole = ({ }): ((roleId: IRole['_id'], granted: boolean) => Promise) => { const dispatchToastMessage = useToastMessageDispatch(); - return useEffectEvent(async (roleId: IRole['_id'], granted: boolean) => { + return useStableCallback(async (roleId: IRole['_id'], granted: boolean) => { try { if (granted) { await onRemove(permissionId, roleId); diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 7b5de5c711c92..0123a800fdefe 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -13,7 +13,7 @@ import { TextAreaInput, FieldError, } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarScrollableContent, ContextualbarFooter } from '@rocket.chat/ui-client'; import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useId } from 'react'; @@ -97,7 +97,7 @@ const EditRoom = ({ room, onChange, onDelete, onClose }: EditRoomProps) => { const handleArchive = useArchiveRoom(room); - const handleUpdateRoomData = useEffectEvent(async ({ isDefault, favorite, ...formData }: EditRoomFormData) => { + const handleUpdateRoomData = useStableCallback(async ({ isDefault, favorite, ...formData }: EditRoomFormData) => { const data = getDirtyFields(formData, dirtyFields); delete data.archived; @@ -117,7 +117,7 @@ const EditRoom = ({ room, onChange, onDelete, onClose }: EditRoomProps) => { } }); - const handleSave = useEffectEvent((data: EditRoomFormData) => + const handleSave = useStableCallback((data: EditRoomFormData) => Promise.all([isDirty && handleUpdateRoomData(data), changeArchiving && handleArchive()].filter(Boolean)), ); diff --git a/apps/meteor/client/views/admin/rooms/RoomsPage.tsx b/apps/meteor/client/views/admin/rooms/RoomsPage.tsx index 4ebc9aaa917e0..051dc42dc909c 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsPage.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsPage.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarDialog, Page, PageHeader, PageContent } from '@rocket.chat/ui-client'; import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import { useRef } from 'react'; @@ -14,7 +14,7 @@ const RoomsPage = () => { const context = useRouteParameter('context'); const reloadRef = useRef(() => null); - const handleCloseContextualbar = useEffectEvent(() => router.navigate('/admin/rooms')); + const handleCloseContextualbar = useStableCallback(() => router.navigate('/admin/rooms')); return ( diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/BooleanSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/BooleanSettingInput.tsx index acb4abb5c0db3..71c4207367d83 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/BooleanSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/BooleanSettingInput.tsx @@ -1,5 +1,5 @@ import { Box, Field, FieldHint, FieldLabel, FieldRow, ToggleSwitch } from '@rocket.chat/fuselage'; -import type { SyntheticEvent } from 'react'; +import type { ChangeEvent } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -18,7 +18,7 @@ function BooleanSettingInput({ onChangeValue, onResetButtonClick, }: BooleanSettingInputProps) { - const handleChange = (event: SyntheticEvent): void => { + const handleChange = (event: ChangeEvent): void => { const value = event.currentTarget.checked; onChangeValue?.(value); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx index a58cd35c3c8db..183cc885c26ab 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/CodeMirror/CodeMirror.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { Editor, EditorFromTextArea } from 'codemirror'; import { useCallback, useEffect, useRef, useState } from 'react'; @@ -43,7 +43,7 @@ function CodeMirror({ ...props }: CodeMirrorProps) { const [value, setValue] = useState(valueProp || defaultValue); - const handleChange = useEffectEvent(onChange); + const handleChange = useStableCallback(onChange); const editorRef = useRef(null); const textAreaRef = useCallback( diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/FontSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/FontSettingInput.tsx index 815bc657b71e6..ba65bda228428 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/FontSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/FontSettingInput.tsx @@ -1,5 +1,5 @@ import { Field, FieldHint, FieldLabel, FieldRow, TextInput } from '@rocket.chat/fuselage'; -import type { FormEventHandler } from 'react'; +import type { ChangeEventHandler } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -22,7 +22,7 @@ function FontSettingInput({ onChangeValue, onResetButtonClick, }: FontSettingInputProps) { - const handleChange: FormEventHandler = (event): void => { + const handleChange: ChangeEventHandler = (event): void => { onChangeValue?.(event.currentTarget.value); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/GenericSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/GenericSettingInput.tsx index 0e5e901255d73..e2e93c8f01a54 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/GenericSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/GenericSettingInput.tsx @@ -1,5 +1,5 @@ import { Field, FieldHint, FieldLabel, FieldRow, TextInput } from '@rocket.chat/fuselage'; -import type { FormEventHandler } from 'react'; +import type { ChangeEventHandler } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -22,7 +22,7 @@ function GenericSettingInput({ onChangeValue, onResetButtonClick, }: GenericSettingInputProps) { - const handleChange: FormEventHandler = (event): void => { + const handleChange: ChangeEventHandler = (event): void => { onChangeValue?.(event.currentTarget.value); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/IntSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/IntSettingInput.tsx index 7b3747ef3f474..8fda81726d045 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/IntSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/IntSettingInput.tsx @@ -1,5 +1,5 @@ import { Field, FieldHint, FieldLabel, FieldRow, InputBox } from '@rocket.chat/fuselage'; -import type { FormEventHandler } from 'react'; +import type { ChangeEventHandler } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -22,7 +22,7 @@ function IntSettingInput({ hasResetButton, onResetButtonClick, }: IntSettingInputProps) { - const handleChange: FormEventHandler = (event) => { + const handleChange: ChangeEventHandler = (event) => { onChangeValue?.(parseInt(event.currentTarget.value, 10)); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/PasswordSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/PasswordSettingInput.tsx index c3e98e3fd551e..9bc582bc83855 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/PasswordSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/PasswordSettingInput.tsx @@ -1,5 +1,5 @@ import { Field, FieldHint, FieldLabel, FieldRow, PasswordInput } from '@rocket.chat/fuselage'; -import type { EventHandler, SyntheticEvent } from 'react'; +import type { ChangeEventHandler } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -20,7 +20,7 @@ function PasswordSettingInput({ onChangeValue, onResetButtonClick, }: PasswordSettingInputProps) { - const handleChange: EventHandler> = (event) => { + const handleChange: ChangeEventHandler = (event) => { onChangeValue?.(event.currentTarget.value); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/RelativeUrlSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/RelativeUrlSettingInput.tsx index bf2eb629b94f3..78e42d2ed0239 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/RelativeUrlSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/RelativeUrlSettingInput.tsx @@ -1,6 +1,6 @@ import { Field, FieldHint, FieldLabel, FieldRow, UrlInput } from '@rocket.chat/fuselage'; import { useAbsoluteUrl } from '@rocket.chat/ui-contexts'; -import type { EventHandler, SyntheticEvent } from 'react'; +import type { ChangeEventHandler } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -23,7 +23,7 @@ function RelativeUrlSettingInput({ }: RelativeUrlSettingInputProps) { const getAbsoluteUrl = useAbsoluteUrl(); - const handleChange: EventHandler> = (event) => { + const handleChange: ChangeEventHandler = (event) => { onChangeValue?.(event.currentTarget.value); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/StringSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/StringSettingInput.tsx index 8db88c13f514e..2c096cbc4dee7 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/StringSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/StringSettingInput.tsx @@ -1,5 +1,5 @@ import { Field, FieldHint, FieldLabel, FieldRow, TextAreaInput, TextInput } from '@rocket.chat/fuselage'; -import type { EventHandler, SyntheticEvent } from 'react'; +import type { ChangeEventHandler } from 'react'; import ResetSettingButton from '../ResetSettingButton'; import type { SettingInputProps } from './types'; @@ -27,7 +27,7 @@ function StringSettingInput({ onChangeValue, onResetButtonClick, }: StringSettingInputProps) { - const handleChange: EventHandler> = (event) => { + const handleChange: ChangeEventHandler = (event) => { onChangeValue?.(event.currentTarget.value); }; diff --git a/apps/meteor/client/views/admin/settings/Setting/inputs/TimespanSettingInput.tsx b/apps/meteor/client/views/admin/settings/Setting/inputs/TimespanSettingInput.tsx index 8ec08a18f22a6..7a65556a4b7b7 100644 --- a/apps/meteor/client/views/admin/settings/Setting/inputs/TimespanSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/inputs/TimespanSettingInput.tsx @@ -1,5 +1,5 @@ import { Field, FieldHint, FieldLabel, FieldRow, InputBox, Select } from '@rocket.chat/fuselage'; -import type { FormEventHandler, Key } from 'react'; +import type { ChangeEventHandler, Key } from 'react'; import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -55,7 +55,7 @@ function TimespanSettingInput({ const [timeUnit, setTimeUnit] = useState(getHighestTimeUnit(Number(value))); const [internalValue, setInternalValue] = useState(msToTimeUnit(timeUnit, Number(value))); - const handleChange: FormEventHandler = (event) => { + const handleChange: ChangeEventHandler = (event) => { const newValue = sanitizeInputValue(Number(event.currentTarget.value)); onChangeValue?.(timeUnitToMs(timeUnit, newValue)); diff --git a/apps/meteor/client/views/admin/settings/SettingsGroupPage/SettingsGroupPage.tsx b/apps/meteor/client/views/admin/settings/SettingsGroupPage/SettingsGroupPage.tsx index 10947f5758f28..f4b5da5304e44 100644 --- a/apps/meteor/client/views/admin/settings/SettingsGroupPage/SettingsGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/SettingsGroupPage/SettingsGroupPage.tsx @@ -1,10 +1,10 @@ import type { ISetting, ISettingColor } from '@rocket.chat/core-typings'; import { Accordion, Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { Page, PageHeader, PageScrollableContentWithShadow, PageFooter } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useToastMessageDispatch, useSettingsDispatch, useSettings } from '@rocket.chat/ui-contexts'; -import type { ReactNode, FormEvent, MouseEvent } from 'react'; +import type { ReactNode, MouseEvent, FormEvent } from 'react'; import { useMemo, memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -57,7 +57,7 @@ const SettingsGroupPage = ({ const isColorSetting = (setting: ISetting): setting is ISettingColor => setting.type === 'color'; - const save = useEffectEvent(async () => { + const save = useStableCallback(async () => { const changes = changedEditableSettings.map((setting) => { if (isColorSetting(setting)) { return { @@ -87,7 +87,7 @@ const SettingsGroupPage = ({ const dispatchToEditing = useEditableSettingsDispatch(); - const cancel = useEffectEvent(() => { + const cancel = useStableCallback(() => { const settingsToDispatch = changedEditableSettings .map(({ _id }) => originalSettings.find((setting) => setting._id === _id)) .map((setting) => { diff --git a/apps/meteor/client/views/admin/settings/SettingsSection/SettingsSection.tsx b/apps/meteor/client/views/admin/settings/SettingsSection/SettingsSection.tsx index b2aca79c78234..2ace5e246d1ec 100644 --- a/apps/meteor/client/views/admin/settings/SettingsSection/SettingsSection.tsx +++ b/apps/meteor/client/views/admin/settings/SettingsSection/SettingsSection.tsx @@ -1,6 +1,6 @@ import { isSetting, isSettingColor } from '@rocket.chat/core-typings'; import { AccordionItem, Box, Button, FieldGroup } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ReactNode } from 'react'; import { useMemo } from 'react'; @@ -42,7 +42,7 @@ function SettingsSection({ groupId, hasReset = true, sectionName, currentTab, so const dispatch = useEditableSettingsDispatch(); - const reset = useEffectEvent(() => { + const reset = useStableCallback(() => { dispatch( editableSettings .filter(({ disabled }) => !disabled) diff --git a/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx index e862afaf03ecb..2e323d4962354 100644 --- a/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx @@ -1,9 +1,9 @@ import type { ISetting } from '@rocket.chat/core-typings'; import { Button, Box, TextInput, Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useSetting, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { FormEvent } from 'react'; +import type { ChangeEvent } from 'react'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -24,7 +24,7 @@ function LDAPGroupPage({ _id, i18nLabel, onClickBack, ...group }: LDAPGroupPageP const testSearch = useEndpoint('POST', '/v1/ldap.testSearch'); const ldapEnabled = useSetting('LDAP_Enable'); const setModal = useSetModal(); - const closeModal = useEffectEvent(() => setModal()); + const closeModal = useStableCallback(() => setModal()); const handleSyncNow = useLdapSync(); const handleLinkClick = useExternalLink(); @@ -53,7 +53,7 @@ function LDAPGroupPage({ _id, i18nLabel, onClickBack, ...group }: LDAPGroupPageP try { await testConnection(); let username = ''; - const handleChangeUsername = (event: FormEvent): void => { + const handleChangeUsername = (event: ChangeEvent): void => { username = event.currentTarget.value; }; @@ -71,7 +71,7 @@ function LDAPGroupPage({ _id, i18nLabel, onClickBack, ...group }: LDAPGroupPageP wrapperFunction={(props) => ( { + onSubmit={(e) => { e.preventDefault(); confirmSearch(); }} diff --git a/apps/meteor/client/views/admin/users/AdminUserForm.tsx b/apps/meteor/client/views/admin/users/AdminUserForm.tsx index a20d7d0afd294..2816f2d1b0400 100644 --- a/apps/meteor/client/views/admin/users/AdminUserForm.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserForm.tsx @@ -17,7 +17,7 @@ import { Skeleton, } from '@rocket.chat/fuselage'; import type { SelectOption } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { UserCreateParamsPOST } from '@rocket.chat/rest-typings'; import { validateEmail } from '@rocket.chat/tools'; import { CustomFieldsForm, ContextualbarScrollableContent, ContextualbarFooter } from '@rocket.chat/ui-client'; @@ -170,7 +170,7 @@ const AdminUserForm = ({ userData, onReload, context, refetchUserFormData, roleD }, }); - const handleSaveUser = useEffectEvent(async (userFormPayload: UserFormProps) => { + const handleSaveUser = useStableCallback(async (userFormPayload: UserFormProps) => { const { avatar, passwordConfirmation, ...userFormData } = userFormPayload; if (!isNewUserPage && userData?._id) { diff --git a/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx b/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx index 5ac84d9c55e92..342d2752a6972 100644 --- a/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx @@ -1,6 +1,6 @@ import type { IRole, IUser, Serialized } from '@rocket.chat/core-typings'; import { Box, Callout } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from 'react-i18next'; import AdminUserForm from './AdminUserForm'; @@ -19,7 +19,7 @@ const AdminUserFormWithData = ({ uid, onReload, context, roleData, roleError }: const { t } = useTranslation(); const { data, isPending, isError, refetch } = useUserInfoQuery({ userId: uid }); - const handleReload = useEffectEvent(() => { + const handleReload = useStableCallback(() => { onReload(); refetch(); }); diff --git a/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx b/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx index 118025907bdf1..bd4aafe002f4d 100644 --- a/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserInfoWithData.tsx @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Callout } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarContent } from '@rocket.chat/ui-client'; import { useSetting, useRolesDescription, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; @@ -41,7 +41,7 @@ const AdminUserInfoWithData = ({ uid, onReload, tab }: AdminUserInfoWithDataProp }, }); - const onChange = useEffectEvent(() => { + const onChange = useStableCallback(() => { onReload(); refetch(); }); diff --git a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx index a8e5a0325f15e..2fe17c50a6e60 100644 --- a/apps/meteor/client/views/admin/users/AdminUsersPage.tsx +++ b/apps/meteor/client/views/admin/users/AdminUsersPage.tsx @@ -1,6 +1,6 @@ import type { LicenseInfo } from '@rocket.chat/core-typings'; import { Callout, ContextualbarIcon, Skeleton, Tabs, TabsItem } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { OptionProp } from '@rocket.chat/ui-client'; import { ExternalLink, @@ -96,7 +96,7 @@ const AdminUsersPage = () => { sortData.setSort(tab === 'pending' ? 'active' : 'name', 'asc'); }; - const handleCloseContextualbar = useEffectEvent(() => router.navigate('/admin/users')); + const handleCloseContextualbar = useStableCallback(() => router.navigate('/admin/users')); useEffect(() => { prevSearchTerm.current = searchTerm; diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx index 8f752e0385af1..38983b5f0df2c 100644 --- a/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx +++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx @@ -1,6 +1,6 @@ import type { IRole, IUser, Serialized } from '@rocket.chat/core-typings'; import { Pagination } from '@rocket.chat/fuselage'; -import { useEffectEvent, useBreakpoints } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useBreakpoints } from '@rocket.chat/fuselage-hooks'; import type { DefaultUserInfo } from '@rocket.chat/rest-typings'; import { GenericTable, @@ -65,7 +65,7 @@ const UsersTable = ({ return (event as KeyboardEvent).key !== undefined; }; - const handleClickOrKeyDown = useEffectEvent((id: IUser['_id'], e: MouseEvent | KeyboardEvent): void => { + const handleClickOrKeyDown = useStableCallback((id: IUser['_id'], e: MouseEvent | KeyboardEvent): void => { e.stopPropagation(); const keyboardSubmitKeys = ['Enter', ' ']; diff --git a/apps/meteor/client/views/admin/users/UsersTable/UsersTableFilters.tsx b/apps/meteor/client/views/admin/users/UsersTable/UsersTableFilters.tsx index 42e007de6f508..e43bc1a6c73d4 100644 --- a/apps/meteor/client/views/admin/users/UsersTable/UsersTableFilters.tsx +++ b/apps/meteor/client/views/admin/users/UsersTable/UsersTableFilters.tsx @@ -3,7 +3,7 @@ import { Box, Icon, Margins, TextInput } from '@rocket.chat/fuselage'; import { useBreakpoints } from '@rocket.chat/fuselage-hooks'; import type { OptionProp } from '@rocket.chat/ui-client'; import { MultiSelectCustom } from '@rocket.chat/ui-client'; -import type { Dispatch, FormEvent, SetStateAction } from 'react'; +import type { ChangeEvent, Dispatch, SetStateAction } from 'react'; import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -21,7 +21,7 @@ const UsersTableFilters = ({ roleData, setUsersFilters }: UsersTableFiltersProps const [text, setText] = useState(''); const handleSearchTextChange = useCallback( - (event: FormEvent) => { + (event: ChangeEvent) => { setText(event.currentTarget.value); setUsersFilters({ text: event.currentTarget.value, roles: selectedRoles }); }, @@ -66,7 +66,7 @@ const UsersTableFilters = ({ roleData, setUsersFilters }: UsersTableFiltersProps ) => { + onSubmit={(event) => { event.preventDefault(); }} display='flex' diff --git a/apps/meteor/client/views/admin/users/hooks/useDeleteUserAction.tsx b/apps/meteor/client/views/admin/users/hooks/useDeleteUserAction.tsx index 410bd7efbbf90..0fe17d6fbdbca 100644 --- a/apps/meteor/client/views/admin/users/hooks/useDeleteUserAction.tsx +++ b/apps/meteor/client/views/admin/users/hooks/useDeleteUserAction.tsx @@ -1,5 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { @@ -56,7 +56,7 @@ export const useDeleteUserAction = (userId: IUser['_id'], onChange: () => void, onChange, ); - const confirmDeleteUser = useEffectEvent(() => { + const confirmDeleteUser = useStableCallback(() => { setModal( setModal()} confirmText={t('Delete')}> {t(`Delete_User_Warning_${erasureType}` as TranslationKey)} diff --git a/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.tsx b/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.tsx index e30cc3e721546..d9398865d7778 100644 --- a/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.tsx +++ b/apps/meteor/client/views/admin/workspace/DeploymentCard/DeploymentCard.tsx @@ -1,6 +1,6 @@ import type { IWorkspaceInfo, IStats } from '@rocket.chat/core-typings'; import { Button, Card, CardBody, CardControls, Margins } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { IInstance } from '@rocket.chat/rest-typings'; import { useSetModal } from '@rocket.chat/ui-contexts'; import { memo } from 'react'; @@ -24,7 +24,7 @@ const DeploymentCard = ({ serverInfo: { info, cloudWorkspaceId }, statistics, in const { commit = {}, marketplaceApiVersion: appsEngineVersion } = info || {}; - const handleInstancesModal = useEffectEvent(() => { + const handleInstancesModal = useStableCallback(() => { setModal( setModal()} />); }); diff --git a/apps/meteor/client/views/admin/workspace/UsersUploadsCard/UsersUploadsCard.tsx b/apps/meteor/client/views/admin/workspace/UsersUploadsCard/UsersUploadsCard.tsx index e0095feb37cf4..356f2290e6f49 100644 --- a/apps/meteor/client/views/admin/workspace/UsersUploadsCard/UsersUploadsCard.tsx +++ b/apps/meteor/client/views/admin/workspace/UsersUploadsCard/UsersUploadsCard.tsx @@ -1,6 +1,6 @@ import type { IStats } from '@rocket.chat/core-typings'; import { Button, Card, CardBody, CardControls, Margins } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRouter } from '@rocket.chat/ui-contexts'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -21,7 +21,7 @@ const UsersUploadsCard = ({ statistics }: UsersUploadsCardProps) => { const router = useRouter(); - const handleEngagement = useEffectEvent(() => { + const handleEngagement = useStableCallback(() => { router.navigate('/admin/engagement'); }); diff --git a/apps/meteor/client/views/admin/workspace/VersionCard/components/VersionCardActionButton.tsx b/apps/meteor/client/views/admin/workspace/VersionCard/components/VersionCardActionButton.tsx index 0f9431c6658c2..cd7bb003c8777 100644 --- a/apps/meteor/client/views/admin/workspace/VersionCard/components/VersionCardActionButton.tsx +++ b/apps/meteor/client/views/admin/workspace/VersionCard/components/VersionCardActionButton.tsx @@ -1,5 +1,5 @@ import { Button } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { LocationPathname } from '@rocket.chat/ui-contexts'; import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactNode } from 'react'; @@ -18,7 +18,7 @@ type VersionCardActionButtonProps = const VersionCardActionButton = (item: VersionCardActionButtonProps) => { const router = useRouter(); - const handleActionButton = useEffectEvent(() => { + const handleActionButton = useStableCallback(() => { if ('action' in item) { return item.action(); } diff --git a/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx b/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx index da65923a97234..a593fb3a9b55e 100644 --- a/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx +++ b/apps/meteor/client/views/audit/components/forms/DateRangePicker.tsx @@ -1,8 +1,8 @@ import { Box, InputBox, Margins } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericMenu } from '@rocket.chat/ui-client'; import { startOfDay, endOfDay, startOfWeek, endOfWeek, startOfMonth, endOfMonth, subDays, subWeeks, subMonths, parseISO } from 'date-fns'; -import type { ComponentProps, SetStateAction, FormEvent } from 'react'; +import type { ComponentProps, SetStateAction, ChangeEvent } from 'react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -125,16 +125,16 @@ type DateRangePickerProps = Omit, 'value' | 'onChange const minDate = (a: Date, b: Date) => (a.getTime() < b.getTime() ? a : b); const DateRangePicker = ({ value, onChange, ...props }: DateRangePickerProps) => { - const dispatch = useEffectEvent((action: DateRangeAction): void => { + const dispatch = useStableCallback((action: DateRangeAction): void => { const newRange = dateRangeReducer(value ?? { start: undefined, end: undefined }, action); onChange?.(newRange); }); - const handleChangeStart = useEffectEvent(({ currentTarget }: FormEvent) => { + const handleChangeStart = useStableCallback(({ currentTarget }: ChangeEvent) => { dispatch({ newStart: currentTarget.value }); }); - const handleChangeEnd = useEffectEvent(({ currentTarget }: FormEvent) => { + const handleChangeEnd = useStableCallback(({ currentTarget }: ChangeEvent) => { dispatch({ newEnd: currentTarget.value }); }); diff --git a/apps/meteor/client/views/audit/hooks/useAuditTab.ts b/apps/meteor/client/views/audit/hooks/useAuditTab.ts index 42d24c5af3c00..363302a770412 100644 --- a/apps/meteor/client/views/audit/hooks/useAuditTab.ts +++ b/apps/meteor/client/views/audit/hooks/useAuditTab.ts @@ -1,5 +1,5 @@ import type { IAuditLog } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute, useRouteParameter } from '@rocket.chat/ui-contexts'; import type { SetStateAction } from 'react'; import { useMemo } from 'react'; @@ -19,7 +19,7 @@ export const useAuditTab = () => { const auditRoute = useRoute('audit-home'); - const setType = useEffectEvent((newType: SetStateAction) => { + const setType = useStableCallback((newType: SetStateAction) => { auditRoute.replace({ tab: typeToTabMap[typeof newType === 'function' ? newType(type) : newType] ?? 'rooms' }); }); diff --git a/apps/meteor/client/views/banners/UiKitBanner.tsx b/apps/meteor/client/views/banners/UiKitBanner.tsx index 523efd7a9eda8..22d316e144179 100644 --- a/apps/meteor/client/views/banners/UiKitBanner.tsx +++ b/apps/meteor/client/views/banners/UiKitBanner.tsx @@ -1,5 +1,5 @@ import { Banner, Icon } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { UiKitContext, bannerParser, UiKitBanner as UiKitBannerSurfaceRender, UiKitComponent } from '@rocket.chat/fuselage-ui-kit'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type * as UiKit from '@rocket.chat/ui-kit'; @@ -32,7 +32,7 @@ const UiKitBanner = ({ initialView }: UiKitBannerProps) => { }, [view.icon]); const dispatchToastMessage = useToastMessageDispatch(); - const handleClose = useEffectEvent(() => { + const handleClose = useStableCallback(() => { void actionManager .emitInteraction(view.appId, { type: 'viewClosed', diff --git a/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx b/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx index 211f2c2f84200..3cc442eca3e48 100644 --- a/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx +++ b/apps/meteor/client/views/composer/AudioMessageRecorder/AudioMessageRecorder.tsx @@ -1,6 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { Box, Icon, Throbber } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { MessageComposerAction } from '@rocket.chat/ui-composer'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -23,7 +23,7 @@ const AudioMessageRecorder = ({ rid, isMicrophoneDenied }: AudioMessageRecorderP const [recordingInterval, setRecordingInterval] = useState | null>(null); const [recordingRoomId, setRecordingRoomId] = useState(null); - const stopRecording = useEffectEvent(async () => { + const stopRecording = useStableCallback(async () => { if (recordingInterval) { clearInterval(recordingInterval); } @@ -41,13 +41,13 @@ const AudioMessageRecorder = ({ rid, isMicrophoneDenied }: AudioMessageRecorderP return blob; }); - const handleUnmount = useEffectEvent(async () => { + const handleUnmount = useStableCallback(async () => { if (state === 'recording') { await stopRecording(); } }); - const handleRecord = useEffectEvent(async () => { + const handleRecord = useStableCallback(async () => { chat?.composer?.setRecordingMode(true); if (recordingRoomId && recordingRoomId !== rid) { @@ -74,13 +74,13 @@ const AudioMessageRecorder = ({ rid, isMicrophoneDenied }: AudioMessageRecorderP } }); - const handleCancelButtonClick = useEffectEvent(async () => { + const handleCancelButtonClick = useStableCallback(async () => { await stopRecording(); }); const chat = useChat(); - const handleDoneButtonClick = useEffectEvent(async () => { + const handleDoneButtonClick = useStableCallback(async () => { setState('loading'); const blob = await stopRecording(); diff --git a/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx b/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx index 8a7131ab41fab..6a551712c2dc5 100644 --- a/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx +++ b/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx @@ -1,7 +1,7 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, ButtonGroup, Button, Icon, PositionAnimated } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useTranslation, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { AllHTMLAttributes, RefObject } from 'react'; import { useRef, useEffect, useState } from 'react'; @@ -94,7 +94,7 @@ const VideoMessageRecorder = ({ rid, tmid, reference }: VideoMessageRecorderProp stopVideoRecording(rid, tmid); }; - const handleCancel = useEffectEvent(() => { + const handleCancel = useStableCallback(() => { VideoRecorder.stop(); chat?.composer?.setRecordingVideo(false); setTime(undefined); diff --git a/apps/meteor/client/views/hooks/roomActions/useArchiveRoom.tsx b/apps/meteor/client/views/hooks/roomActions/useArchiveRoom.tsx index d60e6717fc41d..cdd48f2e7d44e 100644 --- a/apps/meteor/client/views/hooks/roomActions/useArchiveRoom.tsx +++ b/apps/meteor/client/views/hooks/roomActions/useArchiveRoom.tsx @@ -1,5 +1,5 @@ import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -8,7 +8,7 @@ export const useArchiveRoom = (room: Pick) => { const dispatchToastMessage = useToastMessageDispatch(); const archiveAction = useEndpoint('POST', '/v1/rooms.changeArchivationState'); - const handleArchive = useEffectEvent(async () => { + const handleArchive = useStableCallback(async () => { try { await archiveAction({ rid: room._id, action: room.archived ? 'unarchive' : 'archive' }); dispatchToastMessage({ type: 'success', message: room.archived ? t('Room_has_been_unarchived') : t('Room_has_been_archived') }); diff --git a/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx b/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx index 155097874efdc..80c377a583abd 100644 --- a/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx +++ b/apps/meteor/client/views/hooks/roomActions/useDeleteRoom.tsx @@ -1,6 +1,6 @@ import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useRouter, usePermission, useEndpoint } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; @@ -69,7 +69,7 @@ export const useDeleteRoom = (room: IRoom | Pick, { const isDeleting = deleteTeamMutation.isPending || deleteRoomMutation.isPending; - const handleDelete = useEffectEvent(() => { + const handleDelete = useStableCallback(() => { const handleDeleteTeam = async (roomsToRemove: IRoom['_id'][]) => { if (!room.teamId) { return; diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.spec.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.spec.tsx index 59a544526b6b7..a713d8890d5f9 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.spec.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.spec.tsx @@ -1,6 +1,7 @@ import { mockAppRoot } from '@rocket.chat/mock-providers'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import type { ReactNode } from 'react'; import AppDetailsPage from './AppDetailsPage'; import { AppClientOrchestratorInstance } from '../../../apps/orchestrator'; @@ -25,8 +26,8 @@ jest.mock('@rocket.chat/ui-client', () => { const originalModule = jest.requireActual('@rocket.chat/ui-client'); return { ...originalModule, - PageHeader: ({ children }: { children: React.ReactNode }) =>
{children}
, - PageFooter: ({ children, isDirty }: { children: React.ReactNode; isDirty: boolean }) => isDirty &&
{children}
, + PageHeader: ({ children }: { children: ReactNode }) =>
{children}
, + PageFooter: ({ children, isDirty }: { children: ReactNode; isDirty: boolean }) => isDirty &&
{children}
, }; }); diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx index f19cba712cec0..6d1a2e808d939 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx @@ -1,6 +1,6 @@ import type { App, SettingValue } from '@rocket.chat/core-typings'; import { Button, ButtonGroup, Box } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { Page, PageFooter, PageHeader, PageScrollableContentWithShadow } from '@rocket.chat/ui-client'; import { useTranslation, useRouteParameter, useToastMessageDispatch, usePermission, useRouter } from '@rocket.chat/ui-contexts'; import { useMemo, useCallback } from 'react'; @@ -41,7 +41,7 @@ const AppDetailsPage = ({ id }: AppDetailsPageProps) => { const appData = useAppInfo(id, context || ''); const compactMode = useCompactMode(); - const handleReturn = useEffectEvent((): void => { + const handleReturn = useStableCallback((): void => { if (!context) { return; } @@ -52,7 +52,7 @@ const AppDetailsPage = ({ id }: AppDetailsPageProps) => { }); }); - const handleReturnToLogs = useEffectEvent((): void => { + const handleReturnToLogs = useStableCallback((): void => { if (!context) { return; } diff --git a/apps/meteor/client/views/marketplace/components/RadioDropDown/RadioDownAnchor.tsx b/apps/meteor/client/views/marketplace/components/RadioDropDown/RadioDownAnchor.tsx index a12f40bd16126..940e0dbccf44f 100644 --- a/apps/meteor/client/views/marketplace/components/RadioDropDown/RadioDownAnchor.tsx +++ b/apps/meteor/client/views/marketplace/components/RadioDropDown/RadioDownAnchor.tsx @@ -1,12 +1,12 @@ import type { Button } from '@rocket.chat/fuselage'; import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; +import type { ComponentProps, MouseEvent } from 'react'; import { forwardRef } from 'react'; import type { RadioDropDownGroup } from '../../definitions/RadioDropDownDefinitions'; type RadioDropdownAnchorProps = { - onClick: (event: React.MouseEvent) => void; + onClick: (event: MouseEvent) => void; group: RadioDropDownGroup; } & Omit, 'onClick'>; diff --git a/apps/meteor/client/views/mediaCallHistory/CallHistoryPageFilters.tsx b/apps/meteor/client/views/mediaCallHistory/CallHistoryPageFilters.tsx index 56c4942227169..e1f333c571db0 100644 --- a/apps/meteor/client/views/mediaCallHistory/CallHistoryPageFilters.tsx +++ b/apps/meteor/client/views/mediaCallHistory/CallHistoryPageFilters.tsx @@ -2,7 +2,7 @@ import { Box, Icon, TextInput, Select } from '@rocket.chat/fuselage'; import type { OptionProp } from '@rocket.chat/ui-client'; import { MultiSelectCustom } from '@rocket.chat/ui-client'; import { useCallback, useMemo, useState } from 'react'; -import type { FormEvent, Key } from 'react'; +import type { ChangeEvent, Key, FormEvent } from 'react'; import { useTranslation } from 'react-i18next'; type StatesFilter = Array<'ended' | 'transferred' | 'not-answered' | 'failed'>; @@ -79,7 +79,7 @@ const CallHistoryPageFilters = ({ onChangeText, onChangeType, onChangeStates, se return ( e.preventDefault(), [])} + onSubmit={useCallback((e: FormEvent) => e.preventDefault(), [])} mb='x8' display='flex' flexWrap='wrap' @@ -92,7 +92,7 @@ const CallHistoryPageFilters = ({ onChangeText, onChangeType, onChangeStates, se alignItems='center' placeholder={t('Search_calls')} addon={} - onChange={(e: FormEvent) => onChangeText(e.currentTarget.value)} + onChange={(e: ChangeEvent) => onChangeText(e.currentTarget.value)} value={searchText} /> diff --git a/apps/meteor/client/views/mediaCallHistory/useMediaCallInternalHistoryActions.ts b/apps/meteor/client/views/mediaCallHistory/useMediaCallInternalHistoryActions.ts index f3458197217b3..6ea35964a8a4e 100644 --- a/apps/meteor/client/views/mediaCallHistory/useMediaCallInternalHistoryActions.ts +++ b/apps/meteor/client/views/mediaCallHistory/useMediaCallInternalHistoryActions.ts @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useGoToDirectMessage } from '@rocket.chat/ui-client'; import { useRouter, useUserAvatarPath } from '@rocket.chat/ui-contexts'; import { useWidgetExternalControls, usePeekMediaSessionState, type CallHistoryInternalContact } from '@rocket.chat/ui-voip'; @@ -25,7 +25,7 @@ export const useMediaCallInternalHistoryActions = ({ const getAvatarUrl = useUserAvatarPath(); - const voiceCall = useEffectEvent(() => { + const voiceCall = useStableCallback(() => { if (state !== 'available') { return; } @@ -41,7 +41,7 @@ export const useMediaCallInternalHistoryActions = ({ const goToDirectMessage = useGoToDirectMessage({ username: contact.username }, openRoomId ?? ''); - const jumpToMessage = useEffectEvent(() => { + const jumpToMessage = useStableCallback(() => { const rid = messageRoomId || openRoomId; if (!messageId || !rid) { return; @@ -56,7 +56,7 @@ export const useMediaCallInternalHistoryActions = ({ }); }); - const userInfo = useEffectEvent(() => { + const userInfo = useStableCallback(() => { if (!openUserInfo) { return; } diff --git a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx index a9159b9ee1cbf..7ffc4c1a3868b 100644 --- a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx +++ b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { UiKitContext } from '@rocket.chat/fuselage-ui-kit'; import { MarkupInteractionContext } from '@rocket.chat/gazzodown'; import type * as UiKit from '@rocket.chat/ui-kit'; @@ -21,7 +21,7 @@ const UiKitModal = ({ initialView }: UiKitModalProps) => { const { view, errors, values, updateValues, state } = useUiKitView(initialView); const contextValue = useModalContextValue({ view, errors, values, updateValues }); - const handleSubmit = useEffectEvent((e: FormEvent) => { + const handleSubmit = useStableCallback((e: FormEvent) => { preventSyntheticEvent(e); void actionManager.emitInteraction(view.appId, { type: 'viewSubmit', @@ -35,7 +35,7 @@ const UiKitModal = ({ initialView }: UiKitModalProps) => { }); }); - const handleCancel = useEffectEvent((e: FormEvent) => { + const handleCancel = useStableCallback((e: FormEvent) => { preventSyntheticEvent(e); void actionManager.emitInteraction(view.appId, { type: 'viewClosed', @@ -50,7 +50,7 @@ const UiKitModal = ({ initialView }: UiKitModalProps) => { }); }); - const handleClose = useEffectEvent(() => { + const handleClose = useStableCallback(() => { void actionManager.emitInteraction(view.appId, { type: 'viewClosed', payload: { diff --git a/apps/meteor/client/views/navigation/contexts/RoomsNavigationContext.ts b/apps/meteor/client/views/navigation/contexts/RoomsNavigationContext.ts index 1ece998128a1e..ce6d09bdca41d 100644 --- a/apps/meteor/client/views/navigation/contexts/RoomsNavigationContext.ts +++ b/apps/meteor/client/views/navigation/contexts/RoomsNavigationContext.ts @@ -1,5 +1,5 @@ import { type ISubscription, type ILivechatInquiryRecord, type IRoom, isTeamRoom, isDirectMessageRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; import { isTruthy } from '@rocket.chat/tools'; import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts'; @@ -230,7 +230,7 @@ export const useUnreadOnlyToggle = (): [boolean, () => void] => { const { setFilter, parentRid } = useRoomsListContext(); const [currentTab, unread] = useSidePanelFilter(); - return [unread, useEffectEvent(() => setFilter(currentTab, !unread, parentRid))]; + return [unread, useStableCallback(() => setFilter(currentTab, !unread, parentRid))]; }; export const useSwitchSidePanelTab = () => { diff --git a/apps/meteor/client/views/navigation/hooks/useSidePanelParentRid.ts b/apps/meteor/client/views/navigation/hooks/useSidePanelParentRid.ts index e7c2d2d98ab97..73593c2a08671 100644 --- a/apps/meteor/client/views/navigation/hooks/useSidePanelParentRid.ts +++ b/apps/meteor/client/views/navigation/hooks/useSidePanelParentRid.ts @@ -1,12 +1,12 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks'; import { collapsibleFilters, type AllGroupsKeys } from '../contexts/RoomsNavigationContext'; export const useSidePanelParentRid = () => { const [parentRid, setParentRid] = useLocalStorage('sidePanelParentRid', undefined); - const setParentRoom = useEffectEvent((filter: AllGroupsKeys, parentRid: IRoom['_id'] | undefined) => { + const setParentRoom = useStableCallback((filter: AllGroupsKeys, parentRid: IRoom['_id'] | undefined) => { if (collapsibleFilters.some((group) => filter === group)) { setParentRid(parentRid); } diff --git a/apps/meteor/client/views/navigation/providers/RoomsNavigationProvider.tsx b/apps/meteor/client/views/navigation/providers/RoomsNavigationProvider.tsx index 103845e79d07b..23ae31a1d0b85 100644 --- a/apps/meteor/client/views/navigation/providers/RoomsNavigationProvider.tsx +++ b/apps/meteor/client/views/navigation/providers/RoomsNavigationProvider.tsx @@ -1,6 +1,6 @@ import { isDirectMessageRoom, isDiscussion, isOmnichannelRoom, isPrivateRoom, isPublicRoom, isTeamRoom } from '@rocket.chat/core-typings'; import type { ILivechatInquiryRecord, IRoom } from '@rocket.chat/core-typings'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts'; import { useSetting, useUserPreference, useUserSubscriptions, useLayout } from '@rocket.chat/ui-contexts'; import type { ReactNode } from 'react'; @@ -159,7 +159,7 @@ const RoomsNavigationContextProvider = ({ children }: { children: ReactNode }) = const [currentFilter, unread, , setCurrentFilter] = useSidePanelFilter(); - const setFilter = useEffectEvent((filter: AllGroupsKeys, unread: boolean, parentRid?: IRoom['_id']) => { + const setFilter = useStableCallback((filter: AllGroupsKeys, unread: boolean, parentRid?: IRoom['_id']) => { openSidePanel(); setCurrentFilter(getFilterKey(filter, unread)); setParentRoom(filter, parentRid); @@ -167,7 +167,7 @@ const RoomsNavigationContextProvider = ({ children }: { children: ReactNode }) = const [groups, unreadGroupData] = useRoomsGroups(); - const handleRoomOpened = useEffectEvent((rid: string) => { + const handleRoomOpened = useStableCallback((rid: string) => { const room = Rooms.use.getState().find((r) => r._id === rid); if (!room) { diff --git a/apps/meteor/client/views/omnichannel/additionalForms/AutoCompleteUnit.tsx b/apps/meteor/client/views/omnichannel/additionalForms/AutoCompleteUnit.tsx index a9cb772b92e6f..c8a74e9e7439d 100644 --- a/apps/meteor/client/views/omnichannel/additionalForms/AutoCompleteUnit.tsx +++ b/apps/meteor/client/views/omnichannel/additionalForms/AutoCompleteUnit.tsx @@ -1,5 +1,5 @@ import { PaginatedSelectFiltered } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { ComponentProps } from 'react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -34,7 +34,7 @@ const AutoCompleteUnit = ({ const { data: unitsList, fetchNextPage } = useUnitsList({ filter: debouncedUnitFilter, haveNone }); - const handleLoadItems = useEffectEvent(onLoadItems); + const handleLoadItems = useStableCallback(onLoadItems); useEffect(() => { handleLoadItems(unitsList); diff --git a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx index d7efcbd7b5ac7..81096bc2ac675 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx @@ -1,7 +1,7 @@ import type { ILivechatAgent, ILivechatAgentStatus, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; import { Field, FieldLabel, FieldGroup, FieldRow, TextInput, Button, Box, Icon, Select, ButtonGroup } from '@rocket.chat/fuselage'; import type { SelectOption } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarTitle, ContextualbarClose, @@ -79,7 +79,7 @@ const AgentEdit = ({ agentData, agentDepartments }: AgentEditProps) => { const saveAgentInfo = useEndpoint('POST', '/v1/livechat/agents.saveInfo'); const saveAgentStatus = useEndpoint('POST', '/v1/livechat/agent.status'); - const handleSave = useEffectEvent(async ({ status, departments, ...data }: AgentEditFormData) => { + const handleSave = useStableCallback(async ({ status, departments, ...data }: AgentEditFormData) => { try { await saveAgentStatus({ agentId: agentData._id, status }); await saveAgentInfo({ diff --git a/apps/meteor/client/views/omnichannel/agents/AgentsPage.tsx b/apps/meteor/client/views/omnichannel/agents/AgentsPage.tsx index b6b08ddd52958..cb0461b277c34 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentsPage.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentsPage.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarDialog, Page, PageHeader, PageContent } from '@rocket.chat/ui-client'; import { usePermission, useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -15,7 +15,7 @@ const AgentsPage = () => { const context = useRouteParameter('context'); const id = useRouteParameter('id'); const router = useRouter(); - const handleCloseContextualbar = useEffectEvent(() => router.navigate('/omnichannel/agents')); + const handleCloseContextualbar = useStableCallback(() => router.navigate('/omnichannel/agents')); if (!canViewAgents) { return ; diff --git a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AddAgent.tsx b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AddAgent.tsx index 5b23d29d22f02..8668518cdd312 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AddAgent.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AddAgent.tsx @@ -1,5 +1,5 @@ import { Button, Box, Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { UserAutoComplete } from '@rocket.chat/ui-client'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -25,7 +25,7 @@ const AddAgent = () => { }, }); - const handleSave = useEffectEvent(async () => { + const handleSave = useStableCallback(async () => { await saveAction({ username }); }); diff --git a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx index adfe8269f8347..3cb6a49f3db67 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentsTable/AgentsTable.tsx @@ -1,5 +1,5 @@ import { Pagination } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useMediaQuery, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useMediaQuery, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTable, GenericTableBody, @@ -40,7 +40,7 @@ const AgentsTable = () => { const [defaultQuery] = useState(hashKey([query])); const queryHasChanged = defaultQuery !== hashKey([query]); - const onHeaderClick = useEffectEvent((id: 'name' | 'username' | 'emails.address' | 'statusLivechat') => { + const onHeaderClick = useStableCallback((id: 'name' | 'username' | 'emails.address' | 'statusLivechat') => { if (sortBy === id) { setSort(id, sortDirection === 'asc' ? 'desc' : 'asc'); return; diff --git a/apps/meteor/client/views/omnichannel/agents/hooks/useRemoveAgent.tsx b/apps/meteor/client/views/omnichannel/agents/hooks/useRemoveAgent.tsx index 670d2eb802312..f8cd635144fdd 100644 --- a/apps/meteor/client/views/omnichannel/agents/hooks/useRemoveAgent.tsx +++ b/apps/meteor/client/views/omnichannel/agents/hooks/useRemoveAgent.tsx @@ -1,5 +1,5 @@ import type { ILivechatAgent } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useTranslation, useRouter, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -15,7 +15,7 @@ export const useRemoveAgent = (uid: ILivechatAgent['_id']) => { const deleteAction = useEndpoint('DELETE', '/v1/livechat/users/agent/:_id', { _id: uid }); - const handleDelete = useEffectEvent(() => { + const handleDelete = useStableCallback(() => { const onDeleteAgent = async () => { try { await deleteAction(); diff --git a/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx b/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx index 746225ee2f44a..f895ba994f44c 100644 --- a/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx +++ b/apps/meteor/client/views/omnichannel/analytics/DateRangePicker.tsx @@ -1,8 +1,8 @@ import { Box, InputBox, Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericMenu } from '@rocket.chat/ui-client'; import { subDays, subMonths, startOfMonth, endOfMonth, format } from 'date-fns'; -import type { ComponentProps, FormEvent } from 'react'; +import type { ComponentProps, ChangeEvent } from 'react'; import { useState, useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; @@ -39,7 +39,7 @@ const DateRangePicker = ({ onChange = () => undefined, ...props }: DateRangePick const { start, end } = range; - const handleStart = useEffectEvent(({ currentTarget }: FormEvent) => { + const handleStart = useStableCallback(({ currentTarget }: ChangeEvent) => { const rangeObj = { start: currentTarget.value, end: range.end, @@ -48,7 +48,7 @@ const DateRangePicker = ({ onChange = () => undefined, ...props }: DateRangePick onChange(rangeObj); }); - const handleEnd = useEffectEvent(({ currentTarget }: FormEvent) => { + const handleEnd = useStableCallback(({ currentTarget }: ChangeEvent) => { const rangeObj = { end: currentTarget.value, start: range.start, @@ -57,7 +57,7 @@ const DateRangePicker = ({ onChange = () => undefined, ...props }: DateRangePick onChange(rangeObj); }); - const handleRange = useEffectEvent((range: { start: string; end: string }) => { + const handleRange = useStableCallback((range: { start: string; end: string }) => { setRange(range); onChange(range); }); diff --git a/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx b/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx index 87a8c2c4cd953..dba81fc48f925 100644 --- a/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx +++ b/apps/meteor/client/views/omnichannel/analytics/InterchangeableChart.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import type * as chartjs from 'chart.js'; import { useEffect, useRef } from 'react'; @@ -57,7 +57,7 @@ const InterchangeableChart = ({ const loadData = useEndpoint('GET', '/v1/livechat/analytics/dashboards/charts-data'); - const draw = useEffectEvent( + const draw = useStableCallback( async (params: { daterange: { from: string; diff --git a/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx b/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx index 390ab0391b2a3..b12b9d1ee03be 100644 --- a/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx +++ b/apps/meteor/client/views/omnichannel/appearance/AppearancePage.tsx @@ -1,6 +1,6 @@ import type { ISetting, Serialized } from '@rocket.chat/core-typings'; import { ButtonGroup, Button, Box } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { Page, PageHeader, PageScrollableContentWithShadow, PageFooter } from '@rocket.chat/ui-client'; import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import { useId } from 'react'; @@ -56,7 +56,7 @@ const AppearancePage = ({ settings }: { settings: Serialized[] }) => { const currentData = watch(); - const handleSave = useEffectEvent(async (data: LivechatAppearanceSettings) => { + const handleSave = useStableCallback(async (data: LivechatAppearanceSettings) => { const mappedAppearance = Object.entries(data) .map(([_id, value]) => ({ _id, value })) .filter((item) => item.value !== undefined) as { diff --git a/apps/meteor/client/views/omnichannel/businessHours/EditBusinessHours.tsx b/apps/meteor/client/views/omnichannel/businessHours/EditBusinessHours.tsx index 05288e002224b..47204e0849c08 100644 --- a/apps/meteor/client/views/omnichannel/businessHours/EditBusinessHours.tsx +++ b/apps/meteor/client/views/omnichannel/businessHours/EditBusinessHours.tsx @@ -1,6 +1,6 @@ import type { ILivechatBusinessHour, LivechatBusinessHourTypes, Serialized } from '@rocket.chat/core-typings'; import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { canonicalizeTimezone } from '@rocket.chat/tools'; import { Page, PageFooter, PageHeader, PageScrollableContentWithShadow } from '@rocket.chat/ui-client'; import { useToastMessageDispatch, useTranslation, useRouter, useEndpoint } from '@rocket.chat/ui-contexts'; @@ -52,7 +52,7 @@ const EditBusinessHours = ({ businessHourData, type }: EditBusinessHoursProps) = formState: { isDirty }, } = methods; - const handleSave = useEffectEvent(async ({ departments, ...data }: BusinessHoursFormData) => { + const handleSave = useStableCallback(async ({ departments, ...data }: BusinessHoursFormData) => { const departmentsToApplyBusinessHour = departments?.map((dep) => dep.value).join(',') || ''; try { diff --git a/apps/meteor/client/views/omnichannel/businessHours/useRemoveBusinessHour.tsx b/apps/meteor/client/views/omnichannel/businessHours/useRemoveBusinessHour.tsx index b53598b9006d8..659168834341e 100644 --- a/apps/meteor/client/views/omnichannel/businessHours/useRemoveBusinessHour.tsx +++ b/apps/meteor/client/views/omnichannel/businessHours/useRemoveBusinessHour.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -11,7 +11,7 @@ export const useRemoveBusinessHour = () => { const removeBusinessHour = useEndpoint('POST', '/v1/livechat/business-hours.remove'); const queryClient = useQueryClient(); - const handleRemove = useEffectEvent((_id: string, type: string) => { + const handleRemove = useStableCallback((_id: string, type: string) => { const onDeleteBusinessHour = async () => { try { await removeBusinessHour({ _id, type }); diff --git a/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx b/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx index 516a70ee0143d..041952b2e5584 100644 --- a/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx +++ b/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/CannedResponseList.tsx @@ -11,7 +11,7 @@ import { ContextualbarDialog, } from '@rocket.chat/ui-client'; import { useRoomToolbox } from '@rocket.chat/ui-contexts'; -import type { Dispatch, FormEventHandler, MouseEvent, SetStateAction } from 'react'; +import type { Dispatch, ChangeEventHandler, MouseEvent, SetStateAction } from 'react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; @@ -27,7 +27,7 @@ type CannedResponseListProps = { onClose: () => void; options: [string, string][]; text: string; - setText: FormEventHandler; + setText: ChangeEventHandler; type: string; setType: Dispatch>; isRoomOverMacLimit: boolean; diff --git a/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx b/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx index 9109ddcdd1f5c..8f93a34b66af3 100644 --- a/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx +++ b/apps/meteor/client/views/omnichannel/cannedResponses/contextualBar/CannedResponse/WrapCannedResponseList.tsx @@ -1,5 +1,5 @@ import type { IOmnichannelCannedResponse, ILivechatDepartment } from '@rocket.chat/core-typings'; -import { useDebouncedValue, useLocalStorage, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useLocalStorage, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useRouter, useRoomToolbox } from '@rocket.chat/ui-contexts'; import type { ChangeEvent, MouseEvent } from 'react'; import { memo, useCallback, useState } from 'react'; @@ -34,7 +34,7 @@ export const WrapCannedResponseList = () => { // TODO: handle pending and error states const { data, fetchNextPage, refetch } = useCannedResponseList({ filter: debouncedText, type }); - const onClickItem = useEffectEvent( + const onClickItem = useStableCallback( ( data: IOmnichannelCannedResponse & { departmentName: ILivechatDepartment['name']; diff --git a/apps/meteor/client/views/omnichannel/cannedResponses/modals/CannedResponsesTable.tsx b/apps/meteor/client/views/omnichannel/cannedResponses/modals/CannedResponsesTable.tsx index 40b982c809c9b..5e91bf94fa2db 100644 --- a/apps/meteor/client/views/omnichannel/cannedResponses/modals/CannedResponsesTable.tsx +++ b/apps/meteor/client/views/omnichannel/cannedResponses/modals/CannedResponsesTable.tsx @@ -1,5 +1,5 @@ import { Box, Pagination } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { GenericTable, @@ -63,9 +63,9 @@ const CannedResponsesTable = () => { refetchOnWindowFocus: false, }); - const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/canned-responses/new')); + const handleAddNew = useStableCallback(() => router.navigate('/omnichannel/canned-responses/new')); - const onRowClick = useEffectEvent((id: string, scope: string) => (): void => { + const onRowClick = useStableCallback((id: string, scope: string) => (): void => { if (scope === 'global' && isMonitor && !isManager) { return dispatchToastMessage({ type: 'error', diff --git a/apps/meteor/client/views/omnichannel/cannedResponses/modals/useRemoveCannedResponse.tsx b/apps/meteor/client/views/omnichannel/cannedResponses/modals/useRemoveCannedResponse.tsx index be706985158e3..fcf69cffcaf95 100644 --- a/apps/meteor/client/views/omnichannel/cannedResponses/modals/useRemoveCannedResponse.tsx +++ b/apps/meteor/client/views/omnichannel/cannedResponses/modals/useRemoveCannedResponse.tsx @@ -1,5 +1,5 @@ import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useRouter, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -14,7 +14,7 @@ export const useRemoveCannedResponse = (id: IOmnichannelCannedResponse['_id']) = const dispatchToastMessage = useToastMessageDispatch(); const removeCannedResponse = useEndpoint('DELETE', '/v1/canned-responses/:_id', { _id: id }); - const handleDelete = useEffectEvent(() => { + const handleDelete = useStableCallback(() => { const onDeleteCannedResponse: () => Promise = async () => { try { await removeCannedResponse(); diff --git a/apps/meteor/client/views/omnichannel/components/Tags.tsx b/apps/meteor/client/views/omnichannel/components/Tags.tsx index 61d2c049740ed..a5ec667458420 100644 --- a/apps/meteor/client/views/omnichannel/components/Tags.tsx +++ b/apps/meteor/client/views/omnichannel/components/Tags.tsx @@ -1,5 +1,5 @@ import { TextInput, Chip, Button, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ChangeEvent } from 'react'; import { useId, useMemo, useState } from 'react'; @@ -42,7 +42,7 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) handler(tags.filter((tag) => tag !== tagToRemove)); }; - const handleTagTextSubmit = useEffectEvent(() => { + const handleTagTextSubmit = useStableCallback(() => { if (!tags) { return; } diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/OutboundMessageWizard.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/OutboundMessageWizard.tsx index cbb0a76a5577b..fe54512dd695d 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/OutboundMessageWizard.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/OutboundMessageWizard.tsx @@ -1,5 +1,5 @@ import { Box } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar'; import { Wizard, useWizard, WizardContent, WizardTabs } from '@rocket.chat/ui-client'; import { usePermission } from '@rocket.chat/ui-contexts'; @@ -86,7 +86,7 @@ const OutboundMessageWizard = ({ defaultValues = {}, onSuccess, onError }: Outbo } }, [hasOmnichannelModule.data, hasOutboundModule.data, hasProviders, isLoadingModule, isLoadingProviders, upsellModal]); - const handleSubmit = useEffectEvent((values: SubmitPayload) => { + const handleSubmit = useStableCallback((values: SubmitPayload) => { if (!hasOutboundModule.data) { upsellModal.open(); return; @@ -95,7 +95,7 @@ const OutboundMessageWizard = ({ defaultValues = {}, onSuccess, onError }: Outbo setState((state) => ({ ...state, ...values })); }); - const handleSend = useEffectEvent(async () => { + const handleSend = useStableCallback(async () => { try { if (!isRecipientStepValid(state)) { throw new Error('error-invalid-recipient-step'); @@ -144,7 +144,7 @@ const OutboundMessageWizard = ({ defaultValues = {}, onSuccess, onError }: Outbo } }); - const handleDirtyStep = useEffectEvent(() => { + const handleDirtyStep = useStableCallback(() => { wizardApi.resetNextSteps(); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/MessageForm.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/MessageForm.tsx index e57e44fe01b4b..d171ca0ae6455 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/MessageForm.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/MessageForm.tsx @@ -1,6 +1,6 @@ import type { IOutboundProviderTemplate, Serialized, ILivechatContact } from '@rocket.chat/core-typings'; import { Box, Button, FieldGroup, Scrollable } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar'; import type { ReactNode } from 'react'; import { useId, useMemo } from 'react'; @@ -61,7 +61,7 @@ const MessageForm = (props: MessageFormProps) => { const parametersMetadata = useMemo(() => (template ? extractParameterMetadata(template) : []), [template]); const customActions = useMemo(() => renderActions?.({ isSubmitting }), [isSubmitting, renderActions]); - const submit = useEffectEvent(async (values: MessageFormData) => { + const submit = useStableCallback(async (values: MessageFormData) => { try { const { templateId, templateParameters } = values; diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/components/TemplateField.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/components/TemplateField.tsx index 1f116ac24c326..259b129008a0e 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/components/TemplateField.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/MessageForm/components/TemplateField.tsx @@ -1,6 +1,6 @@ import type { IOutboundProviderTemplate, Serialized } from '@rocket.chat/core-typings'; import { Field, FieldError, FieldHint, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { ComponentProps } from 'react'; import { useId } from 'react'; import type { Control } from 'react-hook-form'; @@ -41,7 +41,7 @@ const TemplateField = ({ control, templates, onChange: onChangeExternal, ...prop }, }); - const handleTemplateChange = useEffectEvent((value: string) => { + const handleTemplateChange = useStableCallback((value: string) => { onChangeExternal?.(value); templateField.onChange(value); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/RecipientForm.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/RecipientForm.tsx index 0ecfed32d42d1..8ac31baff588b 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/RecipientForm.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/RecipientForm.tsx @@ -1,6 +1,6 @@ import type { IOutboundProviderMetadata, Serialized, ILivechatContact } from '@rocket.chat/core-typings'; import { Box, Button, FieldGroup, Scrollable } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; @@ -111,12 +111,12 @@ const RecipientForm = (props: RecipientFormProps) => { const isContactNotFound = isSuccessContact && !contact; const isProviderNotFound = isSuccessProvider && !provider; - const validateContactField = useEffectEvent((shouldValidate = false) => { + const validateContactField = useStableCallback((shouldValidate = false) => { trigger('contactId'); setValue('recipient', '', { shouldValidate }); }); - const validateProviderField = useEffectEvent((shouldValidate = false) => { + const validateProviderField = useStableCallback((shouldValidate = false) => { trigger('providerId'); setValue('sender', '', { shouldValidate }); }); @@ -151,7 +151,7 @@ const RecipientForm = (props: RecipientFormProps) => { isDirty && onDirty && onDirty(); }, [isDirty, onDirty]); - const submit = useEffectEvent(async (values: RecipientFormData) => { + const submit = useStableCallback(async (values: RecipientFormData) => { try { // Wait if contact or provider is still being fetched in background const [updatedContact, updatedProvider] = await Promise.all([ diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/components/ContactField.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/components/ContactField.tsx index 7b18cd452dfc0..fab99b29fa37b 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/components/ContactField.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RecipientForm/components/ContactField.tsx @@ -1,5 +1,5 @@ import { Box, Field, FieldError, FieldLabel, FieldRow, Option, OptionContent, OptionDescription } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useId } from 'react'; import type { ComponentProps } from 'react'; @@ -38,7 +38,7 @@ const ContactField = ({ control, isError = false, isFetching = false, onRetry, . }, }); - const renderContactOption = useEffectEvent(({ label, ...props }, { phones }) => { + const renderContactOption = useStableCallback(({ label, ...props }, { phones }) => { const phoneList = phones?.map((p) => formatPhoneNumber(p.phoneNumber)).join(', '); return ( diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/RepliesForm.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/RepliesForm.tsx index a0c5e4a17b89b..275f4bf42505d 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/RepliesForm.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/RepliesForm.tsx @@ -1,6 +1,6 @@ import type { Serialized, ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; import { Box, Button, FieldGroup, Scrollable } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar'; import { useEndpoint, usePermission, useUser } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; @@ -88,7 +88,7 @@ const RepliesForm = (props: RepliesFormProps) => { return () => clearErrors('departmentId'); }, [clearErrors, isErrorDepartment, trigger]); - const submit = useEffectEvent(async ({ agentId, departmentId }: RepliesFormData) => { + const submit = useStableCallback(async ({ agentId, departmentId }: RepliesFormData) => { try { const agent = agents?.find((agent) => agent.agentId === agentId); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/components/DepartmentField.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/components/DepartmentField.tsx index 71842a449892f..324c53078f226 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/components/DepartmentField.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/forms/RepliesForm/components/DepartmentField.tsx @@ -1,5 +1,5 @@ import { Field, FieldError, FieldHint, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import type { ComponentProps } from 'react'; import { useId } from 'react'; import type { Control } from 'react-hook-form'; @@ -43,7 +43,7 @@ const DepartmentField = ({ }, }); - const handleDepartmentChange = useEffectEvent((onChange: (value: string) => void) => { + const handleDepartmentChange = useStableCallback((onChange: (value: string) => void) => { return (value: string) => { onChangeExternal(); onChange(value); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/MessageStep.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/MessageStep.tsx index 6830f6e7b4b83..1cb7382787249 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/MessageStep.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/MessageStep.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useWizardContext, WizardActions, WizardBackButton, WizardNextButton } from '@rocket.chat/ui-client'; import type { ComponentProps } from 'react'; @@ -12,7 +12,7 @@ type MessageStepProps = Omit, 'onSubmit'> & { const MessageStep = ({ contact, templates, defaultValues, onSubmit }: MessageStepProps) => { const { next } = useWizardContext(); - const handleSubmit = useEffectEvent(async (values: MessageFormSubmitPayload) => { + const handleSubmit = useStableCallback(async (values: MessageFormSubmitPayload) => { onSubmit(values); next(); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RecipientStep.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RecipientStep.tsx index 88e9c64e7c573..09fc9e73b1cbf 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RecipientStep.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RecipientStep.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useWizardContext, WizardActions, WizardNextButton } from '@rocket.chat/ui-client'; import type { RecipientFormData, RecipientFormSubmitPayload } from '../forms/RecipientForm'; @@ -13,7 +13,7 @@ type RecipientStepProps = { const RecipientStep = ({ defaultValues, onDirty, onSubmit }: RecipientStepProps) => { const { next } = useWizardContext(); - const handleSubmit = useEffectEvent((values: RecipientFormSubmitPayload) => { + const handleSubmit = useStableCallback((values: RecipientFormSubmitPayload) => { onSubmit(values); next(); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RepliesStep.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RepliesStep.tsx index d3abec35e7aab..aa5b2fe4266a3 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RepliesStep.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/OutboundMessageWizard/steps/RepliesStep.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useWizardContext, WizardActions, WizardBackButton, WizardNextButton } from '@rocket.chat/ui-client'; import type { RepliesFormData, RepliesFormSubmitPayload } from '../forms/RepliesForm'; @@ -12,7 +12,7 @@ type RepliesStepProps = { const RepliesStep = ({ defaultValues, onSubmit }: RepliesStepProps) => { const { next } = useWizardContext(); - const handleSubmit = useEffectEvent(async (values: RepliesFormSubmitPayload) => { + const handleSubmit = useStableCallback(async (values: RepliesFormSubmitPayload) => { onSubmit(values); next(); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/TemplatePlaceholderSelector/TemplatePlaceholderInput.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/TemplatePlaceholderSelector/TemplatePlaceholderInput.tsx index 24aa391420f42..9c3144ca08794 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/components/TemplatePlaceholderSelector/TemplatePlaceholderInput.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/components/TemplatePlaceholderSelector/TemplatePlaceholderInput.tsx @@ -1,6 +1,6 @@ import type { ILivechatContact, Serialized } from '@rocket.chat/core-typings'; import { Box, Icon, TextInput } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRef, type ComponentProps, type FormEvent, type FormEventHandler } from 'react'; import PlaceholderSelector from './TemplatePlaceholderSelector'; @@ -22,7 +22,7 @@ const TemplatePlaceholderInput = ({ contact, value = '', type, onChange, ...prop const addon = type === 'media' ? : undefined; - const handleOpenToggle = useEffectEvent((isOpen: boolean) => { + const handleOpenToggle = useStableCallback((isOpen: boolean) => { if (!isOpen) inputRef.current?.focus(); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/OutboundMessageModal.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/OutboundMessageModal.tsx index 8c81a0dbb4a9e..010781587d0d5 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/OutboundMessageModal.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/OutboundMessageModal.tsx @@ -1,5 +1,5 @@ import { Modal, ModalBackdrop, ModalClose, ModalContent, ModalHeader, ModalTitle } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useRouter } from '@rocket.chat/ui-contexts'; import { useEffect, useId, useState } from 'react'; import type { KeyboardEvent, ComponentProps } from 'react'; @@ -32,7 +32,7 @@ const OutboundMessageModal = ({ defaultValues, onClose }: OutboundMessageModalPr }); }, [initialRoute, onClose, router]); - const handleKeyDown = useEffectEvent((e: KeyboardEvent): void => { + const handleKeyDown = useStableCallback((e: KeyboardEvent): void => { if (e.key !== 'Escape') { return; } diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/useOutboundMessageModal.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/useOutboundMessageModal.tsx index b9f8a28f819e8..d508bb7ae4b4a 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/useOutboundMessageModal.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageModal/useOutboundMessageModal.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal } from '@rocket.chat/ui-contexts'; import type { ComponentProps } from 'react'; import { useMemo } from 'react'; @@ -8,9 +8,9 @@ import OutboundMessageModal from './OutboundMessageModal'; export const useOutboundMessageModal = () => { const setModal = useSetModal(); - const close = useEffectEvent((): void => setModal(null)); + const close = useStableCallback((): void => setModal(null)); - const open = useEffectEvent((defaultValues?: ComponentProps['defaultValues']) => { + const open = useStableCallback((defaultValues?: ComponentProps['defaultValues']) => { setModal(); }); diff --git a/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageUpsellModal/useOutboundMessageUpsellModal.tsx b/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageUpsellModal/useOutboundMessageUpsellModal.tsx index 6234176c44f53..2099789a7ebd4 100644 --- a/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageUpsellModal/useOutboundMessageUpsellModal.tsx +++ b/apps/meteor/client/views/omnichannel/components/outboundMessage/modals/OutboundMessageUpsellModal/useOutboundMessageUpsellModal.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useLicense } from '@rocket.chat/ui-client'; import { useRole, useSetModal } from '@rocket.chat/ui-contexts'; import { useMemo } from 'react'; @@ -12,8 +12,8 @@ export const useOutboundMessageUpsellModal = () => { const license = useLicense(); const { data: hasModule = false } = useHasLicenseModule('outbound-messaging'); - const close = useEffectEvent(() => setModal(null)); - const open = useEffectEvent(() => + const close = useStableCallback(() => setModal(null)); + const open = useStableCallback(() => setModal(), ); diff --git a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsPage.tsx b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsPage.tsx index 1fcf092ede7c3..582502566f419 100644 --- a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsPage.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsPage.tsx @@ -1,5 +1,5 @@ import { Button } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { ContextualbarDialog, Page, PageHeader, PageContent } from '@rocket.chat/ui-client'; import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import { useTranslation } from 'react-i18next'; @@ -15,7 +15,7 @@ const CustomFieldsPage = () => { const context = useRouteParameter('context'); const id = useRouteParameter('id'); - const handleCloseContextualbar = useEffectEvent(() => router.navigate('/omnichannel/customfields')); + const handleCloseContextualbar = useStableCallback(() => router.navigate('/omnichannel/customfields')); return ( diff --git a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx index d249d816e4e70..e041566a0ad07 100644 --- a/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/CustomFieldsTable.tsx @@ -1,5 +1,5 @@ import { IconButton, Pagination } from '@rocket.chat/fuselage'; -import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useDebouncedValue, useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericTable, GenericTableHeader, @@ -29,8 +29,8 @@ const CustomFieldsTable = () => { const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination(); const { sortBy, sortDirection, setSort } = useSort<'_id' | 'label' | 'scope' | 'visibility'>('_id'); - const handleAddNew = useEffectEvent(() => router.navigate('/omnichannel/customfields/new')); - const onRowClick = useEffectEvent((id: string) => () => router.navigate(`/omnichannel/customfields/edit/${id}`)); + const handleAddNew = useStableCallback(() => router.navigate('/omnichannel/customfields/new')); + const onRowClick = useStableCallback((id: string) => () => router.navigate(`/omnichannel/customfields/edit/${id}`)); const handleDelete = useRemoveCustomField(); diff --git a/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx b/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx index d03059d91e8ae..358b0a76ea1c0 100644 --- a/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/useRemoveCustomField.tsx @@ -1,4 +1,4 @@ -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; @@ -13,7 +13,7 @@ export const useRemoveCustomField = () => { const removeCustomField = useEndpoint('POST', '/v1/livechat/custom-fields.delete'); const queryClient = useQueryClient(); - const handleDelete = useEffectEvent((id: string) => { + const handleDelete = useStableCallback((id: string) => { const onDeleteAgent = async () => { try { await removeCustomField({ customFieldId: id }); diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx index 54c9ca78758d3..fa42c958f9c15 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/AddAgent.tsx @@ -1,5 +1,5 @@ import { Box, Button } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { AriaAttributes } from 'react'; import { useState } from 'react'; @@ -23,9 +23,9 @@ function AddAgent({ agentList, onAdd, 'aria-labelledby': ariaLabelledBy }: AddAg const dispatchToastMessage = useToastMessageDispatch(); - const handleAgent = useEffectEvent((e: string) => setUserId(e)); + const handleAgent = useStableCallback((e: string) => setUserId(e)); - const handleSave = useEffectEvent(async () => { + const handleSave = useStableCallback(async () => { if (!userId) { return; } diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx index b7af68c27e25c..96de0b04b7072 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx @@ -1,5 +1,5 @@ import { IconButton } from '@rocket.chat/fuselage'; -import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericModal } from '@rocket.chat/ui-client'; import { useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { MouseEvent } from 'react'; @@ -10,7 +10,7 @@ function RemoveAgentButton({ agentId, onRemove }: { agentId: string; onRemove: ( const dispatchToastMessage = useToastMessageDispatch(); const { t } = useTranslation(); - const handleDelete = useEffectEvent((e: MouseEvent) => { + const handleDelete = useStableCallback((e: MouseEvent) => { e.stopPropagation(); const onRemoveAgent = async () => { diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx index 36815bde66c91..f8854c6575791 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentTags.tsx @@ -1,5 +1,5 @@ import { Button, Chip, FieldRow, TextInput } from '@rocket.chat/fuselage'; -import type { ComponentProps, FormEvent } from 'react'; +import type { ComponentProps, ChangeEvent } from 'react'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -33,7 +33,7 @@ const DepartmentTags = ({ error, value: tags, onChange, ...props }: DepartmentTa error={error} placeholder={t('Enter_a_tag')} value={tagText} - onChange={(e: FormEvent) => setTagText(e.currentTarget.value)} + onChange={(e: ChangeEvent) => setTagText(e.currentTarget.value)} {...props} />