[MM-30432]: Allow users to specify different desktop notification sounds per channel (#21671)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
951456c780
Коммит
fbd6b5efe1
@@ -245,7 +245,7 @@ function setCRTDesktopNotification(type) {
|
|||||||
cy.get('#desktopTitle').
|
cy.get('#desktopTitle').
|
||||||
scrollIntoView().
|
scrollIntoView().
|
||||||
should('be.visible').
|
should('be.visible').
|
||||||
and('contain', 'Send desktop notifications').click();
|
and('contain', 'Desktop notifications').click();
|
||||||
|
|
||||||
// # Select mentions category for messages.
|
// # Select mentions category for messages.
|
||||||
cy.get('#channelNotificationMentions').scrollIntoView().check();
|
cy.get('#channelNotificationMentions').scrollIntoView().check();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ describe('Desktop notifications', () => {
|
|||||||
|
|
||||||
// # Set channel notifications to show on mention only
|
// # Set channel notifications to show on mention only
|
||||||
cy.uiOpenChannelMenu('Notification Preferences');
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
cy.findByText('Send desktop notifications').click();
|
cy.findByText('Desktop notifications').click();
|
||||||
cy.findByRole('radio', {name: 'Only for mentions'}).click();
|
cy.findByRole('radio', {name: 'Only for mentions'}).click();
|
||||||
cy.uiSaveAndClose();
|
cy.uiSaveAndClose();
|
||||||
|
|
||||||
|
|||||||
@@ -1334,6 +1334,14 @@ func (a *App) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]stri
|
|||||||
filteredProps[model.DesktopNotifyProp] = desktop
|
filteredProps[model.DesktopNotifyProp] = desktop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if desktop_sound, exists := data[model.DesktopSoundNotifyProp]; exists {
|
||||||
|
filteredProps[model.DesktopSoundNotifyProp] = desktop_sound
|
||||||
|
}
|
||||||
|
|
||||||
|
if desktop_notification_sound, exists := data["desktop_notification_sound"]; exists {
|
||||||
|
filteredProps["desktop_notification_sound"] = desktop_notification_sound
|
||||||
|
}
|
||||||
|
|
||||||
if desktop_threads, exists := data[model.DesktopThreadsNotifyProp]; exists {
|
if desktop_threads, exists := data[model.DesktopThreadsNotifyProp]; exists {
|
||||||
filteredProps[model.DesktopThreadsNotifyProp] = desktop_threads
|
filteredProps[model.DesktopThreadsNotifyProp] = desktop_threads
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,22 @@ const NOTIFY_TEXT_MAX_LENGTH = 50;
|
|||||||
// windows notification length is based windows chrome which supports 128 characters and is the lowest length of windows browsers
|
// windows notification length is based windows chrome which supports 128 characters and is the lowest length of windows browsers
|
||||||
const WINDOWS_NOTIFY_TEXT_MAX_LENGTH = 120;
|
const WINDOWS_NOTIFY_TEXT_MAX_LENGTH = 120;
|
||||||
|
|
||||||
|
const getSoundFromChannelMemberAndUser = (member, user) => {
|
||||||
|
if (member?.notify_props?.desktop_sound) {
|
||||||
|
return member.notify_props.desktop_sound === 'on';
|
||||||
|
}
|
||||||
|
|
||||||
|
return !user.notify_props || user.notify_props.desktop_sound === 'true';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNotificationSoundFromChannelMemberAndUser = (member, user) => {
|
||||||
|
if (member?.notify_props?.desktop_notification_sound) {
|
||||||
|
return member.notify_props.desktop_notification_sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.notify_props?.desktop_notification_sound ? user.notify_props.desktop_notification_sound : 'Bing';
|
||||||
|
};
|
||||||
|
|
||||||
export function sendDesktopNotification(post, msgProps) {
|
export function sendDesktopNotification(post, msgProps) {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
@@ -159,7 +175,7 @@ export function sendDesktopNotification(post, msgProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Play a sound if explicitly set in settings
|
//Play a sound if explicitly set in settings
|
||||||
const sound = !user.notify_props || user.notify_props.desktop_sound === 'true';
|
const sound = getSoundFromChannelMemberAndUser(member, user);
|
||||||
|
|
||||||
// Notify if you're not looking in the right channel or when
|
// Notify if you're not looking in the right channel or when
|
||||||
// the window itself is not active
|
// the window itself is not active
|
||||||
@@ -174,7 +190,7 @@ export function sendDesktopNotification(post, msgProps) {
|
|||||||
}
|
}
|
||||||
notify = notify || !state.views.browser.focused;
|
notify = notify || !state.views.browser.focused;
|
||||||
|
|
||||||
const soundName = user.notify_props !== undefined && user.notify_props.desktop_notification_sound !== undefined ? user.notify_props.desktop_notification_sound : 'Bing';
|
const soundName = getNotificationSoundFromChannelMemberAndUser(member, user);
|
||||||
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
const updatedState = getState();
|
const updatedState = getState();
|
||||||
|
|||||||
@@ -97,10 +97,17 @@ exports[`components/channel_notifications_modal/ChannelNotificationsModal should
|
|||||||
<NotificationSection
|
<NotificationSection
|
||||||
expand={false}
|
expand={false}
|
||||||
globalNotificationLevel="all"
|
globalNotificationLevel="all"
|
||||||
|
globalNotificationSound="Bing"
|
||||||
|
isNotificationsSettingSameAsGlobal={true}
|
||||||
|
memberDesktopNotificationSound="Bing"
|
||||||
|
memberDesktopSound="on"
|
||||||
memberNotificationLevel="all"
|
memberNotificationLevel="all"
|
||||||
memberThreadsNotificationLevel="all"
|
memberThreadsNotificationLevel="all"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
onChangeDesktopSound={[Function]}
|
||||||
|
onChangeNotificationSound={[Function]}
|
||||||
onChangeThreads={[Function]}
|
onChangeThreads={[Function]}
|
||||||
|
onReset={[Function]}
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
onUpdateSection={[Function]}
|
onUpdateSection={[Function]}
|
||||||
section="desktop"
|
section="desktop"
|
||||||
@@ -111,10 +118,12 @@ exports[`components/channel_notifications_modal/ChannelNotificationsModal should
|
|||||||
/>
|
/>
|
||||||
<NotificationSection
|
<NotificationSection
|
||||||
expand={false}
|
expand={false}
|
||||||
memberNotificationLevel="default"
|
isNotificationsSettingSameAsGlobal={true}
|
||||||
memberThreadsNotificationLevel="default"
|
memberNotificationLevel="all"
|
||||||
|
memberThreadsNotificationLevel="all"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
onChangeThreads={[Function]}
|
onChangeThreads={[Function]}
|
||||||
|
onReset={[Function]}
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
onUpdateSection={[Function]}
|
onUpdateSection={[Function]}
|
||||||
section="push"
|
section="push"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import React, {ComponentProps} from 'react';
|
import React, {ComponentProps} from 'react';
|
||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
import {ChannelAutoFollowThreads, DesktopSound, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
|
|
||||||
import ChannelNotificationsModal from 'components/channel_notifications_modal/channel_notifications_modal';
|
import ChannelNotificationsModal from 'components/channel_notifications_modal/channel_notifications_modal';
|
||||||
@@ -22,6 +22,8 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
|
|||||||
channelMember: {
|
channelMember: {
|
||||||
notify_props: {
|
notify_props: {
|
||||||
desktop: NotificationLevels.ALL,
|
desktop: NotificationLevels.ALL,
|
||||||
|
desktop_sound: DesktopSound.ON,
|
||||||
|
desktop_notification_sound: 'Bing',
|
||||||
mark_unread: NotificationLevels.ALL,
|
mark_unread: NotificationLevels.ALL,
|
||||||
push: NotificationLevels.DEFAULT,
|
push: NotificationLevels.DEFAULT,
|
||||||
ignore_channel_mentions: IgnoreChannelMentions.DEFAULT,
|
ignore_channel_mentions: IgnoreChannelMentions.DEFAULT,
|
||||||
@@ -59,9 +61,11 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
|
|||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.DEFAULT);
|
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.ALL);
|
||||||
|
expect(wrapper.state('desktopSound')).toEqual(DesktopSound.ON);
|
||||||
|
expect(wrapper.state('desktopNotifySound')).toEqual('Bing');
|
||||||
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.ALL);
|
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.ALL);
|
||||||
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.DEFAULT);
|
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.ALL);
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.OFF);
|
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.OFF);
|
||||||
expect(wrapper.state('channelAutoFollowThreads')).toEqual(ChannelAutoFollowThreads.OFF);
|
expect(wrapper.state('channelAutoFollowThreads')).toEqual(ChannelAutoFollowThreads.OFF);
|
||||||
});
|
});
|
||||||
@@ -186,7 +190,7 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
|
|||||||
wrapper.instance().handleExit();
|
wrapper.instance().handleExit();
|
||||||
expect(baseProps.onExited).toHaveBeenCalledTimes(3);
|
expect(baseProps.onExited).toHaveBeenCalledTimes(3);
|
||||||
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
|
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
|
||||||
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.DEFAULT);
|
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.ALL);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match state on updateSection', () => {
|
test('should match state on updateSection', () => {
|
||||||
@@ -214,7 +218,7 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
|
|||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(baseProps.channelMember?.notify_props.desktop);
|
expect(wrapper.state('desktopNotifyLevel')).toEqual(baseProps.channelMember?.notify_props.desktop);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match state on handleSubmitDesktopNotifyLevel', () => {
|
test('should match state on handleSubmitDesktopNotification', () => {
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
const wrapper = shallow<ChannelNotificationsModal>(
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
<ChannelNotificationsModal {...baseProps}/>,
|
||||||
);
|
);
|
||||||
@@ -223,12 +227,12 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
|
|||||||
instance.handleUpdateChannelNotifyProps = jest.fn();
|
instance.handleUpdateChannelNotifyProps = jest.fn();
|
||||||
instance.updateSection = jest.fn();
|
instance.updateSection = jest.fn();
|
||||||
|
|
||||||
wrapper.setState({desktopNotifyLevel: NotificationLevels.DEFAULT});
|
wrapper.setState({desktopNotifyLevel: NotificationLevels.MENTION});
|
||||||
instance.handleSubmitDesktopNotifyLevel();
|
instance.handleSubmitDesktopNotification();
|
||||||
expect(instance.handleUpdateChannelNotifyProps).toHaveBeenCalledTimes(1);
|
expect(instance.handleUpdateChannelNotifyProps).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
wrapper.setState({desktopNotifyLevel: NotificationLevels.ALL});
|
wrapper.setState({desktopNotifyLevel: NotificationLevels.ALL});
|
||||||
instance.handleSubmitDesktopNotifyLevel();
|
instance.handleSubmitDesktopNotification();
|
||||||
expect(instance.updateSection).toHaveBeenCalledTimes(1);
|
expect(instance.updateSection).toHaveBeenCalledTimes(1);
|
||||||
expect(instance.updateSection).toBeCalledWith('');
|
expect(instance.updateSection).toBeCalledWith('');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import React from 'react';
|
|||||||
import {Modal} from 'react-bootstrap';
|
import {Modal} from 'react-bootstrap';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import * as NotificationSounds from 'utils/notification_sounds';
|
||||||
|
|
||||||
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
|
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
import {ChannelAutoFollowThreads, DesktopSound, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
||||||
|
|
||||||
import NotificationSection from 'components/channel_notifications_modal/components/notification_section.jsx';
|
import NotificationSection from 'components/channel_notifications_modal/components/notification_section.jsx';
|
||||||
|
|
||||||
@@ -42,6 +44,8 @@ type State = {
|
|||||||
activeSection: string;
|
activeSection: string;
|
||||||
serverError: string | null;
|
serverError: string | null;
|
||||||
desktopNotifyLevel: ChannelNotifyProps['desktop'];
|
desktopNotifyLevel: ChannelNotifyProps['desktop'];
|
||||||
|
desktopSound: ChannelNotifyProps['desktop_sound'];
|
||||||
|
desktopNotifySound: ChannelNotifyProps['desktop_notification_sound'];
|
||||||
desktopThreadsNotifyLevel: UserNotifyProps['desktop_threads'];
|
desktopThreadsNotifyLevel: UserNotifyProps['desktop_threads'];
|
||||||
markUnreadNotifyLevel: ChannelNotifyProps['mark_unread'];
|
markUnreadNotifyLevel: ChannelNotifyProps['mark_unread'];
|
||||||
pushNotifyLevel: ChannelNotifyProps['push'];
|
pushNotifyLevel: ChannelNotifyProps['push'];
|
||||||
@@ -50,6 +54,59 @@ type State = {
|
|||||||
channelAutoFollowThreads: ChannelNotifyProps['channel_auto_follow_threads'];
|
channelAutoFollowThreads: ChannelNotifyProps['channel_auto_follow_threads'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DesktopNotificationProps = Pick<State, 'desktopNotifyLevel' | 'desktopNotifySound' | 'desktopSound' | 'desktopThreadsNotifyLevel'>
|
||||||
|
export type PushNotificationProps = Pick<State, 'pushNotifyLevel' | 'pushThreadsNotifyLevel'>
|
||||||
|
|
||||||
|
const getDefaultDesktopNotificationLevel = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop'], undefined> => {
|
||||||
|
if (currentUserNotifyProps?.desktop) {
|
||||||
|
if (currentUserNotifyProps.desktop === 'default') {
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
}
|
||||||
|
return currentUserNotifyProps.desktop;
|
||||||
|
}
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultDesktopSound = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop_sound'], undefined> => {
|
||||||
|
if (currentUserNotifyProps?.desktop_sound) {
|
||||||
|
return currentUserNotifyProps.desktop_sound === 'true' ? DesktopSound.ON : DesktopSound.OFF;
|
||||||
|
}
|
||||||
|
return DesktopSound.ON;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultDesktopNotificationSound = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop_notification_sound'], undefined> => {
|
||||||
|
if (currentUserNotifyProps?.desktop_notification_sound) {
|
||||||
|
return currentUserNotifyProps.desktop_notification_sound;
|
||||||
|
}
|
||||||
|
return 'Bing';
|
||||||
|
};
|
||||||
|
const getDefaultDesktopThreadsNotifyLevel = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop_threads'], undefined> => {
|
||||||
|
if (currentUserNotifyProps?.desktop_threads) {
|
||||||
|
return currentUserNotifyProps.desktop_threads;
|
||||||
|
}
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultPushNotifyLevel = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['push'], undefined> => {
|
||||||
|
if (currentUserNotifyProps?.push) {
|
||||||
|
if (currentUserNotifyProps.push === 'default') {
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
}
|
||||||
|
return currentUserNotifyProps.push;
|
||||||
|
}
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultPushThreadsNotifyLevel = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['push_threads'], undefined> => {
|
||||||
|
if (currentUserNotifyProps?.push_threads) {
|
||||||
|
if (currentUserNotifyProps.push_threads === 'default') {
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
}
|
||||||
|
return currentUserNotifyProps.push_threads;
|
||||||
|
}
|
||||||
|
return NotificationLevels.ALL;
|
||||||
|
};
|
||||||
|
|
||||||
export default class ChannelNotificationsModal extends React.PureComponent<Props, State> {
|
export default class ChannelNotificationsModal extends React.PureComponent<Props, State> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -77,9 +134,63 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
this.setState(this.getStateFromNotifyProps(currentUserNotifyProps, channelMemberNotifyProps));
|
this.setState(this.getStateFromNotifyProps(currentUserNotifyProps, channelMemberNotifyProps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verifyNotificationsSettingSameAsGlobal({
|
||||||
|
desktopNotifyLevel,
|
||||||
|
desktopNotifySound,
|
||||||
|
desktopSound,
|
||||||
|
desktopThreadsNotifyLevel,
|
||||||
|
}: DesktopNotificationProps) {
|
||||||
|
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
||||||
|
|
||||||
|
if (
|
||||||
|
desktopNotifyLevel === getDefaultDesktopNotificationLevel(currentUserNotifyProps) &&
|
||||||
|
desktopNotifySound === getDefaultDesktopNotificationSound(currentUserNotifyProps) &&
|
||||||
|
desktopSound === getDefaultDesktopSound(currentUserNotifyProps) &&
|
||||||
|
desktopThreadsNotifyLevel === getDefaultDesktopThreadsNotifyLevel(currentUserNotifyProps)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyPushNotificationsSettingSameAsGlobal({
|
||||||
|
pushNotifyLevel,
|
||||||
|
pushThreadsNotifyLevel,
|
||||||
|
}: PushNotificationProps) {
|
||||||
|
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
||||||
|
|
||||||
|
if (
|
||||||
|
pushNotifyLevel === getDefaultPushNotifyLevel(currentUserNotifyProps) &&
|
||||||
|
pushThreadsNotifyLevel === getDefaultPushThreadsNotifyLevel(currentUserNotifyProps)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
getStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
|
getStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
|
||||||
let ignoreChannelMentionsDefault: ChannelNotifyProps['ignore_channel_mentions'] = IgnoreChannelMentions.OFF;
|
let ignoreChannelMentionsDefault: ChannelNotifyProps['ignore_channel_mentions'] = IgnoreChannelMentions.OFF;
|
||||||
|
|
||||||
|
let desktopNotifyLevelDefault: ChannelNotifyProps['desktop'] = getDefaultDesktopNotificationLevel(currentUserNotifyProps);
|
||||||
|
let pushNotifyLevelDefault: ChannelMemberNotifyProps['push'] = getDefaultPushNotifyLevel(currentUserNotifyProps);
|
||||||
|
let pushThreadsNotifyLevelDefault: ChannelMemberNotifyProps['push_threads'] = getDefaultPushThreadsNotifyLevel(currentUserNotifyProps);
|
||||||
|
|
||||||
|
if (channelMemberNotifyProps?.desktop) {
|
||||||
|
if (channelMemberNotifyProps.desktop !== 'default') {
|
||||||
|
desktopNotifyLevelDefault = channelMemberNotifyProps.desktop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (channelMemberNotifyProps?.push) {
|
||||||
|
if (channelMemberNotifyProps.push !== 'default') {
|
||||||
|
pushNotifyLevelDefault = channelMemberNotifyProps.push;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (channelMemberNotifyProps?.push_threads) {
|
||||||
|
if (channelMemberNotifyProps.push_threads !== 'default') {
|
||||||
|
pushThreadsNotifyLevelDefault = channelMemberNotifyProps.push_threads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (channelMemberNotifyProps?.mark_unread === NotificationLevels.MENTION || (currentUserNotifyProps.channel && currentUserNotifyProps.channel === 'false')) {
|
if (channelMemberNotifyProps?.mark_unread === NotificationLevels.MENTION || (currentUserNotifyProps.channel && currentUserNotifyProps.channel === 'false')) {
|
||||||
ignoreChannelMentionsDefault = IgnoreChannelMentions.ON;
|
ignoreChannelMentionsDefault = IgnoreChannelMentions.ON;
|
||||||
}
|
}
|
||||||
@@ -90,11 +201,13 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
desktopNotifyLevel: channelMemberNotifyProps?.desktop || NotificationLevels.DEFAULT,
|
desktopNotifyLevel: desktopNotifyLevelDefault,
|
||||||
desktopThreadsNotifyLevel: channelMemberNotifyProps?.desktop_threads || NotificationLevels.ALL,
|
desktopSound: channelMemberNotifyProps?.desktop_sound || getDefaultDesktopSound(currentUserNotifyProps),
|
||||||
|
desktopNotifySound: channelMemberNotifyProps?.desktop_notification_sound || getDefaultDesktopNotificationSound(currentUserNotifyProps),
|
||||||
|
desktopThreadsNotifyLevel: channelMemberNotifyProps?.desktop_threads || getDefaultDesktopThreadsNotifyLevel(currentUserNotifyProps),
|
||||||
markUnreadNotifyLevel: channelMemberNotifyProps?.mark_unread || NotificationLevels.ALL,
|
markUnreadNotifyLevel: channelMemberNotifyProps?.mark_unread || NotificationLevels.ALL,
|
||||||
pushNotifyLevel: channelMemberNotifyProps?.push || NotificationLevels.DEFAULT,
|
pushNotifyLevel: pushNotifyLevelDefault,
|
||||||
pushThreadsNotifyLevel: channelMemberNotifyProps?.push_threads || NotificationLevels.ALL,
|
pushThreadsNotifyLevel: pushThreadsNotifyLevelDefault,
|
||||||
ignoreChannelMentions,
|
ignoreChannelMentions,
|
||||||
channelAutoFollowThreads: channelMemberNotifyProps?.channel_auto_follow_threads || ChannelAutoFollowThreads.OFF,
|
channelAutoFollowThreads: channelMemberNotifyProps?.channel_auto_follow_threads || ChannelAutoFollowThreads.OFF,
|
||||||
};
|
};
|
||||||
@@ -130,20 +243,45 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
this.updateSection(NotificationSections.NONE);
|
this.updateSection(NotificationSections.NONE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
handleResetDesktopNotification = () => {
|
||||||
|
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
||||||
|
|
||||||
handleSubmitDesktopNotifyLevel = () => {
|
const userDesktopNotificationDefaults = {
|
||||||
|
desktopNotifyLevel: getDefaultDesktopNotificationLevel(currentUserNotifyProps),
|
||||||
|
desktopSound: getDefaultDesktopSound(currentUserNotifyProps),
|
||||||
|
desktopNotifySound: getDefaultDesktopNotificationSound(currentUserNotifyProps),
|
||||||
|
desktopThreadsNotifyLevel: getDefaultDesktopThreadsNotifyLevel(currentUserNotifyProps),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setState(userDesktopNotificationDefaults);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleResetPushNotification = () => {
|
||||||
|
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
||||||
|
|
||||||
|
const userPushNotificationDefaults = {
|
||||||
|
pushNotifyLevel: getDefaultPushNotifyLevel(currentUserNotifyProps),
|
||||||
|
pushThreadsNotifyLevel: getDefaultPushThreadsNotifyLevel(currentUserNotifyProps),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setState(userPushNotificationDefaults);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSubmitDesktopNotification = () => {
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props as ChannelMemberNotifyProps;
|
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props as ChannelMemberNotifyProps;
|
||||||
const {desktopNotifyLevel, desktopThreadsNotifyLevel} = this.state;
|
const {desktopNotifyLevel, desktopNotifySound, desktopSound, desktopThreadsNotifyLevel} = this.state;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
channelNotifyProps?.desktop === desktopNotifyLevel &&
|
channelNotifyProps?.desktop === desktopNotifyLevel &&
|
||||||
channelNotifyProps?.desktop_threads === desktopThreadsNotifyLevel
|
channelNotifyProps?.desktop_threads === desktopThreadsNotifyLevel &&
|
||||||
|
channelNotifyProps?.desktop_sound === desktopSound &&
|
||||||
|
channelNotifyProps?.desktop_notification_sound === desktopNotifySound
|
||||||
) {
|
) {
|
||||||
this.updateSection(NotificationSections.NONE);
|
this.updateSection(NotificationSections.NONE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = {desktop: desktopNotifyLevel, desktop_threads: desktopThreadsNotifyLevel};
|
const props = {desktop: desktopNotifyLevel, desktop_threads: desktopThreadsNotifyLevel, desktop_sound: desktopSound, desktop_notification_sound: desktopNotifySound};
|
||||||
|
|
||||||
this.handleUpdateChannelNotifyProps(props);
|
this.handleUpdateChannelNotifyProps(props);
|
||||||
};
|
};
|
||||||
@@ -152,6 +290,15 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
|
|
||||||
handleUpdateDesktopThreadsNotifyLevel = (desktopThreadsNotifyLevel: UserNotifyProps['desktop_threads']) => this.setState({desktopThreadsNotifyLevel});
|
handleUpdateDesktopThreadsNotifyLevel = (desktopThreadsNotifyLevel: UserNotifyProps['desktop_threads']) => this.setState({desktopThreadsNotifyLevel});
|
||||||
|
|
||||||
|
handleUpdateDesktopSound = (desktopSound: ChannelNotifyProps['desktop_sound']) => this.setState({desktopSound});
|
||||||
|
|
||||||
|
handleUpdateDesktopNotifySound = (desktopNotifySound: ChannelNotifyProps['desktop_notification_sound']) => {
|
||||||
|
if (desktopNotifySound) {
|
||||||
|
NotificationSounds.tryNotificationSound(desktopNotifySound);
|
||||||
|
}
|
||||||
|
this.setState({desktopNotifySound});
|
||||||
|
};
|
||||||
|
|
||||||
handleSubmitMarkUnreadLevel = () => {
|
handleSubmitMarkUnreadLevel = () => {
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
||||||
const {markUnreadNotifyLevel} = this.state;
|
const {markUnreadNotifyLevel} = this.state;
|
||||||
@@ -220,6 +367,8 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
activeSection,
|
activeSection,
|
||||||
desktopNotifyLevel,
|
desktopNotifyLevel,
|
||||||
desktopThreadsNotifyLevel,
|
desktopThreadsNotifyLevel,
|
||||||
|
desktopSound,
|
||||||
|
desktopNotifySound,
|
||||||
markUnreadNotifyLevel,
|
markUnreadNotifyLevel,
|
||||||
pushNotifyLevel,
|
pushNotifyLevel,
|
||||||
pushThreadsNotifyLevel,
|
pushThreadsNotifyLevel,
|
||||||
@@ -235,6 +384,18 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
sendPushNotifications,
|
sendPushNotifications,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const isNotificationsSettingSameAsGlobal = this.verifyNotificationsSettingSameAsGlobal({
|
||||||
|
desktopNotifyLevel,
|
||||||
|
desktopNotifySound,
|
||||||
|
desktopSound,
|
||||||
|
desktopThreadsNotifyLevel,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isPushNotificationsSettingSameAsGlobal = this.verifyPushNotificationsSettingSameAsGlobal({
|
||||||
|
pushNotifyLevel,
|
||||||
|
pushThreadsNotifyLevel,
|
||||||
|
});
|
||||||
|
|
||||||
let serverErrorTag = null;
|
let serverErrorTag = null;
|
||||||
if (serverError) {
|
if (serverError) {
|
||||||
serverErrorTag = <div className='form-group has-error'><label className='control-label'>{serverError}</label></div>;
|
serverErrorTag = <div className='form-group has-error'><label className='control-label'>{serverError}</label></div>;
|
||||||
@@ -295,10 +456,17 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
expand={activeSection === NotificationSections.DESKTOP}
|
expand={activeSection === NotificationSections.DESKTOP}
|
||||||
memberNotificationLevel={desktopNotifyLevel}
|
memberNotificationLevel={desktopNotifyLevel}
|
||||||
memberThreadsNotificationLevel={desktopThreadsNotifyLevel}
|
memberThreadsNotificationLevel={desktopThreadsNotifyLevel}
|
||||||
|
memberDesktopSound={desktopSound}
|
||||||
|
memberDesktopNotificationSound={desktopNotifySound}
|
||||||
globalNotificationLevel={currentUser.notify_props ? currentUser.notify_props.desktop : NotificationLevels.ALL}
|
globalNotificationLevel={currentUser.notify_props ? currentUser.notify_props.desktop : NotificationLevels.ALL}
|
||||||
|
globalNotificationSound={(currentUser.notify_props && currentUser.notify_props.desktop_notification_sound) ? currentUser.notify_props.desktop_notification_sound : 'Bing'}
|
||||||
|
isNotificationsSettingSameAsGlobal={isNotificationsSettingSameAsGlobal}
|
||||||
onChange={this.handleUpdateDesktopNotifyLevel}
|
onChange={this.handleUpdateDesktopNotifyLevel}
|
||||||
onChangeThreads={this.handleUpdateDesktopThreadsNotifyLevel}
|
onChangeThreads={this.handleUpdateDesktopThreadsNotifyLevel}
|
||||||
onSubmit={this.handleSubmitDesktopNotifyLevel}
|
onChangeDesktopSound={this.handleUpdateDesktopSound}
|
||||||
|
onChangeNotificationSound={this.handleUpdateDesktopNotifySound}
|
||||||
|
onReset={this.handleResetDesktopNotification}
|
||||||
|
onSubmit={this.handleSubmitDesktopNotification}
|
||||||
onUpdateSection={this.updateSection}
|
onUpdateSection={this.updateSection}
|
||||||
serverError={serverError}
|
serverError={serverError}
|
||||||
/>
|
/>
|
||||||
@@ -310,7 +478,9 @@ export default class ChannelNotificationsModal extends React.PureComponent<Props
|
|||||||
memberNotificationLevel={pushNotifyLevel}
|
memberNotificationLevel={pushNotifyLevel}
|
||||||
memberThreadsNotificationLevel={pushThreadsNotifyLevel}
|
memberThreadsNotificationLevel={pushThreadsNotifyLevel}
|
||||||
globalNotificationLevel={currentUser.notify_props ? currentUser.notify_props.push : NotificationLevels.ALL}
|
globalNotificationLevel={currentUser.notify_props ? currentUser.notify_props.push : NotificationLevels.ALL}
|
||||||
|
isNotificationsSettingSameAsGlobal={isPushNotificationsSettingSameAsGlobal}
|
||||||
onChange={this.handleUpdatePushNotificationLevel}
|
onChange={this.handleUpdatePushNotificationLevel}
|
||||||
|
onReset={this.handleResetPushNotification}
|
||||||
onChangeThreads={this.handleUpdatePushThreadsNotificationLevel}
|
onChangeThreads={this.handleUpdatePushThreadsNotificationLevel}
|
||||||
onSubmit={this.handleSubmitPushNotificationLevel}
|
onSubmit={this.handleSubmitPushNotificationLevel}
|
||||||
onUpdateSection={this.updateSection}
|
onUpdateSection={this.updateSection}
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on DESKTOP/PUSH & ALL 1`] = `
|
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on DESKTOP/PUSH & ALL 1`] = `
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="For all activity"
|
defaultMessage="For all activity ({isDefault})"
|
||||||
id="channel_notifications.allActivity"
|
id="channel_notifications.allActivity"
|
||||||
|
values={
|
||||||
|
Object {
|
||||||
|
"isDefault": <React.Fragment />,
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -16,15 +21,25 @@ exports[`components/channel_notifications_modal/NotificationSection should match
|
|||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on MENTION 1`] = `
|
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on MENTION 1`] = `
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Only for mentions"
|
defaultMessage="Only for mentions ({isDefault})"
|
||||||
id="channel_notifications.onlyMentions"
|
id="channel_notifications.onlyMentions"
|
||||||
|
values={
|
||||||
|
Object {
|
||||||
|
"isDefault": <React.Fragment />,
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on NONE 1`] = `
|
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on NONE 1`] = `
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Never"
|
defaultMessage="Never ({isDefault})"
|
||||||
id="channel_notifications.never"
|
id="channel_notifications.never"
|
||||||
|
values={
|
||||||
|
Object {
|
||||||
|
"isDefault": <React.Fragment />,
|
||||||
|
}
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -8,27 +8,14 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
Array [
|
Array [
|
||||||
<div>
|
<div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div
|
<legend
|
||||||
className="radio"
|
className="form-legend"
|
||||||
>
|
>
|
||||||
<label
|
<Memo(MemoizedFormattedMessage)
|
||||||
className=""
|
defaultMessage="Send desktop notifications"
|
||||||
>
|
id="channel_notifications.sendDesktop"
|
||||||
<input
|
/>
|
||||||
checked={false}
|
</legend>
|
||||||
id="channelNotificationGlobalDefault"
|
|
||||||
name="channelDesktopNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="default"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="default"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
className="radio"
|
className="radio"
|
||||||
>
|
>
|
||||||
@@ -38,12 +25,13 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
<input
|
<input
|
||||||
checked={true}
|
checked={true}
|
||||||
id="channelNotificationAllActivity"
|
id="channelNotificationAllActivity"
|
||||||
name="channelDesktopNotifications"
|
name="channelNotifications"
|
||||||
onChange={[MockFunction]}
|
onChange={[MockFunction]}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="all"
|
value="all"
|
||||||
/>
|
/>
|
||||||
<Describe
|
<Describe
|
||||||
|
globalNotifyLevel="default"
|
||||||
memberNotifyLevel="all"
|
memberNotifyLevel="all"
|
||||||
section="desktop"
|
section="desktop"
|
||||||
/>
|
/>
|
||||||
@@ -58,12 +46,13 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
<input
|
<input
|
||||||
checked={false}
|
checked={false}
|
||||||
id="channelNotificationMentions"
|
id="channelNotificationMentions"
|
||||||
name="channelDesktopNotifications"
|
name="channelNotifications"
|
||||||
onChange={[MockFunction]}
|
onChange={[MockFunction]}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="mention"
|
value="mention"
|
||||||
/>
|
/>
|
||||||
<Describe
|
<Describe
|
||||||
|
globalNotifyLevel="default"
|
||||||
memberNotifyLevel="mention"
|
memberNotifyLevel="mention"
|
||||||
section="desktop"
|
section="desktop"
|
||||||
/>
|
/>
|
||||||
@@ -76,12 +65,13 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
<input
|
<input
|
||||||
checked={false}
|
checked={false}
|
||||||
id="channelNotificationNever"
|
id="channelNotificationNever"
|
||||||
name="channelDesktopNotifications"
|
name="channelNotifications"
|
||||||
onChange={[MockFunction]}
|
onChange={[MockFunction]}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="none"
|
value="none"
|
||||||
/>
|
/>
|
||||||
<Describe
|
<Describe
|
||||||
|
globalNotifyLevel="default"
|
||||||
memberNotifyLevel="none"
|
memberNotifyLevel="none"
|
||||||
section="desktop"
|
section="desktop"
|
||||||
/>
|
/>
|
||||||
@@ -95,6 +85,63 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
section="desktop"
|
section="desktop"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<React.Fragment>
|
||||||
|
<hr />
|
||||||
|
<fieldset>
|
||||||
|
<legend
|
||||||
|
className="form-legend"
|
||||||
|
>
|
||||||
|
<Memo(MemoizedFormattedMessage)
|
||||||
|
defaultMessage="Notification sound"
|
||||||
|
id="channel_notifications.sound"
|
||||||
|
/>
|
||||||
|
</legend>
|
||||||
|
<div
|
||||||
|
className="radio"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
className=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
checked={false}
|
||||||
|
id="channelDesktopSoundOn"
|
||||||
|
name="channelDesktopSound"
|
||||||
|
type="radio"
|
||||||
|
value="on"
|
||||||
|
/>
|
||||||
|
<Memo(MemoizedFormattedMessage)
|
||||||
|
defaultMessage="On"
|
||||||
|
id="channel_notifications.sound.on.title"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="radio"
|
||||||
|
>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
checked={false}
|
||||||
|
id="channelDesktopSoundOff"
|
||||||
|
name="channelDesktopSound"
|
||||||
|
type="radio"
|
||||||
|
value="off"
|
||||||
|
/>
|
||||||
|
<Memo(MemoizedFormattedMessage)
|
||||||
|
defaultMessage="Off"
|
||||||
|
id="channel_notifications.sound.off.title"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="mt-5"
|
||||||
|
>
|
||||||
|
<Memo(MemoizedFormattedMessage)
|
||||||
|
defaultMessage="Notification sounds are available on Firefox, Edge, Safari, Chrome and Mattermost Desktop Apps."
|
||||||
|
id="channel_notifications.sound_info"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</React.Fragment>
|
||||||
</div>,
|
</div>,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -104,6 +151,8 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
submit={[MockFunction]}
|
submit={[MockFunction]}
|
||||||
title={
|
title={
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
|
isExpanded={true}
|
||||||
|
onClickResetButton={[MockFunction]}
|
||||||
section="desktop"
|
section="desktop"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -176,6 +225,8 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
submit={[MockFunction]}
|
submit={[MockFunction]}
|
||||||
title={
|
title={
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
|
isExpanded={true}
|
||||||
|
onClickResetButton={[MockFunction]}
|
||||||
section="markUnread"
|
section="markUnread"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -191,27 +242,14 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
Array [
|
Array [
|
||||||
<div>
|
<div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div
|
<legend
|
||||||
className="radio"
|
className="form-legend"
|
||||||
>
|
>
|
||||||
<label
|
<Memo(MemoizedFormattedMessage)
|
||||||
className=""
|
defaultMessage="Send mobile push notifications"
|
||||||
>
|
id="channel_notifications.sendMobilePush"
|
||||||
<input
|
/>
|
||||||
checked={false}
|
</legend>
|
||||||
id="channelNotificationGlobalDefault"
|
|
||||||
name="channelDesktopNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="default"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="default"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
className="radio"
|
className="radio"
|
||||||
>
|
>
|
||||||
@@ -221,12 +259,13 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
<input
|
<input
|
||||||
checked={true}
|
checked={true}
|
||||||
id="channelNotificationAllActivity"
|
id="channelNotificationAllActivity"
|
||||||
name="channelDesktopNotifications"
|
name="channelNotifications"
|
||||||
onChange={[MockFunction]}
|
onChange={[MockFunction]}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="all"
|
value="all"
|
||||||
/>
|
/>
|
||||||
<Describe
|
<Describe
|
||||||
|
globalNotifyLevel="default"
|
||||||
memberNotifyLevel="all"
|
memberNotifyLevel="all"
|
||||||
section="push"
|
section="push"
|
||||||
/>
|
/>
|
||||||
@@ -241,12 +280,13 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
<input
|
<input
|
||||||
checked={false}
|
checked={false}
|
||||||
id="channelNotificationMentions"
|
id="channelNotificationMentions"
|
||||||
name="channelDesktopNotifications"
|
name="channelNotifications"
|
||||||
onChange={[MockFunction]}
|
onChange={[MockFunction]}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="mention"
|
value="mention"
|
||||||
/>
|
/>
|
||||||
<Describe
|
<Describe
|
||||||
|
globalNotifyLevel="default"
|
||||||
memberNotifyLevel="mention"
|
memberNotifyLevel="mention"
|
||||||
section="push"
|
section="push"
|
||||||
/>
|
/>
|
||||||
@@ -259,12 +299,13 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
<input
|
<input
|
||||||
checked={false}
|
checked={false}
|
||||||
id="channelNotificationNever"
|
id="channelNotificationNever"
|
||||||
name="channelDesktopNotifications"
|
name="channelNotifications"
|
||||||
onChange={[MockFunction]}
|
onChange={[MockFunction]}
|
||||||
type="radio"
|
type="radio"
|
||||||
value="none"
|
value="none"
|
||||||
/>
|
/>
|
||||||
<Describe
|
<Describe
|
||||||
|
globalNotifyLevel="default"
|
||||||
memberNotifyLevel="none"
|
memberNotifyLevel="none"
|
||||||
section="push"
|
section="push"
|
||||||
/>
|
/>
|
||||||
@@ -287,6 +328,8 @@ exports[`components/channel_notifications_modal/ExpandView should match snapshot
|
|||||||
submit={[MockFunction]}
|
submit={[MockFunction]}
|
||||||
title={
|
title={
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
|
isExpanded={true}
|
||||||
|
onClickResetButton={[MockFunction]}
|
||||||
section="push"
|
section="push"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on DESKTOP 1`] = `
|
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on DESKTOP 1`] = `
|
||||||
<span>
|
<span>
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Selecting an option other than \\"Default\\" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome."
|
defaultMessage="Selecting an option other than the \\"default\\" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome."
|
||||||
id="channel_notifications.override"
|
id="channel_notifications.override"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
@@ -21,7 +21,7 @@ exports[`components/channel_notifications_modal/ExtraInfo should match snapshot,
|
|||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on PUSH 1`] = `
|
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on PUSH 1`] = `
|
||||||
<span>
|
<span>
|
||||||
<MemoizedFormattedMessage
|
<MemoizedFormattedMessage
|
||||||
defaultMessage="Selecting an option other than \\"Global default\\" will override the global notification settings for mobile push notifications in Settings. Push notifications must be enabled by the System Admin."
|
defaultMessage="Selecting an option other than the \\"default\\" will override the global notification settings for mobile push notifications."
|
||||||
id="channel_notifications.overridePush"
|
id="channel_notifications.overridePush"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -24,8 +24,11 @@ exports[`components/channel_notifications_modal/NotificationSection should match
|
|||||||
memberNotifyLevel="all"
|
memberNotifyLevel="all"
|
||||||
memberThreadsNotifyLevel="all"
|
memberThreadsNotifyLevel="all"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
onChangeDesktopSound={[Function]}
|
||||||
|
onChangeNotificationSound={[Function]}
|
||||||
onChangeThreads={[Function]}
|
onChangeThreads={[Function]}
|
||||||
onCollapseSection={[Function]}
|
onCollapseSection={[Function]}
|
||||||
|
onReset={[Function]}
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
section="desktop"
|
section="desktop"
|
||||||
serverError=""
|
serverError=""
|
||||||
@@ -47,8 +50,11 @@ exports[`components/channel_notifications_modal/NotificationSection should match
|
|||||||
memberNotifyLevel="all"
|
memberNotifyLevel="all"
|
||||||
memberThreadsNotifyLevel="all"
|
memberThreadsNotifyLevel="all"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
onChangeDesktopSound={[Function]}
|
||||||
|
onChangeNotificationSound={[Function]}
|
||||||
onChangeThreads={[Function]}
|
onChangeThreads={[Function]}
|
||||||
onCollapseSection={[Function]}
|
onCollapseSection={[Function]}
|
||||||
|
onReset={[Function]}
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
section="markUnread"
|
section="markUnread"
|
||||||
serverError=""
|
serverError=""
|
||||||
@@ -70,8 +76,11 @@ exports[`components/channel_notifications_modal/NotificationSection should match
|
|||||||
memberNotifyLevel="all"
|
memberNotifyLevel="all"
|
||||||
memberThreadsNotifyLevel="all"
|
memberThreadsNotifyLevel="all"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
onChangeDesktopSound={[Function]}
|
||||||
|
onChangeNotificationSound={[Function]}
|
||||||
onChangeThreads={[Function]}
|
onChangeThreads={[Function]}
|
||||||
onCollapseSection={[Function]}
|
onCollapseSection={[Function]}
|
||||||
|
onReset={[Function]}
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
section="push"
|
section="push"
|
||||||
serverError=""
|
serverError=""
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on DESKTOP 1`] = `
|
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on DESKTOP 1`] = `
|
||||||
<MemoizedFormattedMessage
|
<div
|
||||||
defaultMessage="Send desktop notifications"
|
className="SectionTitle__wrapper"
|
||||||
id="channel_notifications.sendDesktop"
|
>
|
||||||
/>
|
<MemoizedFormattedMessage
|
||||||
|
defaultMessage="Desktop notifications"
|
||||||
|
id="channel_notifications.desktopNotifications"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on MARK_UNREAD 1`] = `
|
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on MARK_UNREAD 1`] = `
|
||||||
@@ -15,8 +19,12 @@ exports[`components/channel_notifications_modal/ExtraInfo should match snapshot,
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on PUSH 1`] = `
|
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on PUSH 1`] = `
|
||||||
<MemoizedFormattedMessage
|
<div
|
||||||
defaultMessage="Send mobile push notifications"
|
className="SectionTitle__wrapper"
|
||||||
id="channel_notifications.push"
|
>
|
||||||
/>
|
<MemoizedFormattedMessage
|
||||||
|
defaultMessage="Mobile push notifications"
|
||||||
|
id="channel_notifications.push"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ type Props = {
|
|||||||
isCollapsed?: boolean;
|
isCollapsed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultOption = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.defaultOption'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
export default function Describe({section, isCollapsed, memberNotifyLevel, globalNotifyLevel, ignoreChannelMentions, channelAutoFollowThreads}: Props) {
|
export default function Describe({section, isCollapsed, memberNotifyLevel, globalNotifyLevel, ignoreChannelMentions, channelAutoFollowThreads}: Props) {
|
||||||
if (memberNotifyLevel === NotificationLevels.DEFAULT && globalNotifyLevel) {
|
if (memberNotifyLevel === NotificationLevels.DEFAULT && globalNotifyLevel) {
|
||||||
t('channel_notifications.levels.default');
|
t('channel_notifications.levels.default');
|
||||||
@@ -95,7 +102,8 @@ export default function Describe({section, isCollapsed, memberNotifyLevel, globa
|
|||||||
return (
|
return (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='channel_notifications.onlyMentions'
|
id='channel_notifications.onlyMentions'
|
||||||
defaultMessage='Only for mentions'
|
defaultMessage='Only for mentions ({isDefault})'
|
||||||
|
values={{isDefault: globalNotifyLevel === NotificationLevels.MENTION ? defaultOption : <></>}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
@@ -105,7 +113,8 @@ export default function Describe({section, isCollapsed, memberNotifyLevel, globa
|
|||||||
return (
|
return (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='channel_notifications.allActivity'
|
id='channel_notifications.allActivity'
|
||||||
defaultMessage='For all activity'
|
defaultMessage='For all activity ({isDefault})'
|
||||||
|
values={{isDefault: globalNotifyLevel === NotificationLevels.ALL ? defaultOption : <></>}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
@@ -123,7 +132,8 @@ export default function Describe({section, isCollapsed, memberNotifyLevel, globa
|
|||||||
return (
|
return (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='channel_notifications.never'
|
id='channel_notifications.never'
|
||||||
defaultMessage='Never'
|
defaultMessage='Never ({isDefault})'
|
||||||
|
values={{isDefault: globalNotifyLevel === NotificationLevels.NONE ? defaultOption : <></>}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ describe('components/channel_notifications_modal/ExpandView', () => {
|
|||||||
onChangeThreads: jest.fn(),
|
onChangeThreads: jest.fn(),
|
||||||
onCollapseSection: jest.fn(),
|
onCollapseSection: jest.fn(),
|
||||||
onSubmit: jest.fn(),
|
onSubmit: jest.fn(),
|
||||||
|
onReset: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
test('should match snapshot, DESKTOP on expanded view', () => {
|
test('should match snapshot, DESKTOP on expanded view', () => {
|
||||||
|
|||||||
@@ -1,41 +1,74 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {ChangeEvent} from 'react';
|
import React, {ChangeEvent, useMemo, useRef} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
import {useSelector} from 'react-redux';
|
import {useSelector} from 'react-redux';
|
||||||
|
|
||||||
|
import ReactSelect, {ValueType} from 'react-select';
|
||||||
|
|
||||||
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
import {ChannelAutoFollowThreads, DesktopSound, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
||||||
|
|
||||||
import SettingItemMax from 'components/setting_item_max';
|
import SettingItemMax from 'components/setting_item_max';
|
||||||
|
|
||||||
|
import {ChannelNotifyProps} from '@mattermost/types/channels';
|
||||||
|
|
||||||
|
import {notificationSounds} from 'utils/notification_sounds';
|
||||||
|
|
||||||
import Describe from './describe';
|
import Describe from './describe';
|
||||||
import ExtraInfo from './extra_info';
|
import ExtraInfo from './extra_info';
|
||||||
import SectionTitle from './section_title';
|
import SectionTitle from './section_title';
|
||||||
|
|
||||||
|
type SelectedOption = {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
ignoreChannelMentions?: string;
|
ignoreChannelMentions?: string;
|
||||||
channelAutoFollowThreads?: string;
|
channelAutoFollowThreads?: string;
|
||||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
onChangeThreads?: (e: ChangeEvent<HTMLInputElement>) => void;
|
onChangeThreads?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
onChangeDesktopSound?: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
onChangeNotificationSound?: (selectedOption: ValueType<SelectedOption>) => void;
|
||||||
onCollapseSection: (section: string) => void;
|
onCollapseSection: (section: string) => void;
|
||||||
|
onReset: () => void;
|
||||||
onSubmit: (setting?: string) => void;
|
onSubmit: (setting?: string) => void;
|
||||||
|
isNotificationsSettingSameAsGlobal?: boolean;
|
||||||
globalNotifyLevel?: string;
|
globalNotifyLevel?: string;
|
||||||
|
globalNotificationSound?: ChannelNotifyProps['desktop_notification_sound'];
|
||||||
memberNotifyLevel: string;
|
memberNotifyLevel: string;
|
||||||
memberThreadsNotifyLevel?: string;
|
memberThreadsNotifyLevel?: string;
|
||||||
|
memberDesktopSound?: string;
|
||||||
|
memberDesktopNotificationSound?: string;
|
||||||
section: string;
|
section: string;
|
||||||
serverError?: string;
|
serverError?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sounds = Array.from(notificationSounds.keys());
|
||||||
|
|
||||||
|
const makeDefaultOptionLabel = (option: string) => `${option} (default)`;
|
||||||
|
|
||||||
|
const makeReactSelectValue = (option: string, isDefault: boolean) => {
|
||||||
|
return {value: option, label: isDefault ? makeDefaultOptionLabel(option) : option};
|
||||||
|
};
|
||||||
|
|
||||||
export default function ExpandView({
|
export default function ExpandView({
|
||||||
section,
|
section,
|
||||||
memberNotifyLevel,
|
memberNotifyLevel,
|
||||||
memberThreadsNotifyLevel,
|
memberThreadsNotifyLevel,
|
||||||
|
memberDesktopSound,
|
||||||
|
memberDesktopNotificationSound,
|
||||||
globalNotifyLevel,
|
globalNotifyLevel,
|
||||||
|
globalNotificationSound,
|
||||||
|
isNotificationsSettingSameAsGlobal,
|
||||||
onChange,
|
onChange,
|
||||||
onChangeThreads,
|
onChangeThreads,
|
||||||
|
onChangeDesktopSound,
|
||||||
|
onChangeNotificationSound,
|
||||||
|
onReset,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
serverError,
|
serverError,
|
||||||
onCollapseSection,
|
onCollapseSection,
|
||||||
@@ -44,32 +77,33 @@ export default function ExpandView({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const isCRTEnabled = useSelector(isCollapsedThreadsEnabled);
|
const isCRTEnabled = useSelector(isCollapsedThreadsEnabled);
|
||||||
|
|
||||||
|
const soundOptions = useMemo(() => sounds.map((sound) => {
|
||||||
|
return {value: sound, label: sound === globalNotificationSound ? makeDefaultOptionLabel(sound) : sound};
|
||||||
|
}), [globalNotificationSound]);
|
||||||
|
|
||||||
|
const dropdownSoundRef = useRef<ReactSelect>(null);
|
||||||
|
|
||||||
const inputs = [(
|
const inputs = [(
|
||||||
<div key='channel-notification-level-radio'>
|
<div key='channel-notification-level-radio'>
|
||||||
{(section === NotificationSections.DESKTOP || section === NotificationSections.PUSH) &&
|
{(section === NotificationSections.DESKTOP || section === NotificationSections.PUSH) &&
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div className='radio'>
|
{ section === NotificationSections.DESKTOP && <legend className='form-legend'>
|
||||||
<label className=''>
|
<FormattedMessage
|
||||||
<input
|
id='channel_notifications.sendDesktop'
|
||||||
id='channelNotificationGlobalDefault'
|
defaultMessage='Send desktop notifications'
|
||||||
name='channelDesktopNotifications'
|
/>
|
||||||
type='radio'
|
</legend>}
|
||||||
value={NotificationLevels.DEFAULT}
|
{ section === NotificationSections.PUSH && <legend className='form-legend'>
|
||||||
checked={memberNotifyLevel === NotificationLevels.DEFAULT}
|
<FormattedMessage
|
||||||
onChange={onChange}
|
id='channel_notifications.sendMobilePush'
|
||||||
/>
|
defaultMessage='Send mobile push notifications'
|
||||||
<Describe
|
/>
|
||||||
section={section}
|
</legend>}
|
||||||
memberNotifyLevel={NotificationLevels.DEFAULT}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className='radio'>
|
<div className='radio'>
|
||||||
<label className=''>
|
<label className=''>
|
||||||
<input
|
<input
|
||||||
id='channelNotificationAllActivity'
|
id='channelNotificationAllActivity'
|
||||||
name='channelDesktopNotifications'
|
name='channelNotifications'
|
||||||
type='radio'
|
type='radio'
|
||||||
value={NotificationLevels.ALL}
|
value={NotificationLevels.ALL}
|
||||||
checked={memberNotifyLevel === NotificationLevels.ALL}
|
checked={memberNotifyLevel === NotificationLevels.ALL}
|
||||||
@@ -78,6 +112,7 @@ export default function ExpandView({
|
|||||||
<Describe
|
<Describe
|
||||||
section={section}
|
section={section}
|
||||||
memberNotifyLevel={NotificationLevels.ALL}
|
memberNotifyLevel={NotificationLevels.ALL}
|
||||||
|
globalNotifyLevel={globalNotifyLevel}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +120,7 @@ export default function ExpandView({
|
|||||||
<label className=''>
|
<label className=''>
|
||||||
<input
|
<input
|
||||||
id='channelNotificationMentions'
|
id='channelNotificationMentions'
|
||||||
name='channelDesktopNotifications'
|
name='channelNotifications'
|
||||||
type='radio'
|
type='radio'
|
||||||
value={NotificationLevels.MENTION}
|
value={NotificationLevels.MENTION}
|
||||||
checked={memberNotifyLevel === NotificationLevels.MENTION}
|
checked={memberNotifyLevel === NotificationLevels.MENTION}
|
||||||
@@ -94,6 +129,7 @@ export default function ExpandView({
|
|||||||
<Describe
|
<Describe
|
||||||
section={section}
|
section={section}
|
||||||
memberNotifyLevel={NotificationLevels.MENTION}
|
memberNotifyLevel={NotificationLevels.MENTION}
|
||||||
|
globalNotifyLevel={globalNotifyLevel}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,7 +137,7 @@ export default function ExpandView({
|
|||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
id='channelNotificationNever'
|
id='channelNotificationNever'
|
||||||
name='channelDesktopNotifications'
|
name='channelNotifications'
|
||||||
type='radio'
|
type='radio'
|
||||||
value={NotificationLevels.NONE}
|
value={NotificationLevels.NONE}
|
||||||
checked={memberNotifyLevel === NotificationLevels.NONE}
|
checked={memberNotifyLevel === NotificationLevels.NONE}
|
||||||
@@ -110,6 +146,7 @@ export default function ExpandView({
|
|||||||
<Describe
|
<Describe
|
||||||
section={section}
|
section={section}
|
||||||
memberNotifyLevel={NotificationLevels.NONE}
|
memberNotifyLevel={NotificationLevels.NONE}
|
||||||
|
globalNotifyLevel={globalNotifyLevel}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,6 +309,72 @@ export default function ExpandView({
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
{(section === NotificationSections.DESKTOP) && memberNotifyLevel !== NotificationLevels.NONE &&
|
||||||
|
|
||||||
|
<>
|
||||||
|
<hr/>
|
||||||
|
<fieldset>
|
||||||
|
<legend className='form-legend'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.sound'
|
||||||
|
defaultMessage='Notification sound'
|
||||||
|
/>
|
||||||
|
</legend>
|
||||||
|
<div className='radio'>
|
||||||
|
<label className=''>
|
||||||
|
<input
|
||||||
|
id='channelDesktopSoundOn'
|
||||||
|
name='channelDesktopSound'
|
||||||
|
type='radio'
|
||||||
|
value={DesktopSound.ON}
|
||||||
|
checked={memberDesktopSound === DesktopSound.ON}
|
||||||
|
onChange={onChangeDesktopSound}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.sound.on.title'
|
||||||
|
defaultMessage='On'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
id='channelDesktopSoundOff'
|
||||||
|
name='channelDesktopSound'
|
||||||
|
type='radio'
|
||||||
|
value={DesktopSound.OFF}
|
||||||
|
checked={memberDesktopSound === DesktopSound.OFF}
|
||||||
|
onChange={onChangeDesktopSound}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.sound.off.title'
|
||||||
|
defaultMessage='Off'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{memberDesktopSound === DesktopSound.ON &&
|
||||||
|
<div className='pt-2'>
|
||||||
|
<ReactSelect
|
||||||
|
className='react-select notification-sound-dropdown'
|
||||||
|
classNamePrefix='react-select'
|
||||||
|
id='channelSoundNotification'
|
||||||
|
options={soundOptions}
|
||||||
|
clearable={false}
|
||||||
|
onChange={onChangeNotificationSound}
|
||||||
|
value={makeReactSelectValue(memberDesktopNotificationSound ?? '', memberDesktopNotificationSound === globalNotificationSound)}
|
||||||
|
isSearchable={false}
|
||||||
|
ref={dropdownSoundRef}
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
|
<div className='mt-5'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.sound_info'
|
||||||
|
defaultMessage='Notification sounds are available on Firefox, Edge, Safari, Chrome and Mattermost Desktop Apps.'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</>
|
||||||
|
}
|
||||||
{isCRTEnabled &&
|
{isCRTEnabled &&
|
||||||
section === NotificationSections.PUSH &&
|
section === NotificationSections.PUSH &&
|
||||||
memberNotifyLevel === NotificationLevels.MENTION &&
|
memberNotifyLevel === NotificationLevels.MENTION &&
|
||||||
@@ -314,7 +417,13 @@ export default function ExpandView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingItemMax
|
<SettingItemMax
|
||||||
title={<SectionTitle section={section}/>}
|
title={
|
||||||
|
<SectionTitle
|
||||||
|
section={section}
|
||||||
|
isExpanded={true}
|
||||||
|
isNotificationsSettingSameAsGlobal={isNotificationsSettingSameAsGlobal}
|
||||||
|
onClickResetButton={onReset}
|
||||||
|
/>}
|
||||||
inputs={inputs}
|
inputs={inputs}
|
||||||
submit={onSubmit}
|
submit={onSubmit}
|
||||||
serverError={serverError}
|
serverError={serverError}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function ExtraInfo({section}: Props) {
|
|||||||
<span>
|
<span>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='channel_notifications.override'
|
id='channel_notifications.override'
|
||||||
defaultMessage='Selecting an option other than "Default" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.'
|
defaultMessage='Selecting an option other than the "default" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.'
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -26,7 +26,7 @@ export default function ExtraInfo({section}: Props) {
|
|||||||
<span>
|
<span>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='channel_notifications.overridePush'
|
id='channel_notifications.overridePush'
|
||||||
defaultMessage='Selecting an option other than "Global default" will override the global notification settings for mobile push notifications in Settings. Push notifications must be enabled by the System Admin.'
|
defaultMessage='Selecting an option other than the "default" will override the global notification settings for mobile push notifications.'
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export default class NotificationSection extends React.PureComponent {
|
|||||||
*/
|
*/
|
||||||
memberNotificationLevel: PropTypes.string.isRequired,
|
memberNotificationLevel: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
memberDesktopSound: PropTypes.string,
|
||||||
|
|
||||||
|
memberDesktopNotificationSound: PropTypes.string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Member's desktop_threads notification level
|
* Member's desktop_threads notification level
|
||||||
*/
|
*/
|
||||||
@@ -47,6 +51,11 @@ export default class NotificationSection extends React.PureComponent {
|
|||||||
*/
|
*/
|
||||||
globalNotificationLevel: PropTypes.string,
|
globalNotificationLevel: PropTypes.string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's global notification sound
|
||||||
|
*/
|
||||||
|
globalNotificationSound: PropTypes.string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onChange handles update of desktop notification level
|
* onChange handles update of desktop notification level
|
||||||
*/
|
*/
|
||||||
@@ -57,6 +66,14 @@ export default class NotificationSection extends React.PureComponent {
|
|||||||
*/
|
*/
|
||||||
onChangeThreads: PropTypes.func,
|
onChangeThreads: PropTypes.func,
|
||||||
|
|
||||||
|
onChangeDesktopSound: PropTypes.func,
|
||||||
|
|
||||||
|
onChangeNotificationSound: PropTypes.func,
|
||||||
|
|
||||||
|
onReset: PropTypes.func,
|
||||||
|
|
||||||
|
isNotificationsSettingSameAsGlobal: PropTypes.bool,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit function to save notification level
|
* Submit function to save notification level
|
||||||
*/
|
*/
|
||||||
@@ -83,6 +100,16 @@ export default class NotificationSection extends React.PureComponent {
|
|||||||
this.props.onChangeThreads(value);
|
this.props.onChangeThreads(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleOnChangeDesktopSound = (e) => {
|
||||||
|
this.props.onChangeDesktopSound(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleOnChangeNotificationSound = (selectedOption) => {
|
||||||
|
if (selectedOption && 'value' in selectedOption) {
|
||||||
|
this.props.onChangeNotificationSound(selectedOption.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleExpandSection = () => {
|
handleExpandSection = () => {
|
||||||
this.props.onUpdateSection(this.props.section);
|
this.props.onUpdateSection(this.props.section);
|
||||||
};
|
};
|
||||||
@@ -95,11 +122,16 @@ export default class NotificationSection extends React.PureComponent {
|
|||||||
const {
|
const {
|
||||||
expand,
|
expand,
|
||||||
globalNotificationLevel,
|
globalNotificationLevel,
|
||||||
|
globalNotificationSound,
|
||||||
memberNotificationLevel,
|
memberNotificationLevel,
|
||||||
memberThreadsNotificationLevel,
|
memberThreadsNotificationLevel,
|
||||||
|
memberDesktopSound,
|
||||||
|
memberDesktopNotificationSound,
|
||||||
ignoreChannelMentions,
|
ignoreChannelMentions,
|
||||||
|
isNotificationsSettingSameAsGlobal,
|
||||||
channelAutoFollowThreads,
|
channelAutoFollowThreads,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
onReset,
|
||||||
section,
|
section,
|
||||||
serverError,
|
serverError,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@@ -110,11 +142,18 @@ export default class NotificationSection extends React.PureComponent {
|
|||||||
section={section}
|
section={section}
|
||||||
memberNotifyLevel={memberNotificationLevel}
|
memberNotifyLevel={memberNotificationLevel}
|
||||||
memberThreadsNotifyLevel={memberThreadsNotificationLevel}
|
memberThreadsNotifyLevel={memberThreadsNotificationLevel}
|
||||||
|
memberDesktopSound={memberDesktopSound}
|
||||||
|
memberDesktopNotificationSound={memberDesktopNotificationSound}
|
||||||
globalNotifyLevel={globalNotificationLevel}
|
globalNotifyLevel={globalNotificationLevel}
|
||||||
|
globalNotificationSound={globalNotificationSound}
|
||||||
ignoreChannelMentions={ignoreChannelMentions}
|
ignoreChannelMentions={ignoreChannelMentions}
|
||||||
|
isNotificationsSettingSameAsGlobal={isNotificationsSettingSameAsGlobal}
|
||||||
channelAutoFollowThreads={channelAutoFollowThreads}
|
channelAutoFollowThreads={channelAutoFollowThreads}
|
||||||
onChange={this.handleOnChange}
|
onChange={this.handleOnChange}
|
||||||
|
onReset={onReset}
|
||||||
onChangeThreads={this.handleOnChangeThreads}
|
onChangeThreads={this.handleOnChangeThreads}
|
||||||
|
onChangeDesktopSound={this.handleOnChangeDesktopSound}
|
||||||
|
onChangeNotificationSound={this.handleOnChangeNotificationSound}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
serverError={serverError}
|
serverError={serverError}
|
||||||
onCollapseSection={this.handleCollapseSection}
|
onCollapseSection={this.handleCollapseSection}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe('components/channel_notifications_modal/NotificationSection', () => {
|
|||||||
globalNotificationLevel: NotificationLevels.DEFAULT,
|
globalNotificationLevel: NotificationLevels.DEFAULT,
|
||||||
onChange: () => {}, //eslint-disable-line no-empty-function
|
onChange: () => {}, //eslint-disable-line no-empty-function
|
||||||
onChangeThreads: () => {}, //eslint-disable-line no-empty-function
|
onChangeThreads: () => {}, //eslint-disable-line no-empty-function
|
||||||
|
onReset: () => {},
|
||||||
onSubmit: () => {}, //eslint-disable-line no-empty-function
|
onSubmit: () => {}, //eslint-disable-line no-empty-function
|
||||||
onUpdateSection: () => {}, //eslint-disable-line no-empty-function
|
onUpdateSection: () => {}, //eslint-disable-line no-empty-function
|
||||||
serverError: '',
|
serverError: '',
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@import 'sass/utils/mixins';
|
||||||
|
|
||||||
|
.SectionTitle {
|
||||||
|
&__wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__resetButton {
|
||||||
|
@include button-style--none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,24 +6,43 @@ import {FormattedMessage} from 'react-intl';
|
|||||||
|
|
||||||
import {NotificationSections} from 'utils/constants';
|
import {NotificationSections} from 'utils/constants';
|
||||||
|
|
||||||
|
import './section_title.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
section: string;
|
section: string;
|
||||||
|
isExpanded?: boolean;
|
||||||
|
isNotificationsSettingSameAsGlobal?: boolean;
|
||||||
|
onClickResetButton?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SectionTitle({section}: Props) {
|
export default function SectionTitle({section, isExpanded, isNotificationsSettingSameAsGlobal, onClickResetButton}: Props) {
|
||||||
if (section === NotificationSections.DESKTOP) {
|
if (section === NotificationSections.DESKTOP || section === NotificationSections.PUSH) {
|
||||||
return (
|
return (
|
||||||
<FormattedMessage
|
<div className='SectionTitle__wrapper'>
|
||||||
id='channel_notifications.sendDesktop'
|
{section === NotificationSections.DESKTOP &&
|
||||||
defaultMessage='Send desktop notifications'
|
<FormattedMessage
|
||||||
/>
|
id='channel_notifications.desktopNotifications'
|
||||||
);
|
defaultMessage='Desktop notifications'
|
||||||
} else if (section === NotificationSections.PUSH) {
|
/>}
|
||||||
return (
|
|
||||||
<FormattedMessage
|
{section === NotificationSections.PUSH &&
|
||||||
id='channel_notifications.push'
|
<FormattedMessage
|
||||||
defaultMessage='Send mobile push notifications'
|
id='channel_notifications.push'
|
||||||
/>
|
defaultMessage='Mobile push notifications'
|
||||||
|
/>}
|
||||||
|
{isExpanded && !isNotificationsSettingSameAsGlobal &&
|
||||||
|
<button
|
||||||
|
className='SectionTitle__resetButton color--link'
|
||||||
|
onClick={onClickResetButton}
|
||||||
|
>
|
||||||
|
<i className='icon icon-refresh'/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.resetToDefaults'
|
||||||
|
defaultMessage='Reset to defaults'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
} else if (section === NotificationSections.MARK_UNREAD) {
|
} else if (section === NotificationSections.MARK_UNREAD) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -31,11 +31,17 @@ jest.mock('actions/global_actions', () => ({
|
|||||||
redirectUserToDefaultTeam: jest.fn(),
|
redirectUserToDefaultTeam: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('utils/utils', () => ({
|
jest.mock('utils/utils', () => {
|
||||||
localizeMessage: () => {},
|
const original = jest.requireActual('utils/utils');
|
||||||
applyTheme: jest.fn(),
|
|
||||||
makeIsEligibleForClick: jest.fn(),
|
return {
|
||||||
}));
|
...original,
|
||||||
|
localizeMessage: () => {},
|
||||||
|
applyTheme: jest.fn(),
|
||||||
|
makeIsEligibleForClick: jest.fn(),
|
||||||
|
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('mattermost-redux/actions/general', () => ({
|
jest.mock('mattermost-redux/actions/general', () => ({
|
||||||
setUrl: () => {},
|
setUrl: () => {},
|
||||||
|
|||||||
@@ -2995,6 +2995,8 @@
|
|||||||
"channel_notifications.channelAutoFollowThreads.help": "When enabled, you will auto-follow all new threads created in this channel unless you unfollow a thread explicitly.",
|
"channel_notifications.channelAutoFollowThreads.help": "When enabled, you will auto-follow all new threads created in this channel unless you unfollow a thread explicitly.",
|
||||||
"channel_notifications.channelAutoFollowThreads.off.title": "Off",
|
"channel_notifications.channelAutoFollowThreads.off.title": "Off",
|
||||||
"channel_notifications.channelAutoFollowThreads.on.title": "On",
|
"channel_notifications.channelAutoFollowThreads.on.title": "On",
|
||||||
|
"channel_notifications.defaultOption": "(default)",
|
||||||
|
"channel_notifications.desktopNotifications": "Desktop notifications",
|
||||||
"channel_notifications.globalDefault": "Global default ({notifyLevel})",
|
"channel_notifications.globalDefault": "Global default ({notifyLevel})",
|
||||||
"channel_notifications.ignoreChannelMentions": "Ignore mentions for @channel, @here and @all",
|
"channel_notifications.ignoreChannelMentions": "Ignore mentions for @channel, @here and @all",
|
||||||
"channel_notifications.ignoreChannelMentions.help": "When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel.",
|
"channel_notifications.ignoreChannelMentions.help": "When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel.",
|
||||||
@@ -3009,13 +3011,19 @@
|
|||||||
"channel_notifications.muteChannel.on.title": "On",
|
"channel_notifications.muteChannel.on.title": "On",
|
||||||
"channel_notifications.muteChannel.on.title.collapse": "Mute is enabled. Desktop, email and push notifications will not be sent for this channel.",
|
"channel_notifications.muteChannel.on.title.collapse": "Mute is enabled. Desktop, email and push notifications will not be sent for this channel.",
|
||||||
"channel_notifications.muteChannel.settings": "Mute channel",
|
"channel_notifications.muteChannel.settings": "Mute channel",
|
||||||
"channel_notifications.never": "Never",
|
"channel_notifications.never": "Never {isDefault}",
|
||||||
"channel_notifications.onlyMentions": "Only for mentions",
|
"channel_notifications.onlyMentions": "Only for mentions {isDefault}",
|
||||||
"channel_notifications.override": "Selecting an option other than \"Default\" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.",
|
"channel_notifications.override": "Selecting an option other than the \"default\" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.",
|
||||||
"channel_notifications.overridePush": "Selecting an option other than \"Global default\" will override the global notification settings for mobile push notifications in settings. Push notifications must be enabled by the System Admin.",
|
"channel_notifications.overridePush": "Selecting an option other than the \"default\" will override the global notification settings for mobile push notifications. ",
|
||||||
"channel_notifications.preferences": "Notification Preferences for ",
|
"channel_notifications.preferences": "Notification Preferences for ",
|
||||||
"channel_notifications.push": "Send mobile push notifications",
|
"channel_notifications.push": "Mobile push notifications",
|
||||||
"channel_notifications.sendDesktop": "Send desktop notifications",
|
"channel_notifications.resetToDefaults": "Reset to defaults",
|
||||||
|
"channel_notifications.sendDesktop": "Send Desktop notifications",
|
||||||
|
"channel_notifications.sendMobilePush": "Send mobile push notifications",
|
||||||
|
"channel_notifications.sound": "Notification sound",
|
||||||
|
"channel_notifications.sound_info": "Notification sounds are available on Firefox, Edge, Safari, Chrome and Mattermost Desktop Apps.",
|
||||||
|
"channel_notifications.sound.off.title": "Off",
|
||||||
|
"channel_notifications.sound.on.title": "On",
|
||||||
"channel_select.placeholder": "--- Select a channel ---",
|
"channel_select.placeholder": "--- Select a channel ---",
|
||||||
"channel_switch_modal.deactivated": "Deactivated",
|
"channel_switch_modal.deactivated": "Deactivated",
|
||||||
"channel_toggle_button.private": "Private",
|
"channel_toggle_button.private": "Private",
|
||||||
|
|||||||
@@ -449,6 +449,7 @@ class TestHelper {
|
|||||||
fakeChannelNotifyProps = (override: Partial<ChannelNotifyProps>): ChannelNotifyProps => {
|
fakeChannelNotifyProps = (override: Partial<ChannelNotifyProps>): ChannelNotifyProps => {
|
||||||
return {
|
return {
|
||||||
desktop: 'default',
|
desktop: 'default',
|
||||||
|
desktop_sound: 'off',
|
||||||
email: 'default',
|
email: 'default',
|
||||||
mark_unread: 'mention',
|
mark_unread: 'mention',
|
||||||
push: 'default',
|
push: 'default',
|
||||||
|
|||||||
@@ -998,6 +998,11 @@ export const NotificationLevels = {
|
|||||||
NONE: 'none',
|
NONE: 'none',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const DesktopSound = {
|
||||||
|
ON: 'on',
|
||||||
|
OFF: 'off',
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const IgnoreChannelMentions = {
|
export const IgnoreChannelMentions = {
|
||||||
ON: 'on',
|
ON: 'on',
|
||||||
OFF: 'off',
|
OFF: 'off',
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ export type ChannelStats = {
|
|||||||
|
|
||||||
export type ChannelNotifyProps = {
|
export type ChannelNotifyProps = {
|
||||||
desktop: 'default' | 'all' | 'mention' | 'none';
|
desktop: 'default' | 'all' | 'mention' | 'none';
|
||||||
|
desktop_sound: 'on' | 'off';
|
||||||
|
desktop_notification_sound?: 'Bing' | 'Crackle' | 'Down' | 'Hello' | 'Ripple' | 'Upstairs';
|
||||||
email: 'default' | 'all' | 'mention' | 'none';
|
email: 'default' | 'all' | 'mention' | 'none';
|
||||||
mark_unread: 'all' | 'mention';
|
mark_unread: 'all' | 'mention';
|
||||||
push: 'default' | 'all' | 'mention' | 'none';
|
push: 'default' | 'all' | 'mention' | 'none';
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user