diff --git a/.changeset/short-pumpkins-fail.md b/.changeset/short-pumpkins-fail.md new file mode 100644 index 0000000000000..b017b5632d7f1 --- /dev/null +++ b/.changeset/short-pumpkins-fail.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes issue when trying to save preferences in the user preferences page diff --git a/apps/meteor/client/hooks/account/useSavePreferences.spec.ts b/apps/meteor/client/hooks/account/useSavePreferences.spec.ts new file mode 100644 index 0000000000000..c90495ab7b2a2 --- /dev/null +++ b/apps/meteor/client/hooks/account/useSavePreferences.spec.ts @@ -0,0 +1,21 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { renderHook, waitFor } from '@testing-library/react'; + +import { useSavePreferences } from './useSavePreferences'; +import type { AccountPreferencesData } from '../../views/account/preferences/useAccountPreferencesValues'; + +const mockSetPreferencesEndpoint = jest.fn(); + +describe('useSavePreferences', () => { + it('should call setPreferencesEndpoint with correct data', async () => { + const dirtyFields = { language: true }; + const { result } = renderHook(() => useSavePreferences({ dirtyFields, reset: jest.fn(), currentData: {} }), { + wrapper: mockAppRoot().withEndpoint('POST', '/v1/users.setPreferences', mockSetPreferencesEndpoint).build(), + }); + + const formData: AccountPreferencesData = { language: 'en' }; + await waitFor(() => result.current(formData)); + + expect(mockSetPreferencesEndpoint).toHaveBeenCalledTimes(1); + }); +}); diff --git a/apps/meteor/client/hooks/account/useSavePreferences.ts b/apps/meteor/client/hooks/account/useSavePreferences.ts new file mode 100644 index 0000000000000..39c9b3cebd1f6 --- /dev/null +++ b/apps/meteor/client/hooks/account/useSavePreferences.ts @@ -0,0 +1,57 @@ +import { useEndpoint } from '@rocket.chat/ui-contexts'; +import { useMutation } from '@tanstack/react-query'; +import type { UseFormReset } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +import { getDirtyFields } from '../../lib/getDirtyFields'; +import { dispatchToastMessage } from '../../lib/toast'; +import type { AccountPreferencesData } from '../../views/account/preferences/useAccountPreferencesValues'; + +type useSavePreferencesProps = { + dirtyFields: Partial>; + reset: UseFormReset; + currentData: AccountPreferencesData; +}; + +export const useSavePreferences = ({ dirtyFields, currentData, reset }: useSavePreferencesProps) => { + const setPreferencesEndpoint = useEndpoint('POST', '/v1/users.setPreferences'); + const { t } = useTranslation(); + + const setPreferencesAction = useMutation({ + mutationFn: ({ + data, + }: { + data: AccountPreferencesData & { dontAskAgainList?: { action: string; label: string }[] } & { highlights?: string[] }; + }) => setPreferencesEndpoint({ data }), + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Preferences_saved') }); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + onSettled: () => reset(currentData, { keepValues: true }), + }); + + return async (formData: AccountPreferencesData) => { + const { highlights, dontAskAgainList, ...data } = getDirtyFields(formData, dirtyFields); + if (highlights || highlights === '') { + Object.assign(data, { + highlights: + typeof highlights === 'string' && + highlights + .split(/,|\n/) + .map((val) => val.trim()) + .filter(Boolean), + }); + } + + if (dontAskAgainList) { + const list = + Array.isArray(dontAskAgainList) && dontAskAgainList.length > 0 + ? dontAskAgainList.map(([action, label]) => ({ action, label })) + : []; + Object.assign(data, { dontAskAgainList: list }); + } + setPreferencesAction.mutateAsync({ data }); + }; +}; diff --git a/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx b/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx index fcec450b607e1..851fb9a7df539 100644 --- a/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx +++ b/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx @@ -1,6 +1,5 @@ import { ButtonGroup, Button, Box, Accordion } from '@rocket.chat/fuselage'; -import { useToastMessageDispatch, useSetting, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; -import { useMutation } from '@tanstack/react-query'; +import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import { useId } from 'react'; import type { ReactElement } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; @@ -13,14 +12,12 @@ import PreferencesMyDataSection from './PreferencesMyDataSection'; import PreferencesNotificationsSection from './PreferencesNotificationsSection'; import PreferencesSoundSection from './PreferencesSoundSection'; import PreferencesUserPresenceSection from './PreferencesUserPresenceSection'; -import type { AccountPreferencesData } from './useAccountPreferencesValues'; import { useAccountPreferencesValues } from './useAccountPreferencesValues'; import { Page, PageHeader, PageScrollableContentWithShadow, PageFooter } from '../../../components/Page'; -import { getDirtyFields } from '../../../lib/getDirtyFields'; +import { useSavePreferences } from '../../../hooks/account/useSavePreferences'; const AccountPreferencesPage = (): ReactElement => { const t = useTranslation(); - const dispatchToastMessage = useToastMessageDispatch(); const dataDownloadEnabled = useSetting('UserData_EnableDownload'); const preferencesValues = useAccountPreferencesValues(); @@ -34,41 +31,7 @@ const AccountPreferencesPage = (): ReactElement => { const currentData = watch(); - const setPreferencesEndpoint = useEndpoint('POST', '/v1/users.setPreferences'); - const setPreferencesAction = useMutation({ - mutationFn: setPreferencesEndpoint, - onSuccess: () => { - dispatchToastMessage({ type: 'success', message: t('Preferences_saved') }); - }, - onError: (error) => { - dispatchToastMessage({ type: 'error', message: error }); - }, - onSettled: () => reset(currentData), - }); - - const handleSaveData = async (formData: AccountPreferencesData) => { - const { highlights, dontAskAgainList, ...data } = getDirtyFields(formData, dirtyFields); - if (highlights || highlights === '') { - Object.assign(data, { - highlights: - typeof highlights === 'string' && - highlights - .split(/,|\n/) - .map((val) => val.trim()) - .filter(Boolean), - }); - } - - if (dontAskAgainList) { - const list = - Array.isArray(dontAskAgainList) && dontAskAgainList.length > 0 - ? dontAskAgainList.map(([action, label]) => ({ action, label })) - : []; - Object.assign(data, { dontAskAgainList: list }); - } - - setPreferencesAction.mutateAsync({ data }); - }; + const handleSaveData = useSavePreferences({ dirtyFields, currentData, reset }); const preferencesFormId = useId();