snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { Contextualbar } from '@rocket.chat/ui-client';
|
||||
import type { StoryObj, Meta } from '@storybook/react';
|
||||
import { action } from 'storybook/actions';
|
||||
|
||||
import NotificationsPreferences from './NotificationPreferences';
|
||||
|
||||
export default {
|
||||
component: NotificationsPreferences,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
},
|
||||
decorators: [(fn) => <Contextualbar height='100vh'>{fn()}</Contextualbar>],
|
||||
} satisfies Meta<typeof NotificationsPreferences>;
|
||||
|
||||
export const Default: StoryObj<typeof NotificationsPreferences> = {
|
||||
name: 'NotificationsPreferences',
|
||||
|
||||
args: {
|
||||
handleClose: action('handleClose'),
|
||||
handleSave: action('handleSaveButton'),
|
||||
handlePlaySound: action('handlePlaySound'),
|
||||
notificationOptions: {
|
||||
alerts: [
|
||||
['default', 'Default'],
|
||||
['all', 'All_messages'],
|
||||
['mentions', 'Mentions'],
|
||||
['nothing', 'Nothing'],
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { SelectOption } from '@rocket.chat/fuselage';
|
||||
import { Button, ButtonGroup } from '@rocket.chat/fuselage';
|
||||
import {
|
||||
ContextualbarHeader,
|
||||
ContextualbarIcon,
|
||||
ContextualbarTitle,
|
||||
ContextualbarClose,
|
||||
ContextualbarScrollableContent,
|
||||
ContextualbarFooter,
|
||||
ContextualbarDialog,
|
||||
} from '@rocket.chat/ui-client';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import NotificationPreferencesForm from './NotificationPreferencesForm';
|
||||
|
||||
export type NotificationPreferencesProps = {
|
||||
handleClose: () => void;
|
||||
handleSave: () => void;
|
||||
notificationOptions: {
|
||||
[key: string]: SelectOption[];
|
||||
};
|
||||
handlePlaySound: () => void;
|
||||
};
|
||||
|
||||
const NotificationPreferences = ({ handleClose, handleSave, notificationOptions, handlePlaySound }: NotificationPreferencesProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
formState: { isDirty, isSubmitting },
|
||||
reset,
|
||||
} = useFormContext();
|
||||
|
||||
return (
|
||||
<ContextualbarDialog>
|
||||
<ContextualbarHeader>
|
||||
<ContextualbarIcon name='bell' />
|
||||
<ContextualbarTitle>{t('Notifications_Preferences')}</ContextualbarTitle>
|
||||
{handleClose && <ContextualbarClose onClick={handleClose} />}
|
||||
</ContextualbarHeader>
|
||||
<ContextualbarScrollableContent>
|
||||
<NotificationPreferencesForm notificationOptions={notificationOptions} handlePlaySound={handlePlaySound} />
|
||||
</ContextualbarScrollableContent>
|
||||
<ContextualbarFooter>
|
||||
<ButtonGroup stretch>
|
||||
<Button type='reset' disabled={!isDirty || isSubmitting} onClick={() => reset()}>
|
||||
{t('Reset')}
|
||||
</Button>
|
||||
<Button primary disabled={!isDirty} loading={isSubmitting} onClick={handleSave}>
|
||||
{t('Save')}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</ContextualbarFooter>
|
||||
</ContextualbarDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationPreferences;
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { SelectOption } from '@rocket.chat/fuselage';
|
||||
import { FieldGroup, IconButton, Margins } from '@rocket.chat/fuselage';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import NotificationByDevice from './components/NotificationByDevice';
|
||||
import NotificationPreference from './components/NotificationPreference';
|
||||
import NotificationToggle from './components/NotificationToggle';
|
||||
|
||||
export type NotificationPreferencesFormProps = {
|
||||
notificationOptions: {
|
||||
[key: string]: SelectOption[];
|
||||
};
|
||||
handlePlaySound: () => void;
|
||||
};
|
||||
|
||||
const NotificationPreferencesForm = ({ notificationOptions, handlePlaySound }: NotificationPreferencesFormProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { watch, control } = useFormContext();
|
||||
|
||||
const { showCounter } = watch();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Controller
|
||||
control={control}
|
||||
name='turnOn'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationToggle label={t('Turn_ON')} description={t('Receive_alerts')} onChange={onChange} checked={value} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name='muteGroupMentions'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationToggle label={t('Mute_Group_Mentions')} onChange={onChange} checked={value} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name='showCounter'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationToggle label={t('Show_counter')} description={t('Display_unread_counter')} onChange={onChange} checked={value} />
|
||||
)}
|
||||
/>
|
||||
{!showCounter && (
|
||||
<Controller
|
||||
control={control}
|
||||
name='showMentions'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationToggle
|
||||
label={t('Show_mentions')}
|
||||
description={t('Display_mentions_counter')}
|
||||
onChange={onChange}
|
||||
checked={value}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FieldGroup>
|
||||
<NotificationByDevice device={t('Desktop')} icon='desktop'>
|
||||
<Controller
|
||||
control={control}
|
||||
name='desktopAlert'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationPreference
|
||||
id='DesktopAlert'
|
||||
name={t('Alerts')}
|
||||
options={notificationOptions.alerts}
|
||||
optionValue={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Margins blockStart={16}>
|
||||
<Controller
|
||||
control={control}
|
||||
name='desktopSound'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationPreference
|
||||
id='DesktopSound'
|
||||
name={t('Sound')}
|
||||
options={notificationOptions.sounds}
|
||||
optionValue={value}
|
||||
onChange={onChange}
|
||||
>
|
||||
<IconButton icon='play' marginInlineStart={4} onClick={handlePlaySound} aria-label={t('Play')} />
|
||||
</NotificationPreference>
|
||||
)}
|
||||
/>
|
||||
</Margins>
|
||||
</NotificationByDevice>
|
||||
<NotificationByDevice device={t('Mobile')} icon='mobile'>
|
||||
<Controller
|
||||
control={control}
|
||||
name='mobileAlert'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationPreference
|
||||
id='MobileAlert'
|
||||
name={t('Alerts')}
|
||||
options={notificationOptions.alerts}
|
||||
optionValue={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</NotificationByDevice>
|
||||
<NotificationByDevice device={t('Email')} icon='mail'>
|
||||
<Controller
|
||||
control={control}
|
||||
name='emailAlert'
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<NotificationPreference
|
||||
id='EmailAlert'
|
||||
name={t('Alerts')}
|
||||
options={notificationOptions.alerts}
|
||||
optionValue={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</NotificationByDevice>
|
||||
</FieldGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationPreferencesForm;
|
||||
@@ -0,0 +1,83 @@
|
||||
import { mockAppRoot } from '@rocket.chat/mock-providers';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import NotificationPreferencesWithData from './NotificationPreferencesWithData';
|
||||
|
||||
const mockPlay = jest.fn();
|
||||
const mockCloseTab = jest.fn();
|
||||
const mockUseRoomSubscription = jest.fn();
|
||||
|
||||
jest.mock('@rocket.chat/ui-contexts', () => ({
|
||||
...jest.requireActual('@rocket.chat/ui-contexts'),
|
||||
useCustomSound: () => ({ play: mockPlay, list: [] }),
|
||||
useRoomToolbox: () => ({ closeTab: mockCloseTab }),
|
||||
useToastMessageDispatch: () => jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../contexts/RoomContext', () => ({
|
||||
useRoom: () => ({ _id: 'GENERAL' }),
|
||||
useRoomSubscription: () => mockUseRoomSubscription(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../hooks/useEndpointMutation', () => ({
|
||||
useEndpointMutation: () => ({ mutateAsync: jest.fn() }),
|
||||
}));
|
||||
|
||||
const appRoot = (userPreferences?: Record<string, unknown>) =>
|
||||
mockAppRoot()
|
||||
.withUserPreference('newMessageNotification', userPreferences?.newMessageNotification ?? 'chime')
|
||||
.build();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPlay.mockClear();
|
||||
mockUseRoomSubscription.mockReturnValue({
|
||||
disableNotifications: false,
|
||||
muteGroupMentions: false,
|
||||
hideUnreadStatus: false,
|
||||
hideMentionStatus: false,
|
||||
audioNotificationValue: undefined, // desktopSound defaults to 'default'
|
||||
});
|
||||
});
|
||||
|
||||
describe('NotificationPreferencesWithData - handlePlaySound', () => {
|
||||
it('plays the user newMessageNotification preference when desktopSound is "default"', async () => {
|
||||
render(<NotificationPreferencesWithData />, {
|
||||
wrapper: appRoot({ newMessageNotification: 'chime' }),
|
||||
});
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Play' }));
|
||||
|
||||
expect(mockPlay).toHaveBeenCalledWith('chime');
|
||||
expect(mockPlay).not.toHaveBeenCalledWith('default');
|
||||
});
|
||||
|
||||
it('plays the user preference sound even when it is not the default chime', async () => {
|
||||
render(<NotificationPreferencesWithData />, {
|
||||
wrapper: appRoot({ newMessageNotification: 'ringtone' }),
|
||||
});
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Play' }));
|
||||
|
||||
expect(mockPlay).toHaveBeenCalledWith('ringtone');
|
||||
});
|
||||
|
||||
it('plays the specific sound directly when desktopSound is not "default"', async () => {
|
||||
mockUseRoomSubscription.mockReturnValue({
|
||||
disableNotifications: false,
|
||||
muteGroupMentions: false,
|
||||
hideUnreadStatus: false,
|
||||
hideMentionStatus: false,
|
||||
audioNotificationValue: 'door',
|
||||
});
|
||||
|
||||
render(<NotificationPreferencesWithData />, {
|
||||
wrapper: appRoot({ newMessageNotification: 'chime' }),
|
||||
});
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Play' }));
|
||||
|
||||
expect(mockPlay).toHaveBeenCalledWith('door');
|
||||
expect(mockPlay).not.toHaveBeenCalledWith('chime');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
import type { SelectOption } from '@rocket.chat/fuselage';
|
||||
import { useCustomSound, useToastMessageDispatch, useRoomToolbox, useUserPreference } from '@rocket.chat/ui-contexts';
|
||||
import { memo } from 'react';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import NotificationPreferences from './NotificationPreferences';
|
||||
import { useEndpointMutation } from '../../../../hooks/useEndpointMutation';
|
||||
import { useRoom, useRoomSubscription } from '../../contexts/RoomContext';
|
||||
|
||||
const NotificationPreferencesWithData = () => {
|
||||
const { t } = useTranslation();
|
||||
const room = useRoom();
|
||||
const subscription = useRoomSubscription();
|
||||
const { closeTab } = useRoomToolbox();
|
||||
const customSound = useCustomSound();
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
const newMessageNotificationPreference = useUserPreference<string>('newMessageNotification', 'chime') as string;
|
||||
|
||||
const { mutateAsync: saveSettings } = useEndpointMutation('POST', '/v1/rooms.saveNotification', {
|
||||
onSuccess: () => {
|
||||
dispatchToastMessage({ type: 'success', message: t('Room_updated_successfully') });
|
||||
},
|
||||
});
|
||||
|
||||
const customSoundAsset: SelectOption[] | undefined = customSound.list?.map((value) => [value._id, value.name]);
|
||||
|
||||
const defaultOption: SelectOption[] = [
|
||||
['default', t('Default')],
|
||||
['all', t('All_messages')],
|
||||
['mentions', t('Mentions')],
|
||||
['nothing', t('Nothing')],
|
||||
];
|
||||
|
||||
const defaultSoundOption: SelectOption[] = [
|
||||
['none', t('None')],
|
||||
['default', t('Default')],
|
||||
];
|
||||
|
||||
const notificationOptions = {
|
||||
alerts: defaultOption,
|
||||
sounds: customSoundAsset ? [...defaultSoundOption, ...customSoundAsset] : defaultSoundOption,
|
||||
};
|
||||
|
||||
const methods = useForm({
|
||||
defaultValues: {
|
||||
turnOn: !subscription?.disableNotifications,
|
||||
muteGroupMentions: !!subscription?.muteGroupMentions,
|
||||
showCounter: !subscription?.hideUnreadStatus,
|
||||
showMentions: !subscription?.hideMentionStatus,
|
||||
desktopAlert: (subscription?.desktopPrefOrigin === 'subscription' && subscription.desktopNotifications) || 'default',
|
||||
desktopSound: subscription?.audioNotificationValue || 'default',
|
||||
mobileAlert: (subscription?.mobilePrefOrigin === 'subscription' && subscription.mobilePushNotifications) || 'default',
|
||||
emailAlert: (subscription?.emailPrefOrigin === 'subscription' && subscription.emailNotifications) || 'default',
|
||||
},
|
||||
});
|
||||
|
||||
const { desktopSound } = methods.watch();
|
||||
|
||||
const handlePlaySound = (): void => {
|
||||
customSound.play(desktopSound === 'default' ? newMessageNotificationPreference : desktopSound);
|
||||
};
|
||||
|
||||
const handleSave = methods.handleSubmit(
|
||||
async ({ turnOn, muteGroupMentions, showCounter, showMentions, desktopAlert, desktopSound, mobileAlert, emailAlert }) => {
|
||||
const notifications = {
|
||||
disableNotifications: turnOn ? '0' : '1',
|
||||
muteGroupMentions: muteGroupMentions ? '1' : '0',
|
||||
hideUnreadStatus: showCounter ? '0' : '1',
|
||||
hideMentionStatus: showMentions ? '0' : '1',
|
||||
desktopNotifications: desktopAlert,
|
||||
audioNotificationValue: desktopSound,
|
||||
mobilePushNotifications: mobileAlert,
|
||||
emailNotifications: emailAlert,
|
||||
};
|
||||
|
||||
await saveSettings({
|
||||
roomId: room._id,
|
||||
notifications,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<NotificationPreferences
|
||||
handleClose={closeTab}
|
||||
handleSave={handleSave}
|
||||
handlePlaySound={handlePlaySound}
|
||||
notificationOptions={notificationOptions}
|
||||
/>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(NotificationPreferencesWithData);
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Box, AccordionItem, Icon, FieldGroup } from '@rocket.chat/fuselage';
|
||||
import type { Keys as IconName } from '@rocket.chat/icons';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
export type NotificationByDeviceProps = {
|
||||
device: string;
|
||||
icon: IconName;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const NotificationByDevice = ({ device, icon, children }: NotificationByDeviceProps) => (
|
||||
<AccordionItem
|
||||
title={
|
||||
<Box display='flex' alignItems='center'>
|
||||
<Icon name={icon} size='x18' />
|
||||
<Box fontScale='p2m' marginInline={16}>
|
||||
{device}
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<FieldGroup>{children}</FieldGroup>
|
||||
</AccordionItem>
|
||||
);
|
||||
|
||||
export default memo(NotificationByDevice);
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { SelectOption } from '@rocket.chat/fuselage';
|
||||
import { Field, FieldLabel, FieldRow, Select } from '@rocket.chat/fuselage';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export type NotificationPreferenceProps = {
|
||||
id: string;
|
||||
name: string;
|
||||
options: SelectOption[];
|
||||
onChange: (e: unknown) => void;
|
||||
optionValue: string;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const NotificationPreference = ({ name, options, onChange, optionValue, children, ...props }: NotificationPreferenceProps) => (
|
||||
<Field {...props}>
|
||||
<FieldLabel>{name}</FieldLabel>
|
||||
<FieldRow>
|
||||
<Select onChange={onChange} options={options} value={optionValue} />
|
||||
{children}
|
||||
</FieldRow>
|
||||
</Field>
|
||||
);
|
||||
|
||||
export default NotificationPreference;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Field, FieldLabel, FieldDescription, FieldGroup, ToggleSwitch, FieldRow } from '@rocket.chat/fuselage';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { memo, useId } from 'react';
|
||||
|
||||
export type NotificationToggleProps = {
|
||||
label: string;
|
||||
description?: string;
|
||||
onChange: (e: unknown) => void;
|
||||
} & ComponentProps<typeof ToggleSwitch>;
|
||||
|
||||
const NotificationToggle = ({ label, description, onChange, ...props }: NotificationToggleProps) => {
|
||||
const fieldId = useId();
|
||||
|
||||
return (
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldRow>
|
||||
<FieldLabel htmlFor={fieldId}>{label}</FieldLabel>
|
||||
<ToggleSwitch id={fieldId} aria-describedby={`${fieldId}-hint`} onChange={onChange} {...props} />
|
||||
</FieldRow>
|
||||
{description && <FieldDescription id={`${fieldId}-hint`}>{description}</FieldDescription>}
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(NotificationToggle);
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './NotificationPreferencesWithData';
|
||||
Reference in New Issue
Block a user