mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-40384]: Channel notification settings modal UX revamp (#24984)
* Move notification preferences modal to new UI * fix lint issue * fix i18n * fix unit test * fix type issue and lint errors * fix test case * move common components to widget modals dir * fix css issue * feedback changes * fix lint and i18n issues * more feedback changes * fix issue with mobile notification ui * fix test * clean up * remove name * fix test --------- Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
ec88ab4ee9
commit
448d442a0b
@ -211,7 +211,7 @@ describe('Channel Info RHS', () => {
|
|||||||
cy.uiGetRHS().findByText('Notification Preferences').should('be.visible').click();
|
cy.uiGetRHS().findByText('Notification Preferences').should('be.visible').click();
|
||||||
|
|
||||||
// * Ensures the modal is there
|
// * Ensures the modal is there
|
||||||
cy.get('.settings-modal').should('be.visible');
|
cy.get('.channel-notifications-settings-modal').should('be.visible');
|
||||||
});
|
});
|
||||||
it('should be able to view files and come back', () => {
|
it('should be able to view files and come back', () => {
|
||||||
// # Go to test channel
|
// # Go to test channel
|
||||||
@ -373,7 +373,7 @@ describe('Channel Info RHS', () => {
|
|||||||
cy.uiGetRHS().findByText('Notification Preferences').should('be.visible').click();
|
cy.uiGetRHS().findByText('Notification Preferences').should('be.visible').click();
|
||||||
|
|
||||||
// * Ensures the modal is there
|
// * Ensures the modal is there
|
||||||
cy.get('.settings-modal').should('be.visible');
|
cy.get('.channel-notifications-settings-modal').should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -52,11 +52,46 @@ describe('CRT Desktop notifications', () => {
|
|||||||
// # Visit channel
|
// # Visit channel
|
||||||
cy.visit(testChannelUrl);
|
cy.visit(testChannelUrl);
|
||||||
|
|
||||||
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
|
cy.get('[data-testid="muteChannel"]').click().then(() => {
|
||||||
|
cy.get('.AlertBanner--app').should('be.visible');
|
||||||
|
});
|
||||||
|
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
|
||||||
|
|
||||||
// Setup notification spy
|
// Setup notification spy
|
||||||
spyNotificationAs('notifySpy', 'granted');
|
spyNotificationAs('notifySpy', 'granted');
|
||||||
|
|
||||||
// # Set users notification settings
|
// # Set users notification settings
|
||||||
setCRTDesktopNotification('ALL');
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
|
|
||||||
|
// # click on Mute Channel to Unmute Channel
|
||||||
|
cy.get('[data-testid="muteChannel"]').click();
|
||||||
|
|
||||||
|
// # Click "Desktop Notifications"
|
||||||
|
cy.findByText('Desktop Notifications').should('be.visible');
|
||||||
|
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-all').should('be.visible').click();
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-all').should('be.checked');
|
||||||
|
|
||||||
|
cy.get('#desktopNotification-mention').should('be.visible').click().then(() => {
|
||||||
|
cy.get('[data-testid="desktopReplyThreads"]').should('be.checked');
|
||||||
|
cy.get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
|
||||||
|
cy.get('[data-testid="desktopReplyThreads"]').should('not.be.checked');
|
||||||
|
});
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.checked');
|
||||||
|
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.visible').click();
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-none').should('be.checked');
|
||||||
|
|
||||||
|
// # click on Save button
|
||||||
|
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
|
||||||
|
|
||||||
|
// # Set users notification settings
|
||||||
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.checked');
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-all').scrollIntoView().should('be.visible').click();
|
||||||
|
|
||||||
|
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
|
||||||
|
|
||||||
// # Post a root message as other user
|
// # Post a root message as other user
|
||||||
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
|
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
|
||||||
@ -99,14 +134,41 @@ describe('CRT Desktop notifications', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MM-T4417_2 Trigger notifications only on mention replies when channel setting is unchecked', () => {
|
it('MM-T4417_2 Click on sameMobileSettingsDesktop and check if additional settings still appears', () => {
|
||||||
|
cy.visit(testChannelUrl);
|
||||||
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click().then(() => {
|
||||||
|
cy.get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
|
||||||
|
});
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').get('[data-testid="sameMobileSettingsDesktop"]').scrollIntoView().click().should('be.checked').then(() => {
|
||||||
|
cy.findByText('Notify me about…').should('not.be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
// check the box to see if the additional settings appears
|
||||||
|
cy.get('.channel-notifications-settings-modal__body').get('[data-testid="sameMobileSettingsDesktop"]').scrollIntoView().click();
|
||||||
|
cy.get('.mm-modal-generic-section-item__title').should('be.visible').and('contain', 'Notify me about');
|
||||||
|
|
||||||
|
cy.get('#MobileNotification-all').should('be.visible').click();
|
||||||
|
cy.get('#MobileNotification-mention').should('be.visible').click().then(() => {
|
||||||
|
cy.get('[data-testid="mobileReplyThreads"]').should('be.visible').click();
|
||||||
|
});
|
||||||
|
cy.get('#MobileNotification-none').should('be.visible').click();
|
||||||
|
|
||||||
|
cy.get('[data-testid="autoFollowThreads"]').should('be.visible').click();
|
||||||
|
|
||||||
|
// # click on Save button
|
||||||
|
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('MM-T4417_3 Trigger notifications only on mention replies when channel setting is unchecked', () => {
|
||||||
cy.visit(testChannelUrl);
|
cy.visit(testChannelUrl);
|
||||||
|
|
||||||
// Setup notification spy
|
// Setup notification spy
|
||||||
spyNotificationAs('notifySpy', 'granted');
|
spyNotificationAs('notifySpy', 'granted');
|
||||||
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
// # Set users notification settings
|
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click();
|
||||||
setCRTDesktopNotification('MENTION');
|
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
|
||||||
|
|
||||||
// # Post a root message as other user
|
// # Post a root message as other user
|
||||||
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
|
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
|
||||||
@ -232,32 +294,3 @@ describe('CRT Desktop notifications', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setCRTDesktopNotification(type) {
|
|
||||||
if (['ALL', 'MENTION'].indexOf(type) === -1) {
|
|
||||||
throw new Error(`${type} is invalid`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Open settings modal
|
|
||||||
cy.uiOpenChannelMenu('Notification Preferences');
|
|
||||||
|
|
||||||
// # Click "Desktop Notifications"
|
|
||||||
cy.get('#desktopTitle').
|
|
||||||
scrollIntoView().
|
|
||||||
should('be.visible').
|
|
||||||
and('contain', 'Desktop notifications').click();
|
|
||||||
|
|
||||||
// # Select mentions category for messages.
|
|
||||||
cy.get('#channelNotificationMentions').scrollIntoView().check();
|
|
||||||
|
|
||||||
if (type === 'ALL') {
|
|
||||||
// # Check notify for all replies.
|
|
||||||
cy.get('#desktopThreadsNotificationAllActivity').scrollIntoView().check().should('be.checked');
|
|
||||||
} else if (type === 'MENTION') {
|
|
||||||
// # Check notify only for mentions.
|
|
||||||
cy.get('#desktopThreadsNotificationAllActivity').scrollIntoView().uncheck().should('not.be.checked');
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Click "Save" and close the modal
|
|
||||||
cy.uiSaveAndClose();
|
|
||||||
}
|
|
||||||
|
@ -53,9 +53,9 @@ 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('Desktop notifications').click();
|
cy.findByText('Desktop Notifications').should('be.visible');
|
||||||
cy.findByRole('radio', {name: 'Only for mentions'}).click();
|
cy.findByRole('radio', {name: 'Mentions, direct messages, and keywords only'}).click().should('be.checked');
|
||||||
cy.uiSaveAndClose();
|
cy.uiSave();
|
||||||
|
|
||||||
// # Visit off-topic
|
// # Visit off-topic
|
||||||
cy.uiClickSidebarItem('off-topic');
|
cy.uiClickSidebarItem('off-topic');
|
||||||
|
@ -109,26 +109,15 @@ function addNumberOfUsersToChannel(num = 1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setIgnoreMentions(toSet) {
|
function setIgnoreMentions(toSet) {
|
||||||
let stringToSet = 'Off';
|
|
||||||
if (toSet) {
|
|
||||||
stringToSet = 'On';
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Open channel menu and click Notification Preferences
|
// # Open channel menu and click Notification Preferences
|
||||||
cy.uiOpenChannelMenu('Notification Preferences');
|
cy.uiOpenChannelMenu('Notification Preferences');
|
||||||
|
|
||||||
// # Click on the edit button for ignore channel mentions
|
// # find mute or ignore section
|
||||||
cy.get('#ignoreChannelMentionsEdit').should('exist').click();
|
cy.findByText('Mute or ignore').should('be.visible');
|
||||||
|
|
||||||
// # Click on selected option
|
// # find Ignore mentions checkbox, set value accordingly
|
||||||
cy.get(`#ignoreChannelMentions${stringToSet}`).should('exist').click();
|
cy.findByRole('checkbox', {name: 'Ignore mentions for @channel, @here and @all'}).click().should(toSet ? 'be.checked' : 'not.be.checked');
|
||||||
|
|
||||||
// # Click on save to save the configuration
|
// # Click on save to save the configuration
|
||||||
cy.get('#saveSetting').should('exist').click();
|
cy.uiSave();
|
||||||
|
|
||||||
// * Assert that the option selected is reflected
|
|
||||||
cy.get('#ignoreChannelMentionsDesc').should('contain', stringToSet);
|
|
||||||
|
|
||||||
// # Click on the X button to close the modal
|
|
||||||
cy.get('#channelNotificationModalLabel').siblings('.close').click();
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ export type AlertBannerProps = {
|
|||||||
id?: string;
|
id?: string;
|
||||||
mode: ModeType;
|
mode: ModeType;
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
|
customIcon?: React.ReactNode;
|
||||||
message?: React.ReactNode;
|
message?: React.ReactNode;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -41,6 +42,7 @@ const AlertBanner = ({
|
|||||||
id,
|
id,
|
||||||
mode,
|
mode,
|
||||||
title,
|
title,
|
||||||
|
customIcon,
|
||||||
message,
|
message,
|
||||||
className,
|
className,
|
||||||
variant = 'sys',
|
variant = 'sys',
|
||||||
@ -57,6 +59,9 @@ const AlertBanner = ({
|
|||||||
const [tooltipId] = useState(`alert_banner_close_btn_tooltip_${Math.random()}`);
|
const [tooltipId] = useState(`alert_banner_close_btn_tooltip_${Math.random()}`);
|
||||||
|
|
||||||
const bannerIcon = useCallback(() => {
|
const bannerIcon = useCallback(() => {
|
||||||
|
if (customIcon) {
|
||||||
|
return customIcon;
|
||||||
|
}
|
||||||
if (mode === 'danger' || mode === 'warning') {
|
if (mode === 'danger' || mode === 'warning') {
|
||||||
return (
|
return (
|
||||||
<AlertOutlineIcon
|
<AlertOutlineIcon
|
||||||
@ -72,7 +77,7 @@ const AlertBanner = ({
|
|||||||
<InformationOutlineIcon
|
<InformationOutlineIcon
|
||||||
size={24}
|
size={24}
|
||||||
/>);
|
/>);
|
||||||
}, [mode]);
|
}, [mode, customIcon]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,139 @@
|
|||||||
|
.modal .channel-notifications-settings-modal {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin-top: -24px;
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: calc(100vh - 240px);
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid rgba(var(--center-channel-color-rgb), 0.16);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: var(--elevation-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__ctr {
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1024px;
|
||||||
|
min-height: 150px;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 32px;
|
||||||
|
gap: 24px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1024px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 24px 32px;
|
||||||
|
border-top: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__reset-btn {
|
||||||
|
display: flex;
|
||||||
|
padding: 5px 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--button-bg);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
gap: 4px;
|
||||||
|
line-height: 9.5px;
|
||||||
|
place-items: center;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__server-error {
|
||||||
|
flex-grow: 1;
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__save-btn {
|
||||||
|
padding: 12px 20px 12px 20px;
|
||||||
|
border: none;
|
||||||
|
background: var(--button-bg);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--button-color);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 14px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.12);
|
||||||
|
background: linear-gradient(0deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08)), var(--button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 18px;
|
||||||
|
border: 2px solid var(--sidebar-text-active-border);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cancel-btn {
|
||||||
|
padding: 12px 20px 12px 20px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.08);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--button-bg);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 14px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
background: rgba(var(--button-bg-rgb), 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__divider {
|
||||||
|
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
max-height: unset;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 900px) and (min-width: 768px) {
|
||||||
|
.modal-content {
|
||||||
|
max-height: calc(100vh - 160px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,17 @@
|
|||||||
// 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 {shallow} from 'enzyme';
|
import {screen, fireEvent, waitFor} from '@testing-library/react';
|
||||||
import React from 'react';
|
|
||||||
import type {ComponentProps} from 'react';
|
import type {ComponentProps} from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import type {ChannelMembership, ChannelNotifyProps} from '@mattermost/types/channels';
|
import type {ChannelMembership} from '@mattermost/types/channels';
|
||||||
import type {UserNotifyProps} from '@mattermost/types/users';
|
import type {UserNotifyProps} from '@mattermost/types/users';
|
||||||
|
|
||||||
import ChannelNotificationsModal from 'components/channel_notifications_modal/channel_notifications_modal';
|
import ChannelNotificationsModal from 'components/channel_notifications_modal/channel_notifications_modal';
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, DesktopSound, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
import {renderWithContext} from 'tests/react_testing_utils';
|
||||||
|
import {IgnoreChannelMentions, NotificationLevels} from 'utils/constants';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
import {TestHelper} from 'utils/test_helper';
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/ChannelNotificationsModal', () => {
|
describe('components/channel_notifications_modal/ChannelNotificationsModal', () => {
|
||||||
@ -23,12 +24,9 @@ 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,
|
||||||
channel_auto_follow_threads: ChannelAutoFollowThreads.OFF,
|
|
||||||
desktop_threads: NotificationLevels.ALL,
|
desktop_threads: NotificationLevels.ALL,
|
||||||
push_threads: NotificationLevels.DEFAULT,
|
push_threads: NotificationLevels.DEFAULT,
|
||||||
},
|
},
|
||||||
@ -42,359 +40,209 @@ describe('components/channel_notifications_modal/ChannelNotificationsModal', ()
|
|||||||
}),
|
}),
|
||||||
sendPushNotifications: true,
|
sendPushNotifications: true,
|
||||||
actions: {
|
actions: {
|
||||||
updateChannelNotifyProps: jest.fn(),
|
updateChannelNotifyProps: jest.fn().mockImplementation(() => Promise.resolve({data: true})),
|
||||||
},
|
},
|
||||||
|
collapsedReplyThreads: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
test('should match snapshot', () => {
|
it('should not show other settings if channel is mute', async () => {
|
||||||
const wrapper = shallow(
|
const wrapper = renderWithContext(
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
<ChannelNotificationsModal {...baseProps}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const muteChannel = screen.getByTestId('muteChannel');
|
||||||
|
|
||||||
|
fireEvent.click(muteChannel);
|
||||||
|
expect(muteChannel).toBeChecked();
|
||||||
|
const AlertBanner = screen.queryByText('This channel is muted');
|
||||||
|
expect(AlertBanner).toBeVisible();
|
||||||
|
|
||||||
|
expect(screen.queryByText('Desktop Notifications')).toBeNull();
|
||||||
|
|
||||||
|
expect(screen.queryByText('Mobile Notifications')).toBeNull();
|
||||||
|
expect(screen.queryByText('Follow all threads in this channel')).toBeNull();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
|
||||||
|
'current_user_id',
|
||||||
|
'channel_id',
|
||||||
|
{
|
||||||
|
desktop: baseProps.channelMember?.notify_props.desktop,
|
||||||
|
ignore_channel_mentions: 'off',
|
||||||
|
mark_unread: 'mention',
|
||||||
|
push: 'all',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match snapshot for GMs', () => {
|
test('should Ignore mentions for @channel, @here and @all', async () => {
|
||||||
const wrapper = shallow(
|
const wrapper = renderWithContext(
|
||||||
<ChannelNotificationsModal
|
<ChannelNotificationsModal {...baseProps}/>,
|
||||||
{...{
|
|
||||||
...baseProps,
|
|
||||||
channel: TestHelper.getChannelMock({
|
|
||||||
id: 'channel_id',
|
|
||||||
display_name: 'channel_display_name',
|
|
||||||
type: 'G',
|
|
||||||
}),
|
|
||||||
channelMember: {
|
|
||||||
notify_props: {
|
|
||||||
...baseProps.channelMember!.notify_props,
|
|
||||||
desktop: NotificationLevels.MENTION,
|
|
||||||
push: NotificationLevels.MENTION,
|
|
||||||
},
|
|
||||||
} as unknown as ChannelMembership,
|
|
||||||
currentUser: TestHelper.getUserMock({
|
|
||||||
id: 'current_user_id',
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.MENTION,
|
|
||||||
desktop_threads: NotificationLevels.ALL,
|
|
||||||
} as UserNotifyProps,
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
);
|
);
|
||||||
|
const ignoreChannel = screen.getByTestId('ignoreMentions');
|
||||||
|
fireEvent.click(ignoreChannel);
|
||||||
|
expect(ignoreChannel).toBeChecked();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
|
||||||
|
'current_user_id',
|
||||||
|
'channel_id',
|
||||||
|
{
|
||||||
|
desktop: 'all',
|
||||||
|
ignore_channel_mentions: 'on',
|
||||||
|
mark_unread:
|
||||||
|
baseProps.channelMember?.notify_props.mark_unread,
|
||||||
|
push: 'all',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should provide default notify props when missing', () => {
|
test('should check the options in the desktop notifications', async () => {
|
||||||
const wrapper = shallow(
|
const wrapper = renderWithContext(
|
||||||
<ChannelNotificationsModal
|
|
||||||
{...baseProps}
|
|
||||||
channelMember={{notify_props: {}} as ChannelMembership}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
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('pushNotifyLevel')).toEqual(NotificationLevels.ALL);
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.OFF);
|
|
||||||
expect(wrapper.state('channelAutoFollowThreads')).toEqual(ChannelAutoFollowThreads.OFF);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should provide correct default when currentUser channel notify props is true', () => {
|
|
||||||
const currentUser = TestHelper.getUserMock({
|
|
||||||
id: 'current_user_id',
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.ALL,
|
|
||||||
desktop_threads: NotificationLevels.ALL,
|
|
||||||
channel: 'true',
|
|
||||||
} as UserNotifyProps,
|
|
||||||
});
|
|
||||||
const props = {...baseProps, currentUser};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.OFF);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should provide correct default when currentUser channel notify props is false', () => {
|
|
||||||
const currentUser = TestHelper.getUserMock({
|
|
||||||
id: 'current_user_id',
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.ALL,
|
|
||||||
desktop_threads: NotificationLevels.ALL,
|
|
||||||
channel: 'false',
|
|
||||||
} as UserNotifyProps,
|
|
||||||
});
|
|
||||||
const props = {...baseProps, currentUser};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.ON);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should provide correct value for ignoreChannelMentions when channelMember channel-wide mentions are off and false on the currentUser', () => {
|
|
||||||
const currentUser = TestHelper.getUserMock({
|
|
||||||
id: 'current_user_id',
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.ALL,
|
|
||||||
desktop_threads: NotificationLevels.ALL,
|
|
||||||
channel: 'false',
|
|
||||||
} as UserNotifyProps,
|
|
||||||
});
|
|
||||||
const channelMember = TestHelper.getChannelMembershipMock({
|
|
||||||
notify_props: {
|
|
||||||
ignore_channel_mentions: IgnoreChannelMentions.OFF,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const props = {...baseProps, channelMember, currentUser};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.OFF);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should provide correct value for ignoreChannelMentions when channelMember channel-wide mentions are on but false on currentUser', () => {
|
|
||||||
const currentUser = TestHelper.getUserMock({
|
|
||||||
id: 'current_user_id',
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.ALL,
|
|
||||||
channel: 'true',
|
|
||||||
} as UserNotifyProps,
|
|
||||||
});
|
|
||||||
const channelMember = TestHelper.getChannelMembershipMock({
|
|
||||||
notify_props: {
|
|
||||||
ignore_channel_mentions: IgnoreChannelMentions.ON,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const props = {...baseProps, channelMember, currentUser};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.ON);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should provide correct value for ignoreChannelMentions when channel is muted', () => {
|
|
||||||
const currentUser = TestHelper.getUserMock({
|
|
||||||
id: 'current_user_id',
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.ALL,
|
|
||||||
channel: 'true',
|
|
||||||
} as UserNotifyProps,
|
|
||||||
});
|
|
||||||
const channelMember = TestHelper.getChannelMembershipMock({
|
|
||||||
notify_props: {
|
|
||||||
mark_unread: NotificationLevels.MENTION,
|
|
||||||
ignore_channel_mentions: IgnoreChannelMentions.DEFAULT,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const props = {...baseProps, channelMember, currentUser};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.ON);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should call onExited and match state on handleOnHide', () => {
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
<ChannelNotificationsModal {...baseProps}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.setState({activeSection: NotificationSections.DESKTOP, desktopNotifyLevel: NotificationLevels.NONE});
|
expect(screen.queryByText('Desktop Notifications')).toBeVisible();
|
||||||
wrapper.instance().handleExit();
|
|
||||||
expect(baseProps.onExited).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
|
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.ALL);
|
|
||||||
|
|
||||||
wrapper.setState({activeSection: NotificationSections.MARK_UNREAD, markUnreadNotifyLevel: NotificationLevels.MENTION});
|
const AlllabelRadio: HTMLInputElement = screen.getByTestId(
|
||||||
wrapper.instance().handleExit();
|
'desktopNotification-all',
|
||||||
expect(baseProps.onExited).toHaveBeenCalledTimes(2);
|
);
|
||||||
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
|
fireEvent.click(AlllabelRadio);
|
||||||
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.ALL);
|
expect(AlllabelRadio.checked).toEqual(true);
|
||||||
|
|
||||||
wrapper.setState({activeSection: NotificationSections.PUSH, pushNotifyLevel: NotificationLevels.NONE});
|
const MentionslabelRadio: HTMLInputElement = screen.getByTestId(
|
||||||
wrapper.instance().handleExit();
|
'desktopNotification-mention',
|
||||||
expect(baseProps.onExited).toHaveBeenCalledTimes(3);
|
);
|
||||||
expect(wrapper.state('activeSection')).toEqual(NotificationSections.NONE);
|
fireEvent.click(MentionslabelRadio);
|
||||||
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.ALL);
|
expect(MentionslabelRadio.checked).toEqual(true);
|
||||||
|
|
||||||
|
const NothinglabelRadio: HTMLInputElement = screen.getByTestId(
|
||||||
|
'desktopNotification-none',
|
||||||
|
);
|
||||||
|
fireEvent.click(NothinglabelRadio);
|
||||||
|
expect(NothinglabelRadio.checked).toEqual(true);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
|
||||||
|
'current_user_id',
|
||||||
|
'channel_id',
|
||||||
|
{
|
||||||
|
desktop: 'none',
|
||||||
|
ignore_channel_mentions: 'off',
|
||||||
|
mark_unread: 'all',
|
||||||
|
push: 'all',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match state on updateSection', () => {
|
test('should save the options exactly same as Desktop for mobile if use same as desktop checkbox is checked', async () => {
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
const wrapper = renderWithContext(
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
<ChannelNotificationsModal {...baseProps}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.setState({activeSection: NotificationSections.NONE});
|
expect(screen.queryByText('Desktop Notifications')).toBeVisible();
|
||||||
wrapper.instance().updateSection(NotificationSections.DESKTOP);
|
|
||||||
expect(wrapper.state('activeSection')).toEqual(NotificationSections.DESKTOP);
|
const sameAsDesktop: HTMLInputElement = screen.getByTestId(
|
||||||
|
'sameMobileSettingsDesktop',
|
||||||
|
);
|
||||||
|
fireEvent.click(sameAsDesktop);
|
||||||
|
expect(sameAsDesktop.checked).toEqual(true);
|
||||||
|
|
||||||
|
expect(screen.queryByText('All new messages')).toBeNull();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
|
||||||
|
'current_user_id',
|
||||||
|
'channel_id',
|
||||||
|
{
|
||||||
|
desktop: 'all',
|
||||||
|
ignore_channel_mentions: 'off',
|
||||||
|
mark_unread: 'all',
|
||||||
|
push: 'all',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should reset state when collapsing a section', () => {
|
test('should check the options in the mobile notifications', async () => {
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
const wrapper = renderWithContext(
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
<ChannelNotificationsModal {...baseProps}/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.instance().updateSection(NotificationSections.DESKTOP);
|
const AlllabelRadio: HTMLInputElement = screen.getByTestId(
|
||||||
wrapper.instance().handleUpdateDesktopNotifyLevel(NotificationLevels.NONE);
|
'MobileNotification-all',
|
||||||
|
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.NONE);
|
|
||||||
|
|
||||||
wrapper.instance().updateSection(NotificationSections.NONE);
|
|
||||||
|
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(baseProps.channelMember?.notify_props.desktop);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match state on handleSubmitDesktopNotification', () => {
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
|
||||||
);
|
);
|
||||||
|
fireEvent.click(AlllabelRadio);
|
||||||
|
expect(AlllabelRadio.checked).toEqual(true);
|
||||||
|
|
||||||
const instance = wrapper.instance();
|
const MentionslabelRadio: HTMLInputElement = screen.getByTestId(
|
||||||
instance.handleUpdateChannelNotifyProps = jest.fn();
|
'MobileNotification-mention',
|
||||||
instance.updateSection = jest.fn();
|
|
||||||
|
|
||||||
wrapper.setState({desktopNotifyLevel: NotificationLevels.MENTION});
|
|
||||||
instance.handleSubmitDesktopNotification();
|
|
||||||
expect(instance.handleUpdateChannelNotifyProps).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
wrapper.setState({desktopNotifyLevel: NotificationLevels.ALL});
|
|
||||||
instance.handleSubmitDesktopNotification();
|
|
||||||
expect(instance.updateSection).toHaveBeenCalledTimes(1);
|
|
||||||
expect(instance.updateSection).toBeCalledWith('');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match state on handleUpdateDesktopNotifyLevel', () => {
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
|
||||||
);
|
);
|
||||||
|
fireEvent.click(MentionslabelRadio);
|
||||||
|
expect(MentionslabelRadio.checked).toEqual(true);
|
||||||
|
|
||||||
wrapper.setState({desktopNotifyLevel: NotificationLevels.ALL});
|
const NothinglabelRadio: HTMLInputElement = screen.getByTestId(
|
||||||
wrapper.instance().handleUpdateDesktopNotifyLevel(NotificationLevels.MENTION);
|
'MobileNotification-none',
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.MENTION);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match state on handleSubmitMarkUnreadLevel', () => {
|
|
||||||
const channelMember = TestHelper.getChannelMembershipMock({
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.NONE,
|
|
||||||
mark_unread: NotificationLevels.ALL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const props = {...baseProps, channelMember};
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
);
|
||||||
|
fireEvent.click(NothinglabelRadio);
|
||||||
|
expect(NothinglabelRadio.checked).toEqual(true);
|
||||||
|
|
||||||
const instance = wrapper.instance();
|
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
|
||||||
instance.handleUpdateChannelNotifyProps = jest.fn();
|
await waitFor(() =>
|
||||||
instance.updateSection = jest.fn();
|
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
|
||||||
|
'current_user_id',
|
||||||
wrapper.setState({markUnreadNotifyLevel: NotificationLevels.MENTION});
|
'channel_id',
|
||||||
instance.handleSubmitMarkUnreadLevel();
|
{
|
||||||
expect(instance.handleUpdateChannelNotifyProps).toHaveBeenCalledTimes(1);
|
desktop: 'all',
|
||||||
|
ignore_channel_mentions: 'off',
|
||||||
wrapper.setState({markUnreadNotifyLevel: NotificationLevels.ALL});
|
mark_unread: 'all',
|
||||||
instance.handleSubmitMarkUnreadLevel();
|
push: 'none',
|
||||||
expect(instance.updateSection).toHaveBeenCalledTimes(1);
|
},
|
||||||
expect(instance.updateSection).toBeCalledWith('');
|
),
|
||||||
});
|
|
||||||
|
|
||||||
test('should match state on handleUpdateMarkUnreadLevel', () => {
|
|
||||||
const channelMember = TestHelper.getChannelMembershipMock({
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.NONE,
|
|
||||||
mark_unread: NotificationLevels.ALL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const props = {...baseProps, channelMember};
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
wrapper.setState({markUnreadNotifyLevel: NotificationLevels.ALL});
|
|
||||||
wrapper.instance().handleUpdateMarkUnreadLevel(NotificationLevels.MENTION);
|
|
||||||
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.MENTION);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should match state on handleSubmitPushNotificationLevel', () => {
|
it('should show auto follow, desktop threads and mobile threads settings if collapsed reply threads is enabled', async () => {
|
||||||
const channelMember = {
|
const props = {
|
||||||
notify_props: {
|
...baseProps,
|
||||||
desktop: NotificationLevels.NONE,
|
collapsedReplyThreads: true,
|
||||||
mark_unread: NotificationLevels.MENTION,
|
|
||||||
push: NotificationLevels.ALL,
|
|
||||||
push_threads: NotificationLevels.ALL,
|
|
||||||
},
|
|
||||||
} as unknown as ChannelMembership;
|
|
||||||
const props = {...baseProps, channelMember};
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
const instance = wrapper.instance();
|
|
||||||
instance.handleUpdateChannelNotifyProps = jest.fn();
|
|
||||||
instance.updateSection = jest.fn();
|
|
||||||
|
|
||||||
wrapper.setState({pushNotifyLevel: NotificationLevels.DEFAULT});
|
|
||||||
instance.handleSubmitPushNotificationLevel();
|
|
||||||
expect(instance.handleUpdateChannelNotifyProps).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
wrapper.setState({pushNotifyLevel: NotificationLevels.ALL});
|
|
||||||
instance.handleSubmitPushNotificationLevel();
|
|
||||||
expect(instance.updateSection).toHaveBeenCalledTimes(1);
|
|
||||||
expect(instance.updateSection).toBeCalledWith('');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match state on handleUpdatePushNotificationLevel', () => {
|
|
||||||
const channelMember = TestHelper.getChannelMembershipMock({
|
|
||||||
notify_props: {
|
|
||||||
desktop: NotificationLevels.NONE,
|
|
||||||
mark_unread: NotificationLevels.MENTION,
|
|
||||||
push: NotificationLevels.ALL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const props = {...baseProps, channelMember};
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
wrapper.setState({pushNotifyLevel: NotificationLevels.ALL});
|
|
||||||
wrapper.instance().handleUpdatePushNotificationLevel(NotificationLevels.MENTION);
|
|
||||||
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.MENTION);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match state on resetStateFromNotifyProps', () => {
|
|
||||||
const channelMemberNotifyProps: Partial<ChannelNotifyProps> = {
|
|
||||||
desktop: NotificationLevels.NONE,
|
|
||||||
mark_unread: NotificationLevels.MENTION,
|
|
||||||
push: NotificationLevels.ALL,
|
|
||||||
};
|
};
|
||||||
const currentUserNotifyProps = {
|
const wrapper = renderWithContext(
|
||||||
channel: 'false',
|
<ChannelNotificationsModal {...props}/>,
|
||||||
} as UserNotifyProps;
|
|
||||||
const wrapper = shallow<ChannelNotificationsModal>(
|
|
||||||
<ChannelNotificationsModal {...baseProps}/>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.instance().resetStateFromNotifyProps(currentUserNotifyProps, channelMemberNotifyProps);
|
expect(screen.queryByText('Follow all threads in this channel')).toBeVisible();
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.NONE);
|
|
||||||
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.MENTION);
|
|
||||||
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.ALL);
|
|
||||||
expect(wrapper.state('ignoreChannelMentions')).toEqual(IgnoreChannelMentions.ON);
|
|
||||||
expect(wrapper.state('channelAutoFollowThreads')).toEqual(ChannelAutoFollowThreads.OFF);
|
|
||||||
|
|
||||||
wrapper.instance().resetStateFromNotifyProps(currentUserNotifyProps, {...channelMemberNotifyProps, desktop: NotificationLevels.ALL});
|
fireEvent.click(screen.getByRole('button', {name: /Save/i}));
|
||||||
expect(wrapper.state('desktopNotifyLevel')).toEqual(NotificationLevels.ALL);
|
|
||||||
|
|
||||||
wrapper.instance().resetStateFromNotifyProps(currentUserNotifyProps, {...channelMemberNotifyProps, mark_unread: NotificationLevels.ALL});
|
await waitFor(() =>
|
||||||
expect(wrapper.state('markUnreadNotifyLevel')).toEqual(NotificationLevels.ALL);
|
expect(baseProps.actions.updateChannelNotifyProps).toHaveBeenCalledWith(
|
||||||
|
'current_user_id',
|
||||||
wrapper.instance().resetStateFromNotifyProps(currentUserNotifyProps, {...channelMemberNotifyProps, push: NotificationLevels.NONE});
|
'channel_id',
|
||||||
expect(wrapper.state('pushNotifyLevel')).toEqual(NotificationLevels.NONE);
|
{
|
||||||
|
desktop: baseProps.channelMember?.notify_props.desktop,
|
||||||
|
ignore_channel_mentions: 'off',
|
||||||
|
mark_unread: 'all',
|
||||||
|
channel_auto_follow_threads: 'off',
|
||||||
|
push: 'all',
|
||||||
|
push_threads: 'default',
|
||||||
|
desktop_threads: 'all',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,23 +1,29 @@
|
|||||||
// 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 deepEqual from 'fast-deep-equal';
|
import React, {useCallback, useState} from 'react';
|
||||||
import React from 'react';
|
|
||||||
import {Modal} from 'react-bootstrap';
|
import {Modal} from 'react-bootstrap';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage, useIntl} from 'react-intl';
|
||||||
|
|
||||||
|
import {BellOffOutlineIcon, RefreshIcon} from '@mattermost/compass-icons/components';
|
||||||
import type {Channel, ChannelNotifyProps} from '@mattermost/types/channels';
|
import type {Channel, ChannelNotifyProps} from '@mattermost/types/channels';
|
||||||
import type {UserNotifyProps, UserProfile} from '@mattermost/types/users';
|
import type {UserNotifyProps, UserProfile} from '@mattermost/types/users';
|
||||||
|
|
||||||
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
|
import AlertBanner from 'components/alert_banner';
|
||||||
|
import CheckboxSettingItem from 'components/widgets/modals/components/checkbox_setting_item';
|
||||||
|
import ModalHeader from 'components/widgets/modals/components/modal_header';
|
||||||
|
import ModalSection from 'components/widgets/modals/components/modal_section';
|
||||||
|
import RadioSettingItem from 'components/widgets/modals/components/radio_setting_item';
|
||||||
|
|
||||||
import NotificationSection from 'components/channel_notifications_modal/components/notification_section.jsx';
|
import {IgnoreChannelMentions, NotificationLevels} from 'utils/constants';
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, DesktopSound, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
import type {ChannelMemberNotifyProps} from './utils';
|
||||||
import * as NotificationSounds from 'utils/notification_sounds';
|
import utils from './utils';
|
||||||
|
|
||||||
import type {PropsFromRedux} from './index';
|
import type {PropsFromRedux} from './index';
|
||||||
|
|
||||||
|
import './channel_notifications_modal.scss';
|
||||||
|
|
||||||
type Props = PropsFromRedux & {
|
type Props = PropsFromRedux & {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,508 +42,296 @@ type Props = PropsFromRedux & {
|
|||||||
currentUser: UserProfile;
|
currentUser: UserProfile;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChannelMemberNotifyProps = Partial<ChannelNotifyProps> & Pick<UserNotifyProps, 'desktop_threads' | 'push_threads'>
|
function getUseSameDesktopSetting(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
|
||||||
|
const isSameAsDesktop = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop === channelMemberNotifyProps?.push :
|
||||||
type State = {
|
currentUserNotifyProps.push === currentUserNotifyProps.desktop;
|
||||||
show: boolean;
|
const isSameAsDesktopThreads = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop_threads === channelMemberNotifyProps?.push_threads :
|
||||||
activeSection: string;
|
currentUserNotifyProps.push_threads === currentUserNotifyProps.desktop_threads;
|
||||||
serverError: string | null;
|
return isSameAsDesktop && isSameAsDesktopThreads;
|
||||||
desktopNotifyLevel: ChannelNotifyProps['desktop'];
|
}
|
||||||
desktopSound: ChannelNotifyProps['desktop_sound'];
|
|
||||||
desktopNotifySound: ChannelNotifyProps['desktop_notification_sound'];
|
function getStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
|
||||||
desktopThreadsNotifyLevel: UserNotifyProps['desktop_threads'];
|
let ignoreChannelMentionsDefault: ChannelNotifyProps['ignore_channel_mentions'] = IgnoreChannelMentions.OFF;
|
||||||
markUnreadNotifyLevel: ChannelNotifyProps['mark_unread'];
|
|
||||||
pushNotifyLevel: ChannelNotifyProps['push'];
|
if (channelMemberNotifyProps?.mark_unread === NotificationLevels.MENTION || (currentUserNotifyProps.channel && currentUserNotifyProps.channel === 'false')) {
|
||||||
pushThreadsNotifyLevel: UserNotifyProps['push_threads'];
|
ignoreChannelMentionsDefault = IgnoreChannelMentions.ON;
|
||||||
ignoreChannelMentions: ChannelNotifyProps['ignore_channel_mentions'];
|
}
|
||||||
channelAutoFollowThreads: ChannelNotifyProps['channel_auto_follow_threads'];
|
|
||||||
};
|
let ignoreChannelMentions = channelMemberNotifyProps?.ignore_channel_mentions;
|
||||||
|
if (!ignoreChannelMentions || ignoreChannelMentions === IgnoreChannelMentions.DEFAULT) {
|
||||||
export type DesktopNotificationProps = Pick<State, 'desktopNotifyLevel' | 'desktopNotifySound' | 'desktopSound' | 'desktopThreadsNotifyLevel'>
|
ignoreChannelMentions = ignoreChannelMentionsDefault;
|
||||||
export type PushNotificationProps = Pick<State, 'pushNotifyLevel' | 'pushThreadsNotifyLevel'>
|
}
|
||||||
|
|
||||||
const getDefaultDesktopNotificationLevel = (currentUserNotifyProps: UserNotifyProps, isGM: boolean): Exclude<ChannelMemberNotifyProps['desktop'], undefined> => {
|
const desktop = channelMemberNotifyProps?.desktop === NotificationLevels.DEFAULT ? currentUserNotifyProps.desktop : (channelMemberNotifyProps?.desktop || currentUserNotifyProps.desktop);
|
||||||
if (currentUserNotifyProps?.desktop) {
|
const push = channelMemberNotifyProps?.push === NotificationLevels.DEFAULT ? currentUserNotifyProps.desktop : (channelMemberNotifyProps?.push || currentUserNotifyProps.push);
|
||||||
if (currentUserNotifyProps.desktop === NotificationLevels.DEFAULT) {
|
|
||||||
return NotificationLevels.ALL;
|
return {
|
||||||
}
|
desktop,
|
||||||
|
desktop_threads: channelMemberNotifyProps?.desktop_threads || NotificationLevels.ALL,
|
||||||
if (isGM && currentUserNotifyProps.desktop === NotificationLevels.MENTION) {
|
mark_unread: channelMemberNotifyProps?.mark_unread || NotificationLevels.ALL,
|
||||||
return NotificationLevels.ALL;
|
push,
|
||||||
}
|
push_threads: channelMemberNotifyProps?.push_threads || NotificationLevels.ALL,
|
||||||
return currentUserNotifyProps.desktop;
|
ignore_channel_mentions: ignoreChannelMentions,
|
||||||
}
|
channel_auto_follow_threads: channelMemberNotifyProps?.channel_auto_follow_threads || 'off',
|
||||||
return NotificationLevels.ALL;
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
const getDefaultDesktopSound = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop_sound'], undefined> => {
|
type SettingsType = {
|
||||||
if (currentUserNotifyProps?.desktop_sound) {
|
desktop: ChannelNotifyProps['desktop'];
|
||||||
return currentUserNotifyProps.desktop_sound === 'true' ? DesktopSound.ON : DesktopSound.OFF;
|
desktop_threads: ChannelNotifyProps['desktop_threads'];
|
||||||
}
|
mark_unread: ChannelNotifyProps['mark_unread'];
|
||||||
return DesktopSound.ON;
|
push: ChannelNotifyProps['push'];
|
||||||
};
|
push_threads: ChannelNotifyProps['push_threads'];
|
||||||
|
ignore_channel_mentions: ChannelNotifyProps['ignore_channel_mentions'];
|
||||||
const getDefaultDesktopNotificationSound = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop_notification_sound'], undefined> => {
|
channel_auto_follow_threads: ChannelNotifyProps['channel_auto_follow_threads'];
|
||||||
if (currentUserNotifyProps?.desktop_notification_sound) {
|
};
|
||||||
return currentUserNotifyProps.desktop_notification_sound;
|
|
||||||
}
|
export default function ChannelNotificationsModal(props: Props) {
|
||||||
return 'Bing';
|
const {formatMessage} = useIntl();
|
||||||
};
|
const [show, setShow] = useState(true);
|
||||||
const getDefaultDesktopThreadsNotifyLevel = (currentUserNotifyProps: UserNotifyProps): Exclude<ChannelMemberNotifyProps['desktop_threads'], undefined> => {
|
const [serverError, setServerError] = useState('');
|
||||||
if (currentUserNotifyProps?.desktop_threads) {
|
const [mobileSettingsSameAsDesktop, setMobileSettingsSameAsDesktop] = useState<boolean>(getUseSameDesktopSetting(props.currentUser.notify_props, props.channelMember?.notify_props));
|
||||||
return currentUserNotifyProps.desktop_threads;
|
const [settings, setSettings] = useState<SettingsType>(getStateFromNotifyProps(props.currentUser.notify_props, props.channelMember?.notify_props));
|
||||||
}
|
|
||||||
return NotificationLevels.ALL;
|
function handleHide() {
|
||||||
};
|
setShow(false);
|
||||||
|
}
|
||||||
const getDefaultPushNotifyLevel = (currentUserNotifyProps: UserNotifyProps, isGM: boolean): Exclude<ChannelMemberNotifyProps['push'], undefined> => {
|
|
||||||
if (currentUserNotifyProps?.push) {
|
const handleChange = useCallback((values: Record<string, string>) => {
|
||||||
if (currentUserNotifyProps.push === NotificationLevels.DEFAULT) {
|
setSettings((prevSettings) => ({...prevSettings, ...values}));
|
||||||
return NotificationLevels.ALL;
|
}, []);
|
||||||
}
|
|
||||||
|
const handleMobileSettingsChange = useCallback(() => {
|
||||||
if (isGM && currentUserNotifyProps.desktop === NotificationLevels.MENTION) {
|
setMobileSettingsSameAsDesktop((prevSettings) => !prevSettings);
|
||||||
return NotificationLevels.ALL;
|
setSettings((prevSettings) => ({...prevSettings, push: prevSettings.desktop, push_threads: prevSettings.desktop_threads}));
|
||||||
}
|
}, []);
|
||||||
|
|
||||||
return currentUserNotifyProps.push;
|
const MuteIgnoreSectionContent = (
|
||||||
}
|
<>
|
||||||
return NotificationLevels.ALL;
|
<CheckboxSettingItem
|
||||||
};
|
description={utils.MuteChannelDesc}
|
||||||
|
inputFieldValue={settings.mark_unread === 'mention'}
|
||||||
const getDefaultPushThreadsNotifyLevel = (currentUserNotifyProps: UserNotifyProps, isGM: boolean): Exclude<ChannelMemberNotifyProps['push_threads'], undefined> => {
|
inputFieldData={utils.MuteChannelInputFieldData}
|
||||||
if (currentUserNotifyProps?.push_threads) {
|
handleChange={(e) => handleChange({mark_unread: e ? 'mention' : 'all'})}
|
||||||
if (currentUserNotifyProps.push_threads === 'default') {
|
/>
|
||||||
return NotificationLevels.ALL;
|
<CheckboxSettingItem
|
||||||
}
|
description={utils.IgnoreMentionsDesc}
|
||||||
|
inputFieldValue={settings.ignore_channel_mentions === 'on'}
|
||||||
if (isGM && currentUserNotifyProps.push_threads === NotificationLevels.MENTION) {
|
inputFieldData={utils.IgnoreMentionsInputFieldData}
|
||||||
return NotificationLevels.ALL;
|
handleChange={(e) => handleChange({ignore_channel_mentions: e ? 'on' : 'off'})}
|
||||||
}
|
/>
|
||||||
|
</>
|
||||||
return currentUserNotifyProps.push_threads;
|
);
|
||||||
}
|
|
||||||
return NotificationLevels.ALL;
|
const DesktopNotificationsSectionContent = (
|
||||||
};
|
<>
|
||||||
|
<RadioSettingItem
|
||||||
export default class ChannelNotificationsModal extends React.PureComponent<Props, State> {
|
title={utils.NotifyMeTitle}
|
||||||
constructor(props: Props) {
|
inputFieldValue={settings.desktop}
|
||||||
super(props);
|
inputFieldData={utils.desktopNotificationInputFieldData(props.currentUser.notify_props.desktop)}
|
||||||
|
handleChange={(e) => handleChange({desktop: e.target.value})}
|
||||||
const channelNotifyProps = props.channelMember?.notify_props;
|
/>
|
||||||
|
{props.collapsedReplyThreads && settings.desktop === 'mention' &&
|
||||||
this.state = {
|
<CheckboxSettingItem
|
||||||
show: true,
|
title={utils.ThreadsReplyTitle}
|
||||||
activeSection: NotificationSections.NONE,
|
inputFieldValue={settings.desktop_threads === 'all'}
|
||||||
serverError: null,
|
inputFieldData={utils.DesktopReplyThreadsInputFieldData}
|
||||||
...this.getStateFromNotifyProps(props.currentUser.notify_props, channelNotifyProps),
|
handleChange={(e) => handleChange({desktop_threads: e ? 'all' : 'mention'})}
|
||||||
};
|
/>}
|
||||||
}
|
</>
|
||||||
|
);
|
||||||
componentDidUpdate(prevProps: Props) {
|
|
||||||
const prevChannelNotifyProps = prevProps.channelMember && prevProps.channelMember.notify_props;
|
const MobileNotificationsSectionContent = (
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
<>
|
||||||
|
<CheckboxSettingItem
|
||||||
if (!deepEqual(channelNotifyProps, prevChannelNotifyProps)) {
|
inputFieldValue={mobileSettingsSameAsDesktop}
|
||||||
this.resetStateFromNotifyProps(this.props.currentUser.notify_props, channelNotifyProps);
|
inputFieldData={utils.sameMobileSettingsDesktopInputFieldData}
|
||||||
}
|
handleChange={() => handleMobileSettingsChange()}
|
||||||
}
|
/>
|
||||||
|
{!mobileSettingsSameAsDesktop && (
|
||||||
resetStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: Partial<ChannelNotifyProps>) {
|
<>
|
||||||
this.setState(this.getStateFromNotifyProps(currentUserNotifyProps, channelMemberNotifyProps));
|
<RadioSettingItem
|
||||||
}
|
title={utils.NotifyMeTitle}
|
||||||
|
inputFieldValue={settings.push}
|
||||||
verifyNotificationsSettingSameAsGlobal({
|
inputFieldData={utils.mobileNotificationInputFieldData(props.currentUser.notify_props.push)}
|
||||||
desktopNotifyLevel,
|
handleChange={(e) => handleChange({push: e.target.value})}
|
||||||
desktopNotifySound,
|
/>
|
||||||
desktopSound,
|
{props.collapsedReplyThreads && settings.push === 'mention' &&
|
||||||
desktopThreadsNotifyLevel,
|
<CheckboxSettingItem
|
||||||
}: DesktopNotificationProps) {
|
title={utils.ThreadsReplyTitle}
|
||||||
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
inputFieldValue={settings.push_threads === 'all'}
|
||||||
|
inputFieldData={utils.MobileReplyThreadsInputFieldData}
|
||||||
if (
|
handleChange={(e) => handleChange({push_threads: e ? 'all' : 'mention'})}
|
||||||
desktopNotifyLevel === getDefaultDesktopNotificationLevel(currentUserNotifyProps, this.isGM()) &&
|
/>}
|
||||||
desktopNotifySound === getDefaultDesktopNotificationSound(currentUserNotifyProps) &&
|
</>
|
||||||
desktopSound === getDefaultDesktopSound(currentUserNotifyProps) &&
|
)}
|
||||||
desktopThreadsNotifyLevel === getDefaultDesktopThreadsNotifyLevel(currentUserNotifyProps)
|
</>
|
||||||
) {
|
);
|
||||||
return true;
|
|
||||||
}
|
const AutoFollowThreadsSectionContent = (
|
||||||
return false;
|
<>
|
||||||
}
|
<CheckboxSettingItem
|
||||||
|
inputFieldValue={settings.channel_auto_follow_threads === 'on'}
|
||||||
isGM() {
|
inputFieldData={utils.AutoFollowThreadsInputFieldData}
|
||||||
return this.props.channel.type === 'G';
|
handleChange={(e) => handleChange({channel_auto_follow_threads: e ? 'on' : 'off'})}
|
||||||
}
|
/>
|
||||||
|
</>
|
||||||
verifyPushNotificationsSettingSameAsGlobal({
|
);
|
||||||
pushNotifyLevel,
|
|
||||||
pushThreadsNotifyLevel,
|
function handleSave() {
|
||||||
}: PushNotificationProps) {
|
const userSettings: Partial<SettingsType> = {...settings};
|
||||||
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
if (!props.collapsedReplyThreads) {
|
||||||
|
delete userSettings.push_threads;
|
||||||
if (
|
delete userSettings.desktop_threads;
|
||||||
pushNotifyLevel === getDefaultPushNotifyLevel(currentUserNotifyProps, this.isGM()) &&
|
delete userSettings.channel_auto_follow_threads;
|
||||||
pushThreadsNotifyLevel === getDefaultPushThreadsNotifyLevel(currentUserNotifyProps, this.isGM())
|
}
|
||||||
) {
|
props.actions.updateChannelNotifyProps(props.currentUser.id, props.channel.id, userSettings).then((value) => {
|
||||||
return true;
|
const {error} = value;
|
||||||
}
|
if (error) {
|
||||||
return false;
|
setServerError(error.message);
|
||||||
}
|
} else {
|
||||||
|
handleHide();
|
||||||
getStateFromNotifyProps(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
|
}
|
||||||
let ignoreChannelMentionsDefault: ChannelNotifyProps['ignore_channel_mentions'] = IgnoreChannelMentions.OFF;
|
});
|
||||||
|
}
|
||||||
const desktopNotifyLevelDefault: ChannelNotifyProps['desktop'] = getDefaultDesktopNotificationLevel(currentUserNotifyProps, this.isGM());
|
|
||||||
const pushNotifyLevelDefault: ChannelMemberNotifyProps['push'] = getDefaultPushNotifyLevel(currentUserNotifyProps, this.isGM());
|
const resetToDefaultBtn = useCallback((settingName: string) => {
|
||||||
const pushThreadsNotifyLevelDefault: ChannelMemberNotifyProps['push_threads'] = getDefaultPushThreadsNotifyLevel(currentUserNotifyProps, this.isGM());
|
const defaultSettings = props.currentUser.notify_props;
|
||||||
|
|
||||||
const channelDesktopNotifyProps = channelMemberNotifyProps?.desktop || NotificationLevels.DEFAULT;
|
const resetToDefault = (settingName: string) => {
|
||||||
let desktopNotifyLevel = desktopNotifyLevelDefault;
|
if (settingName === 'desktop') {
|
||||||
if (channelDesktopNotifyProps !== NotificationLevels.DEFAULT) {
|
setSettings({...settings, desktop: defaultSettings.desktop, desktop_threads: defaultSettings.desktop_threads || settings.desktop_threads});
|
||||||
desktopNotifyLevel = channelDesktopNotifyProps;
|
}
|
||||||
}
|
if (settingName === 'push') {
|
||||||
|
setSettings({...settings, push: defaultSettings.desktop, push_threads: defaultSettings.push_threads || settings.push_threads});
|
||||||
const channelPushNotifyProps = channelMemberNotifyProps?.push || NotificationLevels.DEFAULT;
|
}
|
||||||
let pushNotifyLevel = pushNotifyLevelDefault;
|
};
|
||||||
if (channelPushNotifyProps !== 'default') {
|
|
||||||
pushNotifyLevel = channelPushNotifyProps;
|
const isDesktopSameAsDefault = (defaultSettings.desktop === settings.desktop && defaultSettings.desktop_threads === settings.desktop_threads);
|
||||||
}
|
const isPushSameAsDefault = (defaultSettings.push === settings.push && defaultSettings.push_threads === settings.push_threads);
|
||||||
|
if ((settingName === 'desktop' && isDesktopSameAsDefault) || (settingName === 'push' && isPushSameAsDefault)) {
|
||||||
const channelPushThreadsNotifyProps = channelMemberNotifyProps?.push_threads || NotificationLevels.DEFAULT;
|
return <></>;
|
||||||
let pushThreadsNotifyLevel = pushThreadsNotifyLevelDefault;
|
}
|
||||||
if (channelPushThreadsNotifyProps !== 'default') {
|
return (
|
||||||
pushThreadsNotifyLevel = channelPushThreadsNotifyProps;
|
<button
|
||||||
}
|
className='channel-notifications-settings-modal__reset-btn'
|
||||||
|
onClick={() => resetToDefault(settingName)}
|
||||||
if (channelMemberNotifyProps?.mark_unread === NotificationLevels.MENTION || (currentUserNotifyProps.channel && currentUserNotifyProps.channel === 'false')) {
|
>
|
||||||
ignoreChannelMentionsDefault = IgnoreChannelMentions.ON;
|
<RefreshIcon
|
||||||
}
|
size={14}
|
||||||
|
color={'currentColor'}
|
||||||
let ignoreChannelMentions = channelMemberNotifyProps?.ignore_channel_mentions;
|
/>
|
||||||
if (!ignoreChannelMentions || ignoreChannelMentions === IgnoreChannelMentions.DEFAULT) {
|
{formatMessage({
|
||||||
ignoreChannelMentions = ignoreChannelMentionsDefault;
|
id: 'channel_notifications.resetToDefault',
|
||||||
}
|
defaultMessage: 'Reset to default',
|
||||||
|
})}
|
||||||
return {
|
</button>
|
||||||
desktopNotifyLevel,
|
);
|
||||||
desktopSound: channelMemberNotifyProps?.desktop_sound || getDefaultDesktopSound(currentUserNotifyProps),
|
}, [props.currentUser, settings]);
|
||||||
desktopNotifySound: channelMemberNotifyProps?.desktop_notification_sound || getDefaultDesktopNotificationSound(currentUserNotifyProps),
|
|
||||||
desktopThreadsNotifyLevel: channelMemberNotifyProps?.desktop_threads || getDefaultDesktopThreadsNotifyLevel(currentUserNotifyProps),
|
const settingsAndAlertBanner = settings.mark_unread === 'all' ? (
|
||||||
markUnreadNotifyLevel: channelMemberNotifyProps?.mark_unread || NotificationLevels.ALL,
|
<>
|
||||||
pushNotifyLevel,
|
<div className='channel-notifications-settings-modal__divider'/>
|
||||||
pushThreadsNotifyLevel,
|
<ModalSection
|
||||||
ignoreChannelMentions,
|
title={utils.DesktopNotificationsSectionTitle}
|
||||||
channelAutoFollowThreads: channelMemberNotifyProps?.channel_auto_follow_threads || ChannelAutoFollowThreads.OFF,
|
description={utils.DesktopNotificationsSectionDesc}
|
||||||
};
|
content={DesktopNotificationsSectionContent}
|
||||||
}
|
titleSuffix={resetToDefaultBtn('desktop')}
|
||||||
|
/>
|
||||||
handleHide = () => this.setState({show: false});
|
<div className='channel-notifications-settings-modal__divider'/>
|
||||||
|
<ModalSection
|
||||||
handleExit = () => {
|
title={utils.MobileNotificationsSectionTitle}
|
||||||
this.updateSection(NotificationSections.NONE);
|
description={utils.MobileNotificationsSectionDesc}
|
||||||
this.props.onExited();
|
content={MobileNotificationsSectionContent}
|
||||||
};
|
titleSuffix={resetToDefaultBtn('push')}
|
||||||
|
/>
|
||||||
updateSection = (section = NotificationSections.NONE) => {
|
</>
|
||||||
this.setState({activeSection: section});
|
) : (
|
||||||
|
<AlertBanner
|
||||||
if (section === NotificationSections.NONE) {
|
mode='info'
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
variant='app'
|
||||||
this.resetStateFromNotifyProps(this.props.currentUser.notify_props, channelNotifyProps);
|
customIcon={
|
||||||
}
|
<BellOffOutlineIcon
|
||||||
};
|
size={24}
|
||||||
|
color={'currentColor'}
|
||||||
handleUpdateChannelNotifyProps = async (props: Partial<ChannelNotifyProps>) => {
|
/>
|
||||||
const {
|
}
|
||||||
actions,
|
title={
|
||||||
channel,
|
<FormattedMessage
|
||||||
currentUser,
|
id='channel_notifications.alertBanner.title'
|
||||||
} = this.props;
|
defaultMessage='This channel is muted'
|
||||||
|
/>
|
||||||
const {error} = await actions.updateChannelNotifyProps(currentUser.id, channel.id, props);
|
}
|
||||||
if (error) {
|
message={
|
||||||
this.setState({serverError: error.message});
|
<FormattedMessage
|
||||||
} else {
|
id='channel_notifications.alertBanner.description'
|
||||||
this.updateSection(NotificationSections.NONE);
|
defaultMessage='All other notification preferences for this channel are disabled'
|
||||||
}
|
/>
|
||||||
};
|
}
|
||||||
handleResetDesktopNotification = () => {
|
/>
|
||||||
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
);
|
||||||
|
|
||||||
const userDesktopNotificationDefaults = {
|
return (
|
||||||
desktopNotifyLevel: getDefaultDesktopNotificationLevel(currentUserNotifyProps, this.isGM()),
|
<Modal
|
||||||
desktopSound: getDefaultDesktopSound(currentUserNotifyProps),
|
dialogClassName='a11y__modal channel-notifications-settings-modal'
|
||||||
desktopNotifySound: getDefaultDesktopNotificationSound(currentUserNotifyProps),
|
show={show}
|
||||||
desktopThreadsNotifyLevel: getDefaultDesktopThreadsNotifyLevel(currentUserNotifyProps),
|
onHide={handleHide}
|
||||||
};
|
onExited={props.onExited}
|
||||||
|
role='dialog'
|
||||||
this.setState(userDesktopNotificationDefaults);
|
aria-labelledby='channelNotificationModalLabel'
|
||||||
};
|
style={{display: 'flex', placeItems: 'center'}}
|
||||||
|
>
|
||||||
handleResetPushNotification = () => {
|
<ModalHeader
|
||||||
const currentUserNotifyProps = this.props.currentUser.notify_props;
|
id={'channelNotificationModalLabel'}
|
||||||
|
title={formatMessage({
|
||||||
const userPushNotificationDefaults = {
|
id: 'channel_notifications.preferences',
|
||||||
pushNotifyLevel: getDefaultPushNotifyLevel(currentUserNotifyProps, this.isGM()),
|
defaultMessage: 'Notification Preferences',
|
||||||
pushThreadsNotifyLevel: getDefaultPushThreadsNotifyLevel(currentUserNotifyProps, this.isGM()),
|
})}
|
||||||
};
|
subtitle={props.channel.display_name}
|
||||||
|
handleClose={handleHide}
|
||||||
this.setState(userPushNotificationDefaults);
|
/>
|
||||||
};
|
<main className='channel-notifications-settings-modal__body'>
|
||||||
|
<ModalSection
|
||||||
handleSubmitDesktopNotification = () => {
|
title={utils.MuteAndIgnoreSectionTitle}
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props as ChannelMemberNotifyProps;
|
content={MuteIgnoreSectionContent}
|
||||||
const {desktopNotifyLevel, desktopNotifySound, desktopSound, desktopThreadsNotifyLevel} = this.state;
|
/>
|
||||||
|
{settingsAndAlertBanner}
|
||||||
if (
|
{props.collapsedReplyThreads &&
|
||||||
channelNotifyProps?.desktop === desktopNotifyLevel &&
|
<>
|
||||||
channelNotifyProps?.desktop_threads === desktopThreadsNotifyLevel &&
|
<div className='channel-notifications-settings-modal__divider'/>
|
||||||
channelNotifyProps?.desktop_sound === desktopSound &&
|
<ModalSection
|
||||||
channelNotifyProps?.desktop_notification_sound === desktopNotifySound
|
title={utils.AutoFollowThreadsTitle}
|
||||||
) {
|
description={utils.AutoFollowThreadsDesc}
|
||||||
this.updateSection(NotificationSections.NONE);
|
content={AutoFollowThreadsSectionContent}
|
||||||
return;
|
/>
|
||||||
}
|
</>
|
||||||
|
}
|
||||||
const props = {desktop: desktopNotifyLevel, desktop_threads: desktopThreadsNotifyLevel, desktop_sound: desktopSound, desktop_notification_sound: desktopNotifySound};
|
</main>
|
||||||
|
<footer className='channel-notifications-settings-modal__footer'>
|
||||||
this.handleUpdateChannelNotifyProps(props);
|
{serverError &&
|
||||||
};
|
<span className='channel-notifications-settings-modal__server-error'>
|
||||||
|
{serverError}
|
||||||
handleUpdateDesktopNotifyLevel = (desktopNotifyLevel: ChannelNotifyProps['desktop']) => this.setState({desktopNotifyLevel});
|
</span>
|
||||||
|
}
|
||||||
handleUpdateDesktopThreadsNotifyLevel = (desktopThreadsNotifyLevel: UserNotifyProps['desktop_threads']) => this.setState({desktopThreadsNotifyLevel});
|
<button
|
||||||
|
onClick={handleHide}
|
||||||
handleUpdateDesktopSound = (desktopSound: ChannelNotifyProps['desktop_sound']) => this.setState({desktopSound});
|
className='channel-notifications-settings-modal__cancel-btn'
|
||||||
|
>
|
||||||
handleUpdateDesktopNotifySound = (desktopNotifySound: ChannelNotifyProps['desktop_notification_sound']) => {
|
<FormattedMessage
|
||||||
if (desktopNotifySound) {
|
id='generic_btn.cancel'
|
||||||
NotificationSounds.tryNotificationSound(desktopNotifySound);
|
defaultMessage='Cancel'
|
||||||
}
|
/>
|
||||||
this.setState({desktopNotifySound});
|
</button>
|
||||||
};
|
<button
|
||||||
|
className={'channel-notifications-settings-modal__save-btn'}
|
||||||
handleSubmitMarkUnreadLevel = () => {
|
onClick={handleSave}
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
>
|
||||||
const {markUnreadNotifyLevel} = this.state;
|
<FormattedMessage
|
||||||
|
id='generic_btn.save'
|
||||||
if (channelNotifyProps?.mark_unread === markUnreadNotifyLevel) {
|
defaultMessage='Save'
|
||||||
this.updateSection(NotificationSections.NONE);
|
/>
|
||||||
return;
|
</button>
|
||||||
}
|
</footer>
|
||||||
|
</Modal>
|
||||||
const props = {mark_unread: markUnreadNotifyLevel};
|
);
|
||||||
this.handleUpdateChannelNotifyProps(props);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleUpdateMarkUnreadLevel = (markUnreadNotifyLevel: ChannelNotifyProps['mark_unread']) => this.setState({markUnreadNotifyLevel});
|
|
||||||
|
|
||||||
handleSubmitPushNotificationLevel = () => {
|
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props as ChannelMemberNotifyProps;
|
|
||||||
const {pushNotifyLevel, pushThreadsNotifyLevel} = this.state;
|
|
||||||
|
|
||||||
if (
|
|
||||||
channelNotifyProps?.push === pushNotifyLevel &&
|
|
||||||
channelNotifyProps?.push_threads === pushThreadsNotifyLevel
|
|
||||||
) {
|
|
||||||
this.updateSection(NotificationSections.NONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = {push: pushNotifyLevel, push_threads: pushThreadsNotifyLevel};
|
|
||||||
this.handleUpdateChannelNotifyProps(props);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleUpdatePushNotificationLevel = (pushNotifyLevel: ChannelNotifyProps['push']) => this.setState({pushNotifyLevel});
|
|
||||||
handleUpdatePushThreadsNotificationLevel = (pushThreadsNotifyLevel: UserNotifyProps['push_threads']) => this.setState({pushThreadsNotifyLevel});
|
|
||||||
handleUpdateIgnoreChannelMentions = (ignoreChannelMentions: ChannelNotifyProps['ignore_channel_mentions']) => this.setState({ignoreChannelMentions});
|
|
||||||
|
|
||||||
handleSubmitIgnoreChannelMentions = () => {
|
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
|
||||||
const {ignoreChannelMentions} = this.state;
|
|
||||||
|
|
||||||
if (channelNotifyProps?.ignore_channel_mentions === ignoreChannelMentions) {
|
|
||||||
this.updateSection(NotificationSections.NONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = {ignore_channel_mentions: ignoreChannelMentions};
|
|
||||||
this.handleUpdateChannelNotifyProps(props);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleUpdateChannelAutoFollowThreads = (channelAutoFollowThreads: ChannelNotifyProps['channel_auto_follow_threads']) => this.setState({channelAutoFollowThreads});
|
|
||||||
|
|
||||||
handleSubmitChannelAutoFollowThreads = () => {
|
|
||||||
const channelNotifyProps = this.props.channelMember && this.props.channelMember.notify_props;
|
|
||||||
const {channelAutoFollowThreads} = this.state;
|
|
||||||
|
|
||||||
if (channelNotifyProps?.channel_auto_follow_threads === channelAutoFollowThreads) {
|
|
||||||
this.updateSection(NotificationSections.NONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = {channel_auto_follow_threads: channelAutoFollowThreads};
|
|
||||||
this.handleUpdateChannelNotifyProps(props);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
activeSection,
|
|
||||||
desktopNotifyLevel,
|
|
||||||
desktopThreadsNotifyLevel,
|
|
||||||
desktopSound,
|
|
||||||
desktopNotifySound,
|
|
||||||
markUnreadNotifyLevel,
|
|
||||||
pushNotifyLevel,
|
|
||||||
pushThreadsNotifyLevel,
|
|
||||||
ignoreChannelMentions,
|
|
||||||
channelAutoFollowThreads,
|
|
||||||
serverError,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const {
|
|
||||||
channel,
|
|
||||||
channelMember,
|
|
||||||
currentUser,
|
|
||||||
sendPushNotifications,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const isNotificationsSettingSameAsGlobal = this.verifyNotificationsSettingSameAsGlobal({
|
|
||||||
desktopNotifyLevel,
|
|
||||||
desktopNotifySound,
|
|
||||||
desktopSound,
|
|
||||||
desktopThreadsNotifyLevel,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isPushNotificationsSettingSameAsGlobal = this.verifyPushNotificationsSettingSameAsGlobal({
|
|
||||||
pushNotifyLevel,
|
|
||||||
pushThreadsNotifyLevel,
|
|
||||||
});
|
|
||||||
|
|
||||||
let serverErrorTag = null;
|
|
||||||
if (serverError) {
|
|
||||||
serverErrorTag = <div className='form-group has-error'><label className='control-label'>{serverError}</label></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isGM = this.isGM();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
dialogClassName='a11y__modal settings-modal settings-modal--tabless'
|
|
||||||
show={this.state.show}
|
|
||||||
onHide={this.handleHide}
|
|
||||||
onExited={this.handleExit}
|
|
||||||
role='dialog'
|
|
||||||
aria-labelledby='channelNotificationModalLabel'
|
|
||||||
>
|
|
||||||
<Modal.Header closeButton={true}>
|
|
||||||
<Modal.Title
|
|
||||||
componentClass='h1'
|
|
||||||
id='channelNotificationModalLabel'
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.preferences'
|
|
||||||
defaultMessage='Notification Preferences for '
|
|
||||||
/>
|
|
||||||
<span className='name'>{channel.display_name}</span>
|
|
||||||
</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Body>
|
|
||||||
<div className='settings-table'>
|
|
||||||
<div className='settings-content'>
|
|
||||||
<div className='user-settings'>
|
|
||||||
<br/>
|
|
||||||
<div className='divider-dark first'/>
|
|
||||||
<NotificationSection
|
|
||||||
section={NotificationSections.MARK_UNREAD}
|
|
||||||
expand={activeSection === NotificationSections.MARK_UNREAD}
|
|
||||||
memberNotificationLevel={markUnreadNotifyLevel}
|
|
||||||
onChange={this.handleUpdateMarkUnreadLevel}
|
|
||||||
onSubmit={this.handleSubmitMarkUnreadLevel}
|
|
||||||
onUpdateSection={this.updateSection}
|
|
||||||
serverError={serverError}
|
|
||||||
isGM={isGM}
|
|
||||||
/>
|
|
||||||
<div className='divider-light'/>
|
|
||||||
<NotificationSection
|
|
||||||
section={NotificationSections.IGNORE_CHANNEL_MENTIONS}
|
|
||||||
expand={activeSection === NotificationSections.IGNORE_CHANNEL_MENTIONS}
|
|
||||||
memberNotificationLevel={markUnreadNotifyLevel}
|
|
||||||
ignoreChannelMentions={ignoreChannelMentions}
|
|
||||||
onChange={this.handleUpdateIgnoreChannelMentions}
|
|
||||||
onSubmit={this.handleSubmitIgnoreChannelMentions}
|
|
||||||
onUpdateSection={this.updateSection}
|
|
||||||
serverError={serverError}
|
|
||||||
isGM={isGM}
|
|
||||||
/>
|
|
||||||
{!isChannelMuted(channelMember) &&
|
|
||||||
<div>
|
|
||||||
<div className='divider-light'/>
|
|
||||||
<NotificationSection
|
|
||||||
section={NotificationSections.DESKTOP}
|
|
||||||
expand={activeSection === NotificationSections.DESKTOP}
|
|
||||||
memberNotificationLevel={desktopNotifyLevel}
|
|
||||||
memberThreadsNotificationLevel={desktopThreadsNotifyLevel}
|
|
||||||
memberDesktopSound={desktopSound}
|
|
||||||
memberDesktopNotificationSound={desktopNotifySound}
|
|
||||||
globalNotificationLevel={getDefaultDesktopNotificationLevel(currentUser.notify_props, isGM)}
|
|
||||||
globalNotificationSound={getDefaultDesktopNotificationSound(currentUser.notify_props)}
|
|
||||||
isNotificationsSettingSameAsGlobal={isNotificationsSettingSameAsGlobal}
|
|
||||||
onChange={this.handleUpdateDesktopNotifyLevel}
|
|
||||||
onChangeThreads={this.handleUpdateDesktopThreadsNotifyLevel}
|
|
||||||
onChangeDesktopSound={this.handleUpdateDesktopSound}
|
|
||||||
onChangeNotificationSound={this.handleUpdateDesktopNotifySound}
|
|
||||||
onReset={this.handleResetDesktopNotification}
|
|
||||||
onSubmit={this.handleSubmitDesktopNotification}
|
|
||||||
onUpdateSection={this.updateSection}
|
|
||||||
serverError={serverError}
|
|
||||||
isGM={isGM}
|
|
||||||
/>
|
|
||||||
<div className='divider-light'/>
|
|
||||||
{sendPushNotifications &&
|
|
||||||
<NotificationSection
|
|
||||||
section={NotificationSections.PUSH}
|
|
||||||
expand={activeSection === NotificationSections.PUSH}
|
|
||||||
memberNotificationLevel={pushNotifyLevel}
|
|
||||||
memberThreadsNotificationLevel={pushThreadsNotifyLevel}
|
|
||||||
globalNotificationLevel={getDefaultPushNotifyLevel(currentUser.notify_props, isGM)}
|
|
||||||
isNotificationsSettingSameAsGlobal={isPushNotificationsSettingSameAsGlobal}
|
|
||||||
onChange={this.handleUpdatePushNotificationLevel}
|
|
||||||
onReset={this.handleResetPushNotification}
|
|
||||||
onChangeThreads={this.handleUpdatePushThreadsNotificationLevel}
|
|
||||||
onSubmit={this.handleSubmitPushNotificationLevel}
|
|
||||||
onUpdateSection={this.updateSection}
|
|
||||||
serverError={serverError}
|
|
||||||
isGM={isGM}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{!isGM &&
|
|
||||||
<>
|
|
||||||
<div className='divider-light'/>
|
|
||||||
<NotificationSection
|
|
||||||
section={NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS}
|
|
||||||
expand={activeSection === NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS}
|
|
||||||
memberNotificationLevel={markUnreadNotifyLevel}
|
|
||||||
ignoreChannelMentions={ignoreChannelMentions}
|
|
||||||
channelAutoFollowThreads={channelAutoFollowThreads}
|
|
||||||
onChange={this.handleUpdateChannelAutoFollowThreads}
|
|
||||||
onSubmit={this.handleSubmitChannelAutoFollowThreads}
|
|
||||||
onUpdateSection={this.updateSection}
|
|
||||||
serverError={serverError}
|
|
||||||
isGM={isGM}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
<div className='divider-dark'/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{serverErrorTag}
|
|
||||||
</Modal.Body>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/CollapseView should match snapshot, DESKTOP on collapsed view 1`] = `
|
|
||||||
<SettingItemMin
|
|
||||||
describe={
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
isCollapsed={true}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
section="desktop"
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/CollapseView should match snapshot, MARK_UNREAD on collapsed view 1`] = `
|
|
||||||
<SettingItemMin
|
|
||||||
describe={
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
isCollapsed={true}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
section="markUnread"
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/CollapseView should match snapshot, PUSH on collapsed view 1`] = `
|
|
||||||
<SettingItemMin
|
|
||||||
describe={
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
isCollapsed={true}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
section="push"
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
@ -1,59 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on DESKTOP/PUSH & ALL 1`] = `
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="For all activity {isDefault}"
|
|
||||||
id="channel_notifications.allActivity"
|
|
||||||
values={
|
|
||||||
Object {
|
|
||||||
"isDefault": <React.Fragment />,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on MARK_UNREAD & ALL 1`] = `
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Off"
|
|
||||||
id="channel_notifications.muteChannel.off.title"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on MENTION 1`] = `
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Only for mentions {isDefault}"
|
|
||||||
id="channel_notifications.onlyMentions"
|
|
||||||
values={
|
|
||||||
Object {
|
|
||||||
"isDefault": <React.Fragment />,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on NONE 1`] = `
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Never {isDefault}"
|
|
||||||
id="channel_notifications.never"
|
|
||||||
values={
|
|
||||||
Object {
|
|
||||||
"isDefault": <React.Fragment />,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, on global DEFAULT 1`] = `
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Global default ({notifyLevel})"
|
|
||||||
id="channel_notifications.globalDefault"
|
|
||||||
values={
|
|
||||||
Object {
|
|
||||||
"notifyLevel": <Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="default"
|
|
||||||
id="channel_notifications.levels.default"
|
|
||||||
/>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
`;
|
|
@ -1,942 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView gms should match snapshot, DESKTOP on expanded view when mentions is selected 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Send desktop notifications"
|
|
||||||
id="channel_notifications.sendDesktop"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationAllActivity"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationMentions"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationNever"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="none"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</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>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView gms should match snapshot, PUSH on expanded view when mentions is selected 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Send mobile push notifications"
|
|
||||||
id="channel_notifications.sendMobilePush"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationAllActivity"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationMentions"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationNever"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="none"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView normal channels should match snapshot, DESKTOP on expanded view 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Send desktop notifications"
|
|
||||||
id="channel_notifications.sendDesktop"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationAllActivity"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationMentions"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationNever"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="none"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</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>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView normal channels should match snapshot, DESKTOP on expanded view when mentions is selected 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Send desktop notifications"
|
|
||||||
id="channel_notifications.sendDesktop"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationAllActivity"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationMentions"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationNever"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="none"
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<React.Fragment>
|
|
||||||
<hr />
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Thread reply notifications"
|
|
||||||
id="user.settings.notifications.threads.desktop"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="checkbox"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="desktopThreadsNotificationAllActivity"
|
|
||||||
name="desktopThreadsNotificationLevel"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Notify me about threads I'm following"
|
|
||||||
id="user.settings.notifications.threads.allActivity"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="When enabled, any reply to a thread you're following will send a desktop notification."
|
|
||||||
id="user.settings.notifications.threads"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</React.Fragment>
|
|
||||||
<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>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView normal channels should match snapshot, MARK_UNREAD on expanded view 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationUnmute"
|
|
||||||
name="channelNotificationMute"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationMute"
|
|
||||||
name="channelNotificationMute"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView normal channels should match snapshot, PUSH on expanded view 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Send mobile push notifications"
|
|
||||||
id="channel_notifications.sendMobilePush"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationAllActivity"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationMentions"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationNever"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="none"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExpandView normal channels should match snapshot, PUSH on expanded view when mentions is selected 1`] = `
|
|
||||||
<SettingItemMax
|
|
||||||
containerStyle=""
|
|
||||||
infoPosition="bottom"
|
|
||||||
inputs={
|
|
||||||
Array [
|
|
||||||
<div>
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Send mobile push notifications"
|
|
||||||
id="channel_notifications.sendMobilePush"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationAllActivity"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="channelNotificationMentions"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="mention"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="mention"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="radio"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={false}
|
|
||||||
id="channelNotificationNever"
|
|
||||||
name="channelNotifications"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="none"
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<ExtraInfo
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<React.Fragment>
|
|
||||||
<hr />
|
|
||||||
<fieldset>
|
|
||||||
<legend
|
|
||||||
className="form-legend"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Thread reply notifications"
|
|
||||||
id="user.settings.notifications.threads.push"
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div
|
|
||||||
className="checkbox"
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={true}
|
|
||||||
id="pushThreadsNotificationAllActivity"
|
|
||||||
name="pushThreadsNotificationLevel"
|
|
||||||
onChange={[MockFunction]}
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="Notify me about threads I'm following"
|
|
||||||
id="user.settings.notifications.push_threads.allActivity"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="mt-5"
|
|
||||||
>
|
|
||||||
<Memo(MemoizedFormattedMessage)
|
|
||||||
defaultMessage="When enabled, any reply to a thread you're following will send a mobile push notification."
|
|
||||||
id="user.settings.notifications.push_threads"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</React.Fragment>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
saving={false}
|
|
||||||
section=""
|
|
||||||
serverError=""
|
|
||||||
submit={[MockFunction]}
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
isExpanded={true}
|
|
||||||
onClickResetButton={[MockFunction]}
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={[MockFunction]}
|
|
||||||
/>
|
|
||||||
`;
|
|
@ -1,28 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on DESKTOP 1`] = `
|
|
||||||
<span>
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
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"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on MARK_UNREAD 1`] = `
|
|
||||||
<span>
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Muting turns off desktop, email and push notifications for this channel. The channel will not be marked as unread unless you are mentioned."
|
|
||||||
id="channel_notifications.muteChannel.help"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on PUSH 1`] = `
|
|
||||||
<span>
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Selecting an option other than the \\"default\\" will override the global notification settings for mobile push notifications."
|
|
||||||
id="channel_notifications.overridePush"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
`;
|
|
@ -1,91 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot on server error 1`] = `
|
|
||||||
<CollapseView
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
onExpandSection={[Function]}
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, DESKTOP on collapsed view 1`] = `
|
|
||||||
<CollapseView
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
onExpandSection={[Function]}
|
|
||||||
section="desktop"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, DESKTOP on expanded view 1`] = `
|
|
||||||
<ExpandView
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
isGM={false}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
memberThreadsNotifyLevel="all"
|
|
||||||
onChange={[Function]}
|
|
||||||
onChangeDesktopSound={[Function]}
|
|
||||||
onChangeNotificationSound={[Function]}
|
|
||||||
onChangeThreads={[Function]}
|
|
||||||
onCollapseSection={[Function]}
|
|
||||||
onReset={[Function]}
|
|
||||||
onSubmit={[Function]}
|
|
||||||
section="desktop"
|
|
||||||
serverError=""
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, MARK_UNREAD on collapsed view 1`] = `
|
|
||||||
<CollapseView
|
|
||||||
globalNotifyLevel={null}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
onExpandSection={[Function]}
|
|
||||||
section="markUnread"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, MARK_UNREAD on expanded view 1`] = `
|
|
||||||
<ExpandView
|
|
||||||
globalNotifyLevel={null}
|
|
||||||
isGM={false}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
memberThreadsNotifyLevel="all"
|
|
||||||
onChange={[Function]}
|
|
||||||
onChangeDesktopSound={[Function]}
|
|
||||||
onChangeNotificationSound={[Function]}
|
|
||||||
onChangeThreads={[Function]}
|
|
||||||
onCollapseSection={[Function]}
|
|
||||||
onReset={[Function]}
|
|
||||||
onSubmit={[Function]}
|
|
||||||
section="markUnread"
|
|
||||||
serverError=""
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, PUSH on collapsed view 1`] = `
|
|
||||||
<CollapseView
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
onExpandSection={[Function]}
|
|
||||||
section="push"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/NotificationSection should match snapshot, PUSH on expanded view 1`] = `
|
|
||||||
<ExpandView
|
|
||||||
globalNotifyLevel="default"
|
|
||||||
isGM={false}
|
|
||||||
memberNotifyLevel="all"
|
|
||||||
memberThreadsNotifyLevel="all"
|
|
||||||
onChange={[Function]}
|
|
||||||
onChangeDesktopSound={[Function]}
|
|
||||||
onChangeNotificationSound={[Function]}
|
|
||||||
onChangeThreads={[Function]}
|
|
||||||
onCollapseSection={[Function]}
|
|
||||||
onReset={[Function]}
|
|
||||||
onSubmit={[Function]}
|
|
||||||
section="push"
|
|
||||||
serverError=""
|
|
||||||
/>
|
|
||||||
`;
|
|
@ -1,30 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on DESKTOP 1`] = `
|
|
||||||
<div
|
|
||||||
className="SectionTitle__wrapper"
|
|
||||||
>
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Desktop notifications"
|
|
||||||
id="channel_notifications.desktopNotifications"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on MARK_UNREAD 1`] = `
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Mute Channel"
|
|
||||||
id="channel_notifications.muteChannel.settings"
|
|
||||||
/>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/channel_notifications_modal/ExtraInfo should match snapshot, on PUSH 1`] = `
|
|
||||||
<div
|
|
||||||
className="SectionTitle__wrapper"
|
|
||||||
>
|
|
||||||
<MemoizedFormattedMessage
|
|
||||||
defaultMessage="Mobile push notifications"
|
|
||||||
id="channel_notifications.push"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import CollapseView from 'components/channel_notifications_modal/components/collapse_view';
|
|
||||||
|
|
||||||
import {NotificationLevels, NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/CollapseView', () => {
|
|
||||||
const baseProps = {
|
|
||||||
section: NotificationSections.DESKTOP,
|
|
||||||
memberNotifyLevel: NotificationLevels.ALL,
|
|
||||||
globalNotifyLevel: NotificationLevels.DEFAULT,
|
|
||||||
onExpandSection: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
test('should match snapshot, DESKTOP on collapsed view', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<CollapseView {...baseProps}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, PUSH on collapsed view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<CollapseView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, MARK_UNREAD on collapsed view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<CollapseView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,38 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import SettingItemMin from 'components/setting_item_min';
|
|
||||||
|
|
||||||
import Describe from './describe';
|
|
||||||
import SectionTitle from './section_title';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
ignoreChannelMentions?: string;
|
|
||||||
channelAutoFollowThreads?: string;
|
|
||||||
onExpandSection: (section: string) => void;
|
|
||||||
globalNotifyLevel?: string;
|
|
||||||
memberNotifyLevel: string;
|
|
||||||
section: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function CollapseView({onExpandSection, globalNotifyLevel, memberNotifyLevel, section, ignoreChannelMentions, channelAutoFollowThreads}: Props) {
|
|
||||||
return (
|
|
||||||
<SettingItemMin
|
|
||||||
title={<SectionTitle section={section}/>}
|
|
||||||
describe={
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
ignoreChannelMentions={ignoreChannelMentions}
|
|
||||||
channelAutoFollowThreads={channelAutoFollowThreads}
|
|
||||||
memberNotifyLevel={memberNotifyLevel}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
isCollapsed={true}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
updateSection={onExpandSection}
|
|
||||||
section={section}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import Describe from 'components/channel_notifications_modal/components/describe';
|
|
||||||
|
|
||||||
import {NotificationLevels, NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/NotificationSection', () => {
|
|
||||||
const baseProps = {
|
|
||||||
section: NotificationSections.DESKTOP,
|
|
||||||
memberNotifyLevel: NotificationLevels.DEFAULT,
|
|
||||||
globalNotifyLevel: NotificationLevels.DEFAULT,
|
|
||||||
};
|
|
||||||
|
|
||||||
test('should match snapshot, on global DEFAULT', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<Describe {...baseProps}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on MENTION', () => {
|
|
||||||
const props = {...baseProps, memberNotifyLevel: NotificationLevels.MENTION};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<Describe {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on DESKTOP/PUSH & ALL', () => {
|
|
||||||
const props = {...baseProps, memberNotifyLevel: NotificationLevels.ALL};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<Describe {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on MARK_UNREAD & ALL', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD, memberNotifyLevel: NotificationLevels.ALL};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<Describe {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on NONE', () => {
|
|
||||||
const props = {...baseProps, memberNotifyLevel: NotificationLevels.NONE};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<Describe {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,139 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
|
||||||
import {t} from 'utils/i18n';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
globalNotifyLevel?: string;
|
|
||||||
ignoreChannelMentions?: string;
|
|
||||||
channelAutoFollowThreads?: string;
|
|
||||||
memberNotifyLevel: string;
|
|
||||||
section: string;
|
|
||||||
isCollapsed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOption = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.defaultOption'
|
|
||||||
defaultMessage='(default)'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function Describe({section, isCollapsed, memberNotifyLevel, globalNotifyLevel, ignoreChannelMentions, channelAutoFollowThreads}: Props) {
|
|
||||||
if (memberNotifyLevel === NotificationLevels.DEFAULT && globalNotifyLevel) {
|
|
||||||
t('channel_notifications.levels.default');
|
|
||||||
t('channel_notifications.levels.all');
|
|
||||||
t('channel_notifications.levels.mention');
|
|
||||||
t('channel_notifications.levels.none');
|
|
||||||
const levelsFormattedMessageId = 'channel_notifications.levels.' + globalNotifyLevel;
|
|
||||||
const notifyLevel = (
|
|
||||||
<FormattedMessage
|
|
||||||
id={levelsFormattedMessageId}
|
|
||||||
defaultMessage={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.globalDefault'
|
|
||||||
defaultMessage='Global default ({notifyLevel})'
|
|
||||||
values={{notifyLevel}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (memberNotifyLevel === NotificationLevels.MENTION && section === NotificationSections.MARK_UNREAD) {
|
|
||||||
if (isCollapsed) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.muteChannel.on.title.collapse'
|
|
||||||
defaultMessage='Mute is enabled. Desktop, email and push notifications will not be sent for this channel.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.muteChannel.on.title'
|
|
||||||
defaultMessage='On'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
section === NotificationSections.IGNORE_CHANNEL_MENTIONS &&
|
|
||||||
ignoreChannelMentions === IgnoreChannelMentions.ON
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.ignoreChannelMentions.on.title'
|
|
||||||
defaultMessage='On'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
section === NotificationSections.IGNORE_CHANNEL_MENTIONS &&
|
|
||||||
ignoreChannelMentions === IgnoreChannelMentions.OFF
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.ignoreChannelMentions.off.title'
|
|
||||||
defaultMessage='Off'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
section === NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS &&
|
|
||||||
channelAutoFollowThreads === ChannelAutoFollowThreads.ON
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.channelAutoFollowThreads.on.title'
|
|
||||||
defaultMessage='On'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
section === NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS &&
|
|
||||||
channelAutoFollowThreads === ChannelAutoFollowThreads.OFF
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.channelAutoFollowThreads.off.title'
|
|
||||||
defaultMessage='Off'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (memberNotifyLevel === NotificationLevels.MENTION) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.onlyMentions'
|
|
||||||
defaultMessage='Only for mentions {isDefault}'
|
|
||||||
values={{isDefault: globalNotifyLevel === NotificationLevels.MENTION ? defaultOption : <></>}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
(section === NotificationSections.DESKTOP || section === NotificationSections.PUSH) &&
|
|
||||||
memberNotifyLevel === NotificationLevels.ALL
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.allActivity'
|
|
||||||
defaultMessage='For all activity {isDefault}'
|
|
||||||
values={{isDefault: globalNotifyLevel === NotificationLevels.ALL ? defaultOption : <></>}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
section === NotificationSections.MARK_UNREAD &&
|
|
||||||
memberNotifyLevel === NotificationLevels.ALL
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.muteChannel.off.title'
|
|
||||||
defaultMessage='Off'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.never'
|
|
||||||
defaultMessage='Never {isDefault}'
|
|
||||||
values={{isDefault: globalNotifyLevel === NotificationLevels.NONE ? defaultOption : <></>}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import ExpandView from 'components/channel_notifications_modal/components/expand_view';
|
|
||||||
|
|
||||||
import {NotificationLevels, NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
jest.mock('react-redux', () => ({
|
|
||||||
...jest.requireActual('react-redux'),
|
|
||||||
useSelector: jest.fn(() => true),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/ExpandView', () => {
|
|
||||||
const baseProps = {
|
|
||||||
section: NotificationSections.DESKTOP,
|
|
||||||
memberNotifyLevel: NotificationLevels.ALL,
|
|
||||||
memberThreadsNotifyLevel: NotificationLevels.ALL,
|
|
||||||
globalNotifyLevel: NotificationLevels.DEFAULT,
|
|
||||||
serverError: '',
|
|
||||||
onChange: jest.fn(),
|
|
||||||
onChangeThreads: jest.fn(),
|
|
||||||
onCollapseSection: jest.fn(),
|
|
||||||
onSubmit: jest.fn(),
|
|
||||||
onReset: jest.fn(),
|
|
||||||
isGM: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('normal channels', () => {
|
|
||||||
test('should match snapshot, DESKTOP on expanded view', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...baseProps}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, PUSH on expanded view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, MARK_UNREAD on expanded view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, DESKTOP on expanded view when mentions is selected', () => {
|
|
||||||
const props = {...baseProps, memberNotifyLevel: NotificationLevels.MENTION};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, PUSH on expanded view when mentions is selected', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH, memberNotifyLevel: NotificationLevels.MENTION};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('gms', () => {
|
|
||||||
test('should match snapshot, DESKTOP on expanded view when mentions is selected', () => {
|
|
||||||
const props = {...baseProps, isGM: true, memberNotifyLevel: NotificationLevels.MENTION};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, PUSH on expanded view when mentions is selected', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH, isGM: true, memberNotifyLevel: NotificationLevels.MENTION};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExpandView {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,437 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React, {useMemo, useRef} from 'react';
|
|
||||||
import type {ChangeEvent} from 'react';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {useSelector} from 'react-redux';
|
|
||||||
import ReactSelect from 'react-select';
|
|
||||||
import type {ValueType} from 'react-select';
|
|
||||||
|
|
||||||
import type {ChannelNotifyProps} from '@mattermost/types/channels';
|
|
||||||
|
|
||||||
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
|
|
||||||
|
|
||||||
import SettingItemMax from 'components/setting_item_max';
|
|
||||||
|
|
||||||
import {ChannelAutoFollowThreads, DesktopSound, IgnoreChannelMentions, NotificationLevels, NotificationSections} from 'utils/constants';
|
|
||||||
import {notificationSounds} from 'utils/notification_sounds';
|
|
||||||
|
|
||||||
import Describe from './describe';
|
|
||||||
import ExtraInfo from './extra_info';
|
|
||||||
import SectionTitle from './section_title';
|
|
||||||
|
|
||||||
type SelectedOption = {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
ignoreChannelMentions?: string;
|
|
||||||
channelAutoFollowThreads?: string;
|
|
||||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
onChangeThreads?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
onChangeDesktopSound?: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
onChangeNotificationSound?: (selectedOption: ValueType<SelectedOption>) => void;
|
|
||||||
onCollapseSection: (section: string) => void;
|
|
||||||
onReset: () => void;
|
|
||||||
onSubmit: (setting?: string) => void;
|
|
||||||
isNotificationsSettingSameAsGlobal?: boolean;
|
|
||||||
globalNotifyLevel?: string;
|
|
||||||
globalNotificationSound?: ChannelNotifyProps['desktop_notification_sound'];
|
|
||||||
memberNotifyLevel: string;
|
|
||||||
memberThreadsNotifyLevel?: string;
|
|
||||||
memberDesktopSound?: string;
|
|
||||||
memberDesktopNotificationSound?: string;
|
|
||||||
section: string;
|
|
||||||
serverError?: string;
|
|
||||||
isGM: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
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({
|
|
||||||
section,
|
|
||||||
memberNotifyLevel,
|
|
||||||
memberThreadsNotifyLevel,
|
|
||||||
memberDesktopSound,
|
|
||||||
memberDesktopNotificationSound,
|
|
||||||
globalNotifyLevel,
|
|
||||||
globalNotificationSound,
|
|
||||||
isNotificationsSettingSameAsGlobal,
|
|
||||||
onChange,
|
|
||||||
onChangeThreads,
|
|
||||||
onChangeDesktopSound,
|
|
||||||
onChangeNotificationSound,
|
|
||||||
onReset,
|
|
||||||
onSubmit,
|
|
||||||
serverError,
|
|
||||||
onCollapseSection,
|
|
||||||
ignoreChannelMentions,
|
|
||||||
channelAutoFollowThreads,
|
|
||||||
isGM,
|
|
||||||
}: Props) {
|
|
||||||
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 = [(
|
|
||||||
<div key='channel-notification-level-radio'>
|
|
||||||
{(section === NotificationSections.DESKTOP || section === NotificationSections.PUSH) &&
|
|
||||||
<fieldset>
|
|
||||||
{ section === NotificationSections.DESKTOP && <legend className='form-legend'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.sendDesktop'
|
|
||||||
defaultMessage='Send desktop notifications'
|
|
||||||
/>
|
|
||||||
</legend>}
|
|
||||||
{ section === NotificationSections.PUSH && <legend className='form-legend'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.sendMobilePush'
|
|
||||||
defaultMessage='Send mobile push notifications'
|
|
||||||
/>
|
|
||||||
</legend>}
|
|
||||||
<div className='radio'>
|
|
||||||
<label className=''>
|
|
||||||
<input
|
|
||||||
id='channelNotificationAllActivity'
|
|
||||||
name='channelNotifications'
|
|
||||||
type='radio'
|
|
||||||
value={NotificationLevels.ALL}
|
|
||||||
checked={memberNotifyLevel === NotificationLevels.ALL}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
memberNotifyLevel={NotificationLevels.ALL}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className='radio'>
|
|
||||||
<label className=''>
|
|
||||||
<input
|
|
||||||
id='channelNotificationMentions'
|
|
||||||
name='channelNotifications'
|
|
||||||
type='radio'
|
|
||||||
value={NotificationLevels.MENTION}
|
|
||||||
checked={memberNotifyLevel === NotificationLevels.MENTION}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
memberNotifyLevel={NotificationLevels.MENTION}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className='radio'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='channelNotificationNever'
|
|
||||||
name='channelNotifications'
|
|
||||||
type='radio'
|
|
||||||
value={NotificationLevels.NONE}
|
|
||||||
checked={memberNotifyLevel === NotificationLevels.NONE}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
memberNotifyLevel={NotificationLevels.NONE}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
}
|
|
||||||
{section === NotificationSections.IGNORE_CHANNEL_MENTIONS &&
|
|
||||||
<fieldset>
|
|
||||||
<div className='radio'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='ignoreChannelMentionsOn'
|
|
||||||
name='ignoreChannelMentions'
|
|
||||||
type='radio'
|
|
||||||
value={IgnoreChannelMentions.ON}
|
|
||||||
checked={ignoreChannelMentions === IgnoreChannelMentions.ON}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
ignoreChannelMentions={IgnoreChannelMentions.ON}
|
|
||||||
memberNotifyLevel={memberNotifyLevel}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className='radio'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='ignoreChannelMentionsOff'
|
|
||||||
name='ignoreChannelMentions'
|
|
||||||
type='radio'
|
|
||||||
value={IgnoreChannelMentions.OFF}
|
|
||||||
checked={ignoreChannelMentions === IgnoreChannelMentions.OFF}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
ignoreChannelMentions={IgnoreChannelMentions.OFF}
|
|
||||||
memberNotifyLevel={memberNotifyLevel}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
}
|
|
||||||
{section === NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS &&
|
|
||||||
<fieldset>
|
|
||||||
<div className='radio'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='channelAutoFollowThreadsOn'
|
|
||||||
name='channelAutoFollowThreads'
|
|
||||||
type='radio'
|
|
||||||
value={ChannelAutoFollowThreads.ON}
|
|
||||||
checked={channelAutoFollowThreads === ChannelAutoFollowThreads.ON}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
channelAutoFollowThreads={ChannelAutoFollowThreads.ON}
|
|
||||||
memberNotifyLevel={memberNotifyLevel}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className='radio'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='channelAutoFollowThreadsOff'
|
|
||||||
name='channelAutoFollowThreads'
|
|
||||||
type='radio'
|
|
||||||
value={ChannelAutoFollowThreads.OFF}
|
|
||||||
checked={channelAutoFollowThreads === ChannelAutoFollowThreads.OFF}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
channelAutoFollowThreads={ChannelAutoFollowThreads.OFF}
|
|
||||||
memberNotifyLevel={memberNotifyLevel}
|
|
||||||
globalNotifyLevel={globalNotifyLevel}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
}
|
|
||||||
{section === NotificationSections.MARK_UNREAD &&
|
|
||||||
<fieldset>
|
|
||||||
<div className='radio'>
|
|
||||||
<label className=''>
|
|
||||||
<input
|
|
||||||
id='channelNotificationUnmute'
|
|
||||||
name='channelNotificationMute'
|
|
||||||
type='radio'
|
|
||||||
value={NotificationLevels.MENTION}
|
|
||||||
checked={memberNotifyLevel === NotificationLevels.MENTION}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
memberNotifyLevel={NotificationLevels.MENTION}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className='radio'>
|
|
||||||
<label className=''>
|
|
||||||
<input
|
|
||||||
id='channelNotificationMute'
|
|
||||||
name='channelNotificationMute'
|
|
||||||
type='radio'
|
|
||||||
value={NotificationLevels.ALL}
|
|
||||||
checked={memberNotifyLevel === NotificationLevels.ALL}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
<Describe
|
|
||||||
section={section}
|
|
||||||
memberNotifyLevel={NotificationLevels.ALL}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
}
|
|
||||||
<div className='mt-5'>
|
|
||||||
<ExtraInfo section={section}/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isCRTEnabled &&
|
|
||||||
section === NotificationSections.DESKTOP &&
|
|
||||||
memberNotifyLevel === NotificationLevels.MENTION &&
|
|
||||||
!isGM &&
|
|
||||||
<>
|
|
||||||
<hr/>
|
|
||||||
<fieldset>
|
|
||||||
<legend className='form-legend'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.notifications.threads.desktop'
|
|
||||||
defaultMessage='Thread reply notifications'
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div className='checkbox'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='desktopThreadsNotificationAllActivity'
|
|
||||||
type='checkbox'
|
|
||||||
name='desktopThreadsNotificationLevel'
|
|
||||||
checked={memberThreadsNotifyLevel === NotificationLevels.ALL}
|
|
||||||
onChange={onChangeThreads}
|
|
||||||
/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.notifications.threads.allActivity'
|
|
||||||
defaultMessage={'Notify me about threads I\'m following'}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<br/>
|
|
||||||
</div>
|
|
||||||
<div className='mt-5'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.notifications.threads'
|
|
||||||
defaultMessage={'When enabled, any reply to a thread you\'re following will send a desktop notification.'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</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 &&
|
|
||||||
section === NotificationSections.PUSH &&
|
|
||||||
memberNotifyLevel === NotificationLevels.MENTION &&
|
|
||||||
!isGM &&
|
|
||||||
<>
|
|
||||||
<hr/>
|
|
||||||
<fieldset>
|
|
||||||
<legend className='form-legend'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.notifications.threads.push'
|
|
||||||
defaultMessage='Thread reply notifications'
|
|
||||||
/>
|
|
||||||
</legend>
|
|
||||||
<div className='checkbox'>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='pushThreadsNotificationAllActivity'
|
|
||||||
type='checkbox'
|
|
||||||
name='pushThreadsNotificationLevel'
|
|
||||||
checked={memberThreadsNotifyLevel === NotificationLevels.ALL}
|
|
||||||
onChange={onChangeThreads}
|
|
||||||
/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.notifications.push_threads.allActivity'
|
|
||||||
defaultMessage={'Notify me about threads I\'m following'}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<br/>
|
|
||||||
</div>
|
|
||||||
<div className='mt-5'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.notifications.push_threads'
|
|
||||||
defaultMessage={'When enabled, any reply to a thread you\'re following will send a mobile push notification.'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingItemMax
|
|
||||||
title={
|
|
||||||
<SectionTitle
|
|
||||||
section={section}
|
|
||||||
isExpanded={true}
|
|
||||||
isNotificationsSettingSameAsGlobal={isNotificationsSettingSameAsGlobal}
|
|
||||||
onClickResetButton={onReset}
|
|
||||||
/>}
|
|
||||||
inputs={inputs}
|
|
||||||
submit={onSubmit}
|
|
||||||
serverError={serverError}
|
|
||||||
updateSection={onCollapseSection}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import ExtraInfo from 'components/channel_notifications_modal/components/extra_info';
|
|
||||||
|
|
||||||
import {NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/ExtraInfo', () => {
|
|
||||||
const baseProps = {
|
|
||||||
section: NotificationSections.DESKTOP,
|
|
||||||
};
|
|
||||||
|
|
||||||
test('should match snapshot, on DESKTOP', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExtraInfo {...baseProps}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on PUSH', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExtraInfo {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on MARK_UNREAD', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<ExtraInfo {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,64 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
|
|
||||||
import {NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
section: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ExtraInfo({section}: Props) {
|
|
||||||
switch (section) {
|
|
||||||
case NotificationSections.DESKTOP:
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.override'
|
|
||||||
defaultMessage='Selecting an option other than the "default" will override the global notification settings. Desktop notifications are available on Firefox, Safari, and Chrome.'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case NotificationSections.PUSH:
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.overridePush'
|
|
||||||
defaultMessage='Selecting an option other than the "default" will override the global notification settings for mobile push notifications.'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case NotificationSections.MARK_UNREAD:
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.muteChannel.help'
|
|
||||||
defaultMessage='Muting turns off desktop, email and push notifications for this channel. The channel will not be marked as unread unless you are mentioned.'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case NotificationSections.IGNORE_CHANNEL_MENTIONS:
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.ignoreChannelMentions.help'
|
|
||||||
defaultMessage='When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel.'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
case NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS:
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.channelAutoFollowThreads.help'
|
|
||||||
defaultMessage='When enabled, you will auto-follow all new threads created in this channel unless you unfollow a thread explicitly.'
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import {NotificationSections, NotificationLevels} from 'utils/constants';
|
|
||||||
|
|
||||||
import CollapseView from './collapse_view';
|
|
||||||
import ExpandView from './expand_view';
|
|
||||||
|
|
||||||
export default class NotificationSection extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notification section
|
|
||||||
*/
|
|
||||||
section: PropTypes.string.isRequired,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expand if true, else collapse the section
|
|
||||||
*/
|
|
||||||
expand: PropTypes.bool.isRequired,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Member's desktop notification level
|
|
||||||
*/
|
|
||||||
memberNotificationLevel: PropTypes.string.isRequired,
|
|
||||||
|
|
||||||
memberDesktopSound: PropTypes.string,
|
|
||||||
|
|
||||||
memberDesktopNotificationSound: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Member's desktop_threads notification level
|
|
||||||
*/
|
|
||||||
memberThreadsNotificationLevel: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ignore channel-wide mentions @channel, @here and @all
|
|
||||||
*/
|
|
||||||
ignoreChannelMentions: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-follow all new threads in this channel
|
|
||||||
*/
|
|
||||||
channelAutoFollowThreads: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User's global notification level
|
|
||||||
*/
|
|
||||||
globalNotificationLevel: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User's global notification sound
|
|
||||||
*/
|
|
||||||
globalNotificationSound: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* onChange handles update of desktop notification level
|
|
||||||
*/
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* onChangeThreads handles update of desktop_threads notification level
|
|
||||||
*/
|
|
||||||
onChangeThreads: PropTypes.func,
|
|
||||||
|
|
||||||
onChangeDesktopSound: PropTypes.func,
|
|
||||||
|
|
||||||
onChangeNotificationSound: PropTypes.func,
|
|
||||||
|
|
||||||
onReset: PropTypes.func,
|
|
||||||
|
|
||||||
isNotificationsSettingSameAsGlobal: PropTypes.bool,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit function to save notification level
|
|
||||||
*/
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update function to to expand or collapse a section
|
|
||||||
*/
|
|
||||||
onUpdateSection: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error string from the server
|
|
||||||
*/
|
|
||||||
serverError: PropTypes.string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the preferences are those of a GM
|
|
||||||
*/
|
|
||||||
isGM: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
handleOnChange = (e) => {
|
|
||||||
this.props.onChange(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleOnChangeThreads = (e) => {
|
|
||||||
const value = e.target.checked ? NotificationLevels.ALL : NotificationLevels.MENTION;
|
|
||||||
|
|
||||||
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 = () => {
|
|
||||||
this.props.onUpdateSection(this.props.section);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleCollapseSection = () => {
|
|
||||||
this.props.onUpdateSection(NotificationSections.NONE);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
expand,
|
|
||||||
globalNotificationLevel,
|
|
||||||
globalNotificationSound,
|
|
||||||
memberNotificationLevel,
|
|
||||||
memberThreadsNotificationLevel,
|
|
||||||
memberDesktopSound,
|
|
||||||
memberDesktopNotificationSound,
|
|
||||||
ignoreChannelMentions,
|
|
||||||
isNotificationsSettingSameAsGlobal,
|
|
||||||
channelAutoFollowThreads,
|
|
||||||
onSubmit,
|
|
||||||
onReset,
|
|
||||||
section,
|
|
||||||
serverError,
|
|
||||||
isGM,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (expand) {
|
|
||||||
return (
|
|
||||||
<ExpandView
|
|
||||||
section={section}
|
|
||||||
memberNotifyLevel={memberNotificationLevel}
|
|
||||||
memberThreadsNotifyLevel={memberThreadsNotificationLevel}
|
|
||||||
memberDesktopSound={memberDesktopSound}
|
|
||||||
memberDesktopNotificationSound={memberDesktopNotificationSound}
|
|
||||||
globalNotifyLevel={globalNotificationLevel}
|
|
||||||
globalNotificationSound={globalNotificationSound}
|
|
||||||
ignoreChannelMentions={ignoreChannelMentions}
|
|
||||||
isNotificationsSettingSameAsGlobal={isNotificationsSettingSameAsGlobal}
|
|
||||||
channelAutoFollowThreads={channelAutoFollowThreads}
|
|
||||||
onChange={this.handleOnChange}
|
|
||||||
onReset={onReset}
|
|
||||||
onChangeThreads={this.handleOnChangeThreads}
|
|
||||||
onChangeDesktopSound={this.handleOnChangeDesktopSound}
|
|
||||||
onChangeNotificationSound={this.handleOnChangeNotificationSound}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
serverError={serverError}
|
|
||||||
onCollapseSection={this.handleCollapseSection}
|
|
||||||
isGM={isGM}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CollapseView
|
|
||||||
section={section}
|
|
||||||
onExpandSection={this.handleExpandSection}
|
|
||||||
memberNotifyLevel={memberNotificationLevel}
|
|
||||||
globalNotifyLevel={globalNotificationLevel}
|
|
||||||
ignoreChannelMentions={ignoreChannelMentions}
|
|
||||||
channelAutoFollowThreads={channelAutoFollowThreads}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import NotificationSection from 'components/channel_notifications_modal/components/notification_section.jsx';
|
|
||||||
|
|
||||||
import {NotificationLevels, NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/NotificationSection', () => {
|
|
||||||
const baseProps = {
|
|
||||||
section: NotificationSections.DESKTOP,
|
|
||||||
expand: false,
|
|
||||||
memberNotificationLevel: NotificationLevels.ALL,
|
|
||||||
memberThreadsNotificationLevel: NotificationLevels.ALL,
|
|
||||||
globalNotificationLevel: NotificationLevels.DEFAULT,
|
|
||||||
onChange: () => {}, //eslint-disable-line no-empty-function
|
|
||||||
onChangeThreads: () => {}, //eslint-disable-line no-empty-function
|
|
||||||
onReset: () => {},
|
|
||||||
onSubmit: () => {}, //eslint-disable-line no-empty-function
|
|
||||||
onUpdateSection: () => {}, //eslint-disable-line no-empty-function
|
|
||||||
serverError: '',
|
|
||||||
isGM: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
test('should match snapshot, DESKTOP on collapsed view', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...baseProps}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, DESKTOP on expanded view', () => {
|
|
||||||
const props = {...baseProps, expand: true};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, PUSH on collapsed view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, PUSH on expanded view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH, expand: true};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, MARK_UNREAD on collapsed view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD, globalNotificationLevel: null};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, MARK_UNREAD on expanded view', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD, expand: true, globalNotificationLevel: null};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should have called onChange when handleOnChange is called', () => {
|
|
||||||
const onChange = jest.fn();
|
|
||||||
const props = {...baseProps, expand: true, onChange};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
wrapper.instance().handleOnChange({target: {value: NotificationLevels.ALL}});
|
|
||||||
expect(onChange).toHaveBeenCalledTimes(1);
|
|
||||||
expect(onChange).toHaveBeenCalledWith(NotificationLevels.ALL);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should have called onUpdateSection when handleExpandSection is called', () => {
|
|
||||||
const onUpdateSection = jest.fn();
|
|
||||||
const props = {...baseProps, expand: true, onUpdateSection};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
wrapper.instance().handleExpandSection({preventDefault: jest.fn()});
|
|
||||||
expect(onUpdateSection).toHaveBeenCalledTimes(1);
|
|
||||||
expect(onUpdateSection).toHaveBeenCalledWith(NotificationSections.DESKTOP);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should have called onUpdateSection when handleCollapseSection is called', () => {
|
|
||||||
const onUpdateSection = jest.fn();
|
|
||||||
const props = {...baseProps, expand: true, onUpdateSection};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
wrapper.instance().handleCollapseSection({preventDefault: jest.fn()});
|
|
||||||
expect(onUpdateSection).toHaveBeenCalledTimes(1);
|
|
||||||
expect(onUpdateSection).toHaveBeenCalledWith(NotificationSections.NONE);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot on server error', () => {
|
|
||||||
const props = {...baseProps, serverError: 'server error occurred'};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<NotificationSection {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,15 +0,0 @@
|
|||||||
@import 'sass/utils/mixins';
|
|
||||||
|
|
||||||
.SectionTitle {
|
|
||||||
&__wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__resetButton {
|
|
||||||
@include button-style--none;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {shallow} from 'enzyme';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import SectionTitle from 'components/channel_notifications_modal/components/section_title';
|
|
||||||
|
|
||||||
import {NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
describe('components/channel_notifications_modal/ExtraInfo', () => {
|
|
||||||
const baseProps = {
|
|
||||||
section: NotificationSections.DESKTOP,
|
|
||||||
};
|
|
||||||
|
|
||||||
test('should match snapshot, on DESKTOP', () => {
|
|
||||||
const wrapper = shallow(
|
|
||||||
<SectionTitle {...baseProps}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on PUSH', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.PUSH};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<SectionTitle {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should match snapshot, on MARK_UNREAD', () => {
|
|
||||||
const props = {...baseProps, section: NotificationSections.MARK_UNREAD};
|
|
||||||
const wrapper = shallow(
|
|
||||||
<SectionTitle {...props}/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,71 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
|
|
||||||
import {NotificationSections} from 'utils/constants';
|
|
||||||
|
|
||||||
import './section_title.scss';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
section: string;
|
|
||||||
isExpanded?: boolean;
|
|
||||||
isNotificationsSettingSameAsGlobal?: boolean;
|
|
||||||
onClickResetButton?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SectionTitle({section, isExpanded, isNotificationsSettingSameAsGlobal, onClickResetButton}: Props) {
|
|
||||||
if (section === NotificationSections.DESKTOP || section === NotificationSections.PUSH) {
|
|
||||||
return (
|
|
||||||
<div className='SectionTitle__wrapper'>
|
|
||||||
{section === NotificationSections.DESKTOP &&
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.desktopNotifications'
|
|
||||||
defaultMessage='Desktop notifications'
|
|
||||||
/>}
|
|
||||||
|
|
||||||
{section === NotificationSections.PUSH &&
|
|
||||||
<FormattedMessage
|
|
||||||
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) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.muteChannel.settings'
|
|
||||||
defaultMessage='Mute Channel'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (section === NotificationSections.IGNORE_CHANNEL_MENTIONS) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.ignoreChannelMentions'
|
|
||||||
defaultMessage='Ignore mentions for @channel, @here and @all'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (section === NotificationSections.CHANNEL_AUTO_FOLLOW_THREADS) {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='channel_notifications.channelAutoFollowThreads'
|
|
||||||
defaultMessage='Auto-follow all new threads in this channel'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
@ -11,6 +11,9 @@ import type {ChannelNotifyProps} from '@mattermost/types/channels';
|
|||||||
import {updateChannelNotifyProps} from 'mattermost-redux/actions/channels';
|
import {updateChannelNotifyProps} from 'mattermost-redux/actions/channels';
|
||||||
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||||
|
import {
|
||||||
|
isCollapsedThreadsEnabled,
|
||||||
|
} from 'mattermost-redux/selectors/entities/preferences';
|
||||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store/index';
|
import type {GlobalState} from 'types/store/index';
|
||||||
@ -18,6 +21,7 @@ import type {GlobalState} from 'types/store/index';
|
|||||||
import ChannelNotificationsModal from './channel_notifications_modal';
|
import ChannelNotificationsModal from './channel_notifications_modal';
|
||||||
|
|
||||||
const mapStateToProps = (state: GlobalState) => ({
|
const mapStateToProps = (state: GlobalState) => ({
|
||||||
|
collapsedReplyThreads: isCollapsedThreadsEnabled(state),
|
||||||
channelMember: getMyCurrentChannelMembership(state),
|
channelMember: getMyCurrentChannelMembership(state),
|
||||||
sendPushNotifications: getConfig(state).SendPushNotifications === 'true',
|
sendPushNotifications: getConfig(state).SendPushNotifications === 'true',
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,276 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {FormattedMessage, defineMessages} from 'react-intl';
|
||||||
|
|
||||||
|
import type {ChannelNotifyProps} from '@mattermost/types/channels';
|
||||||
|
import type {UserNotifyProps} from '@mattermost/types/users';
|
||||||
|
|
||||||
|
import type {FieldsetCheckbox} from 'components/widgets/modals/components/checkbox_setting_item';
|
||||||
|
import type {FieldsetRadio} from 'components/widgets/modals/components/radio_setting_item';
|
||||||
|
|
||||||
|
import {NotificationLevels} from 'utils/constants';
|
||||||
|
|
||||||
|
export type ChannelMemberNotifyProps = Partial<ChannelNotifyProps> & Pick<UserNotifyProps, 'desktop_threads' | 'push_threads'>
|
||||||
|
|
||||||
|
const translations = defineMessages({
|
||||||
|
MuteAndIgnoreSectionTitle: {
|
||||||
|
id: 'channel_notifications.muteAndIgnore',
|
||||||
|
defaultMessage: 'Mute or ignore',
|
||||||
|
},
|
||||||
|
NotifyMeTitle: {
|
||||||
|
id: 'channel_notifications.NotifyMeTitle',
|
||||||
|
defaultMessage: 'Notify me about…',
|
||||||
|
},
|
||||||
|
ThreadsReplyTitle: {
|
||||||
|
id: 'channel_notifications.ThreadsReplyTitle',
|
||||||
|
defaultMessage: 'Thread reply notifications',
|
||||||
|
},
|
||||||
|
|
||||||
|
DesktopNotificationsSectionTitle: {
|
||||||
|
id: 'channel_notifications.desktopNotificationsTitle',
|
||||||
|
defaultMessage: 'Desktop Notifications',
|
||||||
|
},
|
||||||
|
|
||||||
|
DesktopNotificationsSectionDesc: {
|
||||||
|
id: 'channel_notifications.desktopNotificationsDesc',
|
||||||
|
defaultMessage: 'Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.',
|
||||||
|
},
|
||||||
|
|
||||||
|
MobileNotificationsSectionTitle: {
|
||||||
|
id: 'channel_notifications.mobileNotificationsTitle',
|
||||||
|
defaultMessage: 'Mobile Notifications',
|
||||||
|
},
|
||||||
|
|
||||||
|
MobileNotificationsSectionDesc: {
|
||||||
|
id: 'channel_notifications.mobileNotificationsDesc',
|
||||||
|
defaultMessage: 'Notification alerts are pushed to your mobile device when there is activity in Mattermost.',
|
||||||
|
},
|
||||||
|
|
||||||
|
MuteChannelDesc: {
|
||||||
|
id: 'channel_notifications.muteChannelDesc',
|
||||||
|
defaultMessage: 'Turns off notifications for this channel. You’ll still see badges if you’re mentioned.',
|
||||||
|
},
|
||||||
|
|
||||||
|
IgnoreMentionsDesc: {
|
||||||
|
id: 'channel_notifications.ignoreMentionsDesc',
|
||||||
|
defaultMessage: 'When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel',
|
||||||
|
},
|
||||||
|
|
||||||
|
MuteChannelInputFieldTitle: {
|
||||||
|
id: 'channel_notifications.muteChannelTitle',
|
||||||
|
defaultMessage: 'Mute channel',
|
||||||
|
},
|
||||||
|
|
||||||
|
DesktopReplyThreadsInputFieldTitle: {
|
||||||
|
id: 'channel_notifications.checkbox.threadsReplyTitle',
|
||||||
|
defaultMessage: 'Notify me about replies to threads I\'m following',
|
||||||
|
},
|
||||||
|
|
||||||
|
MobileReplyThreadsInputFieldTitle: {
|
||||||
|
id: 'channel_notifications.checkbox.threadsReplyTitle',
|
||||||
|
defaultMessage: 'Notify me about replies to threads I\'m following',
|
||||||
|
},
|
||||||
|
|
||||||
|
sameMobileSettingsDesktopInputFieldTitle: {
|
||||||
|
id: 'channel_notifications.checkbox.sameMobileSettingsDesktop',
|
||||||
|
defaultMessage: 'Use the same notification settings as desktop',
|
||||||
|
},
|
||||||
|
|
||||||
|
IgnoreMentionsInputFieldTitle: {
|
||||||
|
id: 'channel_notifications.ignoreMentionsTitle',
|
||||||
|
defaultMessage: 'Ignore mentions for @channel, @here and @all',
|
||||||
|
},
|
||||||
|
|
||||||
|
AutoFollowThreadsTitle: {
|
||||||
|
id: 'channel_notifications.autoFollowThreadsTitle',
|
||||||
|
defaultMessage: 'Follow all threads in this channel',
|
||||||
|
},
|
||||||
|
|
||||||
|
AutoFollowThreadsDesc: {
|
||||||
|
id: 'channel_notifications.autoFollowThreadsDesc',
|
||||||
|
defaultMessage: 'When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.',
|
||||||
|
},
|
||||||
|
|
||||||
|
AutoFollowThreadsInputFieldTitle: {
|
||||||
|
id: 'channel_notifications.checkbox.autoFollowThreadsTitle',
|
||||||
|
defaultMessage: 'Automatically follow threads in this channel',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const desktopNotificationInputFieldOptions = defineMessages({
|
||||||
|
allNewMessages: {
|
||||||
|
id: 'channel_notifications.desktopNotificationAllLabel',
|
||||||
|
defaultMessage: 'All new messages',
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
id: 'channel_notifications.desktopNotificationMentionLabel',
|
||||||
|
defaultMessage: 'Mentions, direct messages, and keywords only',
|
||||||
|
},
|
||||||
|
nothing: {
|
||||||
|
id: 'channel_notifications.desktopNotificationNothingLabel',
|
||||||
|
defaultMessage: 'Nothing',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mobileNotificationInputFieldOptions = defineMessages({
|
||||||
|
allNewMessages: {
|
||||||
|
id: 'channel_notifications.MobileNotificationAllLabel',
|
||||||
|
defaultMessage: 'All new messages',
|
||||||
|
},
|
||||||
|
mentions: {
|
||||||
|
id: 'channel_notifications.MobileNotificationMentionLabel',
|
||||||
|
defaultMessage: 'Mentions, direct messages, and keywords only',
|
||||||
|
},
|
||||||
|
nothing: {
|
||||||
|
id: 'channel_notifications.MobileNotificationNothingLabel',
|
||||||
|
defaultMessage: 'Nothing',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const MuteChannelInputFieldData: FieldsetCheckbox = {
|
||||||
|
title: translations.MuteChannelInputFieldTitle,
|
||||||
|
name: 'mute channel',
|
||||||
|
dataTestId: 'muteChannel',
|
||||||
|
};
|
||||||
|
|
||||||
|
const DesktopReplyThreadsInputFieldData: FieldsetCheckbox = {
|
||||||
|
title: translations.DesktopReplyThreadsInputFieldTitle,
|
||||||
|
name: 'desktop reply threads',
|
||||||
|
dataTestId: 'desktopReplyThreads',
|
||||||
|
};
|
||||||
|
|
||||||
|
const MobileReplyThreadsInputFieldData: FieldsetCheckbox = {
|
||||||
|
title: translations.MobileReplyThreadsInputFieldTitle,
|
||||||
|
name: 'mobile reply threads',
|
||||||
|
dataTestId: 'mobileReplyThreads',
|
||||||
|
};
|
||||||
|
|
||||||
|
const sameMobileSettingsDesktopInputFieldData: FieldsetCheckbox = {
|
||||||
|
title: translations.sameMobileSettingsDesktopInputFieldTitle,
|
||||||
|
name: 'same mobile settings as Desktop',
|
||||||
|
dataTestId: 'sameMobileSettingsDesktop',
|
||||||
|
};
|
||||||
|
|
||||||
|
const IgnoreMentionsInputFieldData: FieldsetCheckbox = {
|
||||||
|
title: translations.IgnoreMentionsInputFieldTitle,
|
||||||
|
name: 'ignore mentions',
|
||||||
|
dataTestId: 'ignoreMentions',
|
||||||
|
};
|
||||||
|
|
||||||
|
const AutoFollowThreadsInputFieldData: FieldsetCheckbox = {
|
||||||
|
title: translations.AutoFollowThreadsInputFieldTitle,
|
||||||
|
name: 'auto follow threads',
|
||||||
|
dataTestId: 'autoFollowThreads',
|
||||||
|
};
|
||||||
|
|
||||||
|
const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
|
||||||
|
return {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
dataTestId: `desktopNotification-${NotificationLevels.ALL}`,
|
||||||
|
title: desktopNotificationInputFieldOptions.allNewMessages,
|
||||||
|
name: `desktopNotification-${NotificationLevels.ALL}`,
|
||||||
|
key: `desktopNotification-${NotificationLevels.ALL}`,
|
||||||
|
value: NotificationLevels.ALL,
|
||||||
|
suffix: defaultOption === NotificationLevels.ALL ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.default'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>) : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataTestId: `desktopNotification-${NotificationLevels.MENTION}`,
|
||||||
|
title: desktopNotificationInputFieldOptions.mentions,
|
||||||
|
name: `desktopNotification-${NotificationLevels.MENTION}`,
|
||||||
|
key: `desktopNotification-${NotificationLevels.MENTION}`,
|
||||||
|
value: NotificationLevels.MENTION,
|
||||||
|
suffix: defaultOption === NotificationLevels.MENTION ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.default'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>) : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataTestId: `desktopNotification-${NotificationLevels.NONE}`,
|
||||||
|
title: desktopNotificationInputFieldOptions.nothing,
|
||||||
|
name: `desktopNotification-${NotificationLevels.NONE}`,
|
||||||
|
key: `desktopNotification-${NotificationLevels.NONE}`,
|
||||||
|
value: NotificationLevels.NONE,
|
||||||
|
suffix: defaultOption === NotificationLevels.NONE ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.default'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>) : undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
|
||||||
|
return {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
dataTestId: `MobileNotification-${NotificationLevels.ALL}`,
|
||||||
|
title: mobileNotificationInputFieldOptions.allNewMessages,
|
||||||
|
name: `MobileNotification-${NotificationLevels.ALL}`,
|
||||||
|
key: `MobileNotification-${NotificationLevels.ALL}`,
|
||||||
|
value: NotificationLevels.ALL,
|
||||||
|
suffix: defaultOption === NotificationLevels.ALL ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.default'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>) : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataTestId: `MobileNotification-${NotificationLevels.MENTION}`,
|
||||||
|
title: mobileNotificationInputFieldOptions.mentions,
|
||||||
|
name: `MobileNotification-${NotificationLevels.MENTION}`,
|
||||||
|
key: `MobileNotification-${NotificationLevels.MENTION}`,
|
||||||
|
value: NotificationLevels.MENTION,
|
||||||
|
suffix: defaultOption === NotificationLevels.MENTION ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.default'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>) : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataTestId: `MobileNotification-${NotificationLevels.NONE}`,
|
||||||
|
title: mobileNotificationInputFieldOptions.nothing,
|
||||||
|
name: `MobileNotification-${NotificationLevels.NONE}`,
|
||||||
|
key: `MobileNotification-${NotificationLevels.NONE}`,
|
||||||
|
value: NotificationLevels.NONE,
|
||||||
|
suffix: defaultOption === NotificationLevels.NONE ? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='channel_notifications.default'
|
||||||
|
defaultMessage='(default)'
|
||||||
|
/>) : undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const utils = {
|
||||||
|
desktopNotificationInputFieldData,
|
||||||
|
DesktopNotificationsSectionDesc: translations.DesktopNotificationsSectionDesc,
|
||||||
|
DesktopNotificationsSectionTitle: translations.DesktopNotificationsSectionTitle,
|
||||||
|
IgnoreMentionsDesc: translations.IgnoreMentionsDesc,
|
||||||
|
IgnoreMentionsInputFieldData,
|
||||||
|
mobileNotificationInputFieldData,
|
||||||
|
MobileNotificationsSectionDesc: translations.MobileNotificationsSectionDesc,
|
||||||
|
MobileNotificationsSectionTitle: translations.MobileNotificationsSectionTitle,
|
||||||
|
MuteAndIgnoreSectionTitle: translations.MuteAndIgnoreSectionTitle,
|
||||||
|
MuteChannelDesc: translations.MuteChannelDesc,
|
||||||
|
MuteChannelInputFieldData,
|
||||||
|
NotifyMeTitle: translations.NotifyMeTitle,
|
||||||
|
DesktopReplyThreadsInputFieldData,
|
||||||
|
ThreadsReplyTitle: translations.ThreadsReplyTitle,
|
||||||
|
MobileReplyThreadsInputFieldData,
|
||||||
|
AutoFollowThreadsTitle: translations.AutoFollowThreadsTitle,
|
||||||
|
AutoFollowThreadsDesc: translations.AutoFollowThreadsDesc,
|
||||||
|
AutoFollowThreadsInputFieldData,
|
||||||
|
sameMobileSettingsDesktopInputFieldData,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default utils;
|
@ -0,0 +1,152 @@
|
|||||||
|
.mm-modal-generic-section-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fieldset-checkbox-ctr {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fieldset-checkbox {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fieldset-radio {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label-radio {
|
||||||
|
display: flex;
|
||||||
|
width: fit-content;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
gap: 8px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: add styling for react select
|
||||||
|
//&__fieldset-react-select{
|
||||||
|
//}
|
||||||
|
|
||||||
|
&__input-checkbox {
|
||||||
|
width: 1.6rem;
|
||||||
|
height: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
display: grid;
|
||||||
|
border: 1px solid rgba(var(--center-channel-color-rgb), 0.24);
|
||||||
|
margin: 0;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.24);
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
place-content: center;
|
||||||
|
-webkit-transition: background-color 200ms ease-out;
|
||||||
|
-moz-transition: background-color 200ms ease-out;
|
||||||
|
-o-transition: background-color 200ms ease-out;
|
||||||
|
transition: background-color 200ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]::before {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background: var(--button-color);
|
||||||
|
clip-path: polygon(0% 57%, 32% 88%, 100% 20%, 88% 8%, 32% 64%, 12% 45%);
|
||||||
|
content: '';
|
||||||
|
transform: scale(0);
|
||||||
|
transform-origin: center center;
|
||||||
|
transition: 200ms transform ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked::before {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked {
|
||||||
|
border-color: var(--denim-button-bg);
|
||||||
|
background: var(--denim-button-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=radio] {
|
||||||
|
display: grid;
|
||||||
|
width: 1.6rem;
|
||||||
|
height: 1.6rem;
|
||||||
|
border: 1px solid rgba(var(--center-channel-color-rgb), 0.24);
|
||||||
|
margin: 0;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.24);
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
place-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]::before {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: var(--denim-button-bg);
|
||||||
|
border-radius: 50%;
|
||||||
|
content: '';
|
||||||
|
transform: scale(0);
|
||||||
|
transform-origin: center center;
|
||||||
|
transition: 200ms transform ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:checked::before {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:checked {
|
||||||
|
border-color: var(--denim-button-bg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import type {MessageDescriptor} from 'react-intl';
|
||||||
|
import {useIntl} from 'react-intl';
|
||||||
|
|
||||||
|
import './base_setting_item.scss';
|
||||||
|
|
||||||
|
export type BaseSettingItemProps = {
|
||||||
|
title?: MessageDescriptor;
|
||||||
|
description?: MessageDescriptor;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = BaseSettingItemProps & {
|
||||||
|
content: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function BaseSettingItem({title, description, content}: Props): JSX.Element {
|
||||||
|
const {formatMessage} = useIntl();
|
||||||
|
const Title = title && (
|
||||||
|
<h4 className='mm-modal-generic-section-item__title'>
|
||||||
|
{formatMessage({id: title.id, defaultMessage: title.defaultMessage})}
|
||||||
|
</h4>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Description = description && (
|
||||||
|
<p className='mm-modal-generic-section-item__description'>
|
||||||
|
{formatMessage({id: description.id, defaultMessage: description.defaultMessage})}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mm-modal-generic-section-item'>
|
||||||
|
{Title}
|
||||||
|
<div className='mm-modal-generic-section-item__content'>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
{Description}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseSettingItem;
|
||||||
|
|
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import type {MessageDescriptor} from 'react-intl';
|
||||||
|
|
||||||
|
import type {BaseSettingItemProps} from './base_setting_item';
|
||||||
|
import BaseSettingItem from './base_setting_item';
|
||||||
|
|
||||||
|
export type FieldsetCheckbox = {
|
||||||
|
dataTestId?: string;
|
||||||
|
title: MessageDescriptor;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = BaseSettingItemProps & {
|
||||||
|
inputFieldData: FieldsetCheckbox;
|
||||||
|
inputFieldValue: boolean;
|
||||||
|
handleChange: (e: boolean) => void;
|
||||||
|
}
|
||||||
|
function CheckboxSettingItem({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
inputFieldData,
|
||||||
|
inputFieldValue,
|
||||||
|
handleChange,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const content = (
|
||||||
|
<fieldset
|
||||||
|
key={inputFieldData.name}
|
||||||
|
className='mm-modal-generic-section-item__fieldset-checkbox-ctr'
|
||||||
|
>
|
||||||
|
<label className='mm-modal-generic-section-item__fieldset-checkbox'>
|
||||||
|
<input
|
||||||
|
className='mm-modal-generic-section-item__input-checkbox'
|
||||||
|
data-testid={inputFieldData.dataTestId}
|
||||||
|
type='checkbox'
|
||||||
|
name={inputFieldData.name}
|
||||||
|
checked={inputFieldValue}
|
||||||
|
onChange={(e) => handleChange(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id={inputFieldData.title.id}
|
||||||
|
defaultMessage={inputFieldData.title.defaultMessage}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<BaseSettingItem
|
||||||
|
content={content}
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckboxSettingItem;
|
@ -0,0 +1,84 @@
|
|||||||
|
.mm-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 20px 20px 32px;
|
||||||
|
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
background: none;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-family: 'Metropolis', serif;
|
||||||
|
font-size: 22px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__vertical-divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 24px;
|
||||||
|
background: rgba(var(--center-channel-color-rgb), 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__ctr {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close-btn {
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: unset;
|
||||||
|
margin-left: 8px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
background: rgba(var(--center-channel-color-rgb), 0.08);
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.72);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
padding: 18px 24px;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__vertical-divider {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {CloseIcon} from '@mattermost/compass-icons/components';
|
||||||
|
import './modal_header.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
handleClose?: (e: React.MouseEvent) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ModalHeader({id, title, subtitle, handleClose}: Props) {
|
||||||
|
return (
|
||||||
|
<header className='mm-modal-header'>
|
||||||
|
<h1
|
||||||
|
id={`mm-modal-header-${id}`}
|
||||||
|
className='mm-modal-header__title'
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
<div className='mm-modal-header__vertical-divider'/>
|
||||||
|
<p className='mm-modal-header__subtitle'>{subtitle}</p>
|
||||||
|
<div
|
||||||
|
className='mm-modal-header__ctr'
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<button className='style--none mm-modal-header__close-btn'>
|
||||||
|
<CloseIcon
|
||||||
|
size={24}
|
||||||
|
color={'currentcolor'}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default ModalHeader;
|
@ -0,0 +1,50 @@
|
|||||||
|
.mm-modal-generic-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
&__info-ctr {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--center-channel-color);
|
||||||
|
font-family: 'Metropolis', sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import type {MessageDescriptor} from 'react-intl';
|
||||||
|
import {useIntl} from 'react-intl';
|
||||||
|
|
||||||
|
import './modal_section.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title: MessageDescriptor;
|
||||||
|
description?: MessageDescriptor;
|
||||||
|
content: JSX.Element;
|
||||||
|
titleSuffix?: JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ModalSection({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
content,
|
||||||
|
titleSuffix,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const {formatMessage} = useIntl();
|
||||||
|
const titleContent = (
|
||||||
|
<h4 className='mm-modal-generic-section__title'>
|
||||||
|
{formatMessage({id: title.id, defaultMessage: title.defaultMessage})}
|
||||||
|
</h4>
|
||||||
|
);
|
||||||
|
|
||||||
|
const descriptionContent = description && (
|
||||||
|
<p className='mm-modal-generic-section__description'>
|
||||||
|
{formatMessage({id: description.id, defaultMessage: description.defaultMessage})}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
|
||||||
|
function titleRow() {
|
||||||
|
if (titleSuffix) {
|
||||||
|
return (<div className='mm-modal-generic-section__row'>
|
||||||
|
{titleContent}
|
||||||
|
{titleSuffix}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
return titleContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className='mm-modal-generic-section'>
|
||||||
|
<div className='mm-modal-generic-section__info-ctr'>
|
||||||
|
{titleRow()}
|
||||||
|
{descriptionContent}
|
||||||
|
</div>
|
||||||
|
<div className='mm-modal-generic-section__content'>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalSection;
|
@ -0,0 +1,35 @@
|
|||||||
|
.mm-modal-sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
width: 184px;
|
||||||
|
height: 32px;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(var(--denim-button-bg-rgb), 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item--active {
|
||||||
|
background: rgba(var(--denim-button-bg-rgb), 0.08);
|
||||||
|
color: var(--denim-button-bg);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import './modal_sidebar.scss';
|
||||||
|
|
||||||
|
export type Tab = {
|
||||||
|
icon: JSX.Element;
|
||||||
|
name: string;
|
||||||
|
uiName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
activeTab?: string;
|
||||||
|
tabs: Tab[];
|
||||||
|
updateTab: (name: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ModalSidebar({tabs, activeTab, updateTab}: Props) {
|
||||||
|
const handleClick = (tab: Tab, e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
updateTab(tab.name);
|
||||||
|
(e.target as Element).closest('.settings-modal')?.classList.add('display--content');
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabList = tabs.map((tab) => {
|
||||||
|
const key = `${tab.name}_li`;
|
||||||
|
let className = 'mm-modal-sidebar__item';
|
||||||
|
if (activeTab === tab.name) {
|
||||||
|
className += ' mm-modal-sidebar__item--active';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
id={`${tab.name}Li`}
|
||||||
|
key={key}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
id={`${tab.name}Button`}
|
||||||
|
className={className}
|
||||||
|
onClick={handleClick.bind(null, tab)}
|
||||||
|
aria-label={tab.uiName.toLowerCase()}
|
||||||
|
>
|
||||||
|
{tab.icon}
|
||||||
|
{tab.uiName}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul
|
||||||
|
id='tabList'
|
||||||
|
className='mm-modal-sidebar'
|
||||||
|
>
|
||||||
|
{tabList}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default ModalSidebar;
|
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import type {MessageDescriptor} from 'react-intl';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import type {BaseSettingItemProps} from './base_setting_item';
|
||||||
|
import BaseSettingItem from './base_setting_item';
|
||||||
|
|
||||||
|
export type FieldsetRadio = {
|
||||||
|
options: Array<{
|
||||||
|
dataTestId?: string;
|
||||||
|
title: MessageDescriptor;
|
||||||
|
name: string;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
suffix?: JSX.Element;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = BaseSettingItemProps & {
|
||||||
|
inputFieldData: FieldsetRadio;
|
||||||
|
inputFieldValue: string;
|
||||||
|
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
}
|
||||||
|
function RadioSettingItem({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
inputFieldData,
|
||||||
|
inputFieldValue,
|
||||||
|
handleChange,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const fields = inputFieldData.options.map((option) => {
|
||||||
|
return (
|
||||||
|
<label
|
||||||
|
key={option.key}
|
||||||
|
className='mm-modal-generic-section-item__label-radio'
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id={option.key}
|
||||||
|
data-testid={option.dataTestId}
|
||||||
|
type='radio'
|
||||||
|
name={option.name}
|
||||||
|
checked={option.value === inputFieldValue}
|
||||||
|
value={option.value}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id={option.title.id}
|
||||||
|
defaultMessage={option.title.defaultMessage}
|
||||||
|
/>
|
||||||
|
{option.suffix}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<fieldset className='mm-modal-generic-section-item__fieldset-radio'>
|
||||||
|
{[...fields]}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<BaseSettingItem
|
||||||
|
content={content}
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RadioSettingItem;
|
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import type {ValueType} from 'react-select';
|
||||||
|
import ReactSelect from 'react-select';
|
||||||
|
|
||||||
|
import type {BaseSettingItemProps} from './base_setting_item';
|
||||||
|
import BaseSettingItem from './base_setting_item';
|
||||||
|
|
||||||
|
export type Option = {
|
||||||
|
value: number;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FieldsetReactSelect = {
|
||||||
|
dataTestId?: string;
|
||||||
|
options: Option[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = BaseSettingItemProps & {
|
||||||
|
inputFieldData: FieldsetReactSelect;
|
||||||
|
inputFieldValue: Option;
|
||||||
|
handleChange: (selected: ValueType<Option>) => void;
|
||||||
|
}
|
||||||
|
function ReactSelectItemCreator({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
inputFieldData,
|
||||||
|
inputFieldValue,
|
||||||
|
handleChange,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const content = (
|
||||||
|
<fieldset className='mm-modal-generic-section-item__fieldset-react-select'>
|
||||||
|
<ReactSelect
|
||||||
|
className='react-select'
|
||||||
|
classNamePrefix='react-select'
|
||||||
|
id='limitVisibleGMsDMs'
|
||||||
|
options={inputFieldData.options}
|
||||||
|
clearable={false}
|
||||||
|
onChange={handleChange}
|
||||||
|
value={inputFieldValue}
|
||||||
|
isSearchable={false}
|
||||||
|
menuPortalTarget={document.body}
|
||||||
|
styles={reactStyles}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<BaseSettingItem
|
||||||
|
content={content}
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactSelectItemCreator;
|
||||||
|
|
||||||
|
const reactStyles = {
|
||||||
|
menuPortal: (provided: React.CSSProperties) => ({
|
||||||
|
...provided,
|
||||||
|
zIndex: 9999,
|
||||||
|
}),
|
||||||
|
};
|
@ -0,0 +1,68 @@
|
|||||||
|
.mm-save-changes-panel {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
width: calc(100% - 232px - 40px);
|
||||||
|
align-items: center;
|
||||||
|
padding: 18px;
|
||||||
|
background-color: var(--dnd-indicator);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: var(--elevation-3);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--sidebar-text);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__btn-ctr {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cancel-btn {
|
||||||
|
display: flex;
|
||||||
|
width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--sidebar-text);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
gap: 10px;
|
||||||
|
line-height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__save-btn {
|
||||||
|
display: flex;
|
||||||
|
width: 59px;
|
||||||
|
height: 32px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border: none;
|
||||||
|
background: var(--sidebar-text);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--denim-button-bg);
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
gap: 10px;
|
||||||
|
line-height: 10px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
import './save_changes_panel.scss';
|
||||||
|
import {AlertCircleOutlineIcon} from '@mattermost/compass-icons/components';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
handleSubmit: () => void;
|
||||||
|
handleCancel: () => void;
|
||||||
|
}
|
||||||
|
function SaveChangesPanel({handleSubmit, handleCancel}: Props) {
|
||||||
|
return (
|
||||||
|
<div className='mm-save-changes-panel'>
|
||||||
|
<p className='mm-save-changes-panel__message'>
|
||||||
|
<AlertCircleOutlineIcon
|
||||||
|
size={18}
|
||||||
|
color={'currentcolor'}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='saveChangesPanel.message'
|
||||||
|
defaultMessage='You have unsaved changes'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<div className='mm-save-changes-panel__btn-ctr'>
|
||||||
|
<button
|
||||||
|
className='mm-save-changes-panel__cancel-btn'
|
||||||
|
onClick={handleCancel}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='saveChangesPanel.cancel'
|
||||||
|
defaultMessage='Undo'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className='mm-save-changes-panel__save-btn'
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='saveChangesPanel.save'
|
||||||
|
defaultMessage='Save'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default SaveChangesPanel;
|
@ -2998,40 +2998,34 @@
|
|||||||
"channel_modal.type.private.title": "Private Channel",
|
"channel_modal.type.private.title": "Private Channel",
|
||||||
"channel_modal.type.public.description": "Anyone can join",
|
"channel_modal.type.public.description": "Anyone can join",
|
||||||
"channel_modal.type.public.title": "Public Channel",
|
"channel_modal.type.public.title": "Public Channel",
|
||||||
"channel_notifications.allActivity": "For all activity {isDefault}",
|
"channel_notifications.alertBanner.description": "All other notification preferences for this channel are disabled",
|
||||||
"channel_notifications.channelAutoFollowThreads": "Auto-follow all new threads in this channel",
|
"channel_notifications.alertBanner.title": "This channel is muted",
|
||||||
"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.autoFollowThreadsDesc": "When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.",
|
||||||
"channel_notifications.channelAutoFollowThreads.off.title": "Off",
|
"channel_notifications.autoFollowThreadsTitle": "Follow all threads in this channel",
|
||||||
"channel_notifications.channelAutoFollowThreads.on.title": "On",
|
"channel_notifications.checkbox.autoFollowThreadsTitle": "Automatically follow threads in this channel",
|
||||||
"channel_notifications.defaultOption": "(default)",
|
"channel_notifications.checkbox.sameMobileSettingsDesktop": "Use the same notification settings as desktop",
|
||||||
"channel_notifications.desktopNotifications": "Desktop notifications",
|
"channel_notifications.checkbox.threadsReplyTitle": "Notify me about replies to threads I’m following",
|
||||||
"channel_notifications.globalDefault": "Global default ({notifyLevel})",
|
"channel_notifications.default": "(default)",
|
||||||
"channel_notifications.ignoreChannelMentions": "Ignore mentions for @channel, @here and @all",
|
"channel_notifications.desktopNotificationAllLabel": "All new messages",
|
||||||
"channel_notifications.ignoreChannelMentions.help": "When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel.",
|
"channel_notifications.desktopNotificationMentionLabel": "Mentions, direct messages, and keywords only",
|
||||||
"channel_notifications.ignoreChannelMentions.off.title": "Off",
|
"channel_notifications.desktopNotificationNothingLabel": "Nothing",
|
||||||
"channel_notifications.ignoreChannelMentions.on.title": "On",
|
"channel_notifications.desktopNotificationsDesc": "Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.",
|
||||||
|
"channel_notifications.desktopNotificationsTitle": "Desktop Notifications",
|
||||||
|
"channel_notifications.ignoreMentionsDesc": "When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel",
|
||||||
|
"channel_notifications.ignoreMentionsTitle": "Ignore mentions for @channel, @here and @all",
|
||||||
"channel_notifications.levels.all": "All",
|
"channel_notifications.levels.all": "All",
|
||||||
"channel_notifications.levels.default": "Default",
|
"channel_notifications.MobileNotificationAllLabel": "All new messages",
|
||||||
"channel_notifications.levels.mention": "Mention",
|
"channel_notifications.MobileNotificationMentionLabel": "Mentions, direct messages, and keywords only",
|
||||||
"channel_notifications.levels.none": "None",
|
"channel_notifications.MobileNotificationNothingLabel": "Nothing",
|
||||||
"channel_notifications.muteChannel.help": "Muting turns off desktop, email and push notifications for this channel. The channel will not be marked as unread unless you're mentioned.",
|
"channel_notifications.mobileNotificationsDesc": "Notification alerts are pushed to your mobile device when there is activity in Mattermost.",
|
||||||
"channel_notifications.muteChannel.off.title": "Off",
|
"channel_notifications.mobileNotificationsTitle": "Mobile Notifications",
|
||||||
"channel_notifications.muteChannel.on.title": "On",
|
"channel_notifications.muteAndIgnore": "Mute or ignore",
|
||||||
"channel_notifications.muteChannel.on.title.collapse": "Mute is enabled. Desktop, email and push notifications will not be sent for this channel.",
|
"channel_notifications.muteChannelDesc": "Turns off notifications for this channel. You’ll still see badges if you’re mentioned.",
|
||||||
"channel_notifications.muteChannel.settings": "Mute channel",
|
"channel_notifications.muteChannelTitle": "Mute channel",
|
||||||
"channel_notifications.never": "Never {isDefault}",
|
"channel_notifications.NotifyMeTitle": "Notify me about…",
|
||||||
"channel_notifications.onlyMentions": "Only for mentions {isDefault}",
|
"channel_notifications.preferences": "Notification Preferences",
|
||||||
"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.resetToDefault": "Reset to default",
|
||||||
"channel_notifications.overridePush": "Selecting an option other than the \"default\" will override the global notification settings for mobile push notifications. ",
|
"channel_notifications.ThreadsReplyTitle": "Thread reply notifications",
|
||||||
"channel_notifications.preferences": "Notification Preferences for ",
|
|
||||||
"channel_notifications.push": "Mobile push 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",
|
||||||
@ -3564,6 +3558,8 @@
|
|||||||
"general_tab.teamNameRestrictions": "Team Name must be {min} or more characters up to a maximum of {max}. You can add a longer team description.",
|
"general_tab.teamNameRestrictions": "Team Name must be {min} or more characters up to a maximum of {max}. You can add a longer team description.",
|
||||||
"general_tab.title": "General Settings",
|
"general_tab.title": "General Settings",
|
||||||
"general_tab.yes": "Yes",
|
"general_tab.yes": "Yes",
|
||||||
|
"generic_btn.cancel": "Cancel",
|
||||||
|
"generic_btn.save": "Save",
|
||||||
"generic_icons.add": "Add Icon",
|
"generic_icons.add": "Add Icon",
|
||||||
"generic_icons.add-mail": "Add Mail Icon",
|
"generic_icons.add-mail": "Add Mail Icon",
|
||||||
"generic_icons.add-reaction": "Add Reaction Icon",
|
"generic_icons.add-reaction": "Add Reaction Icon",
|
||||||
@ -4587,6 +4583,9 @@
|
|||||||
"rhs_thread.toast.newReplies": "New Replies",
|
"rhs_thread.toast.newReplies": "New Replies",
|
||||||
"save_button.save": "Save",
|
"save_button.save": "Save",
|
||||||
"save_button.saving": "Saving",
|
"save_button.saving": "Saving",
|
||||||
|
"saveChangesPanel.cancel": "Undo",
|
||||||
|
"saveChangesPanel.message": "You have unsaved changes",
|
||||||
|
"saveChangesPanel.save": "Save",
|
||||||
"search_bar.files_tab": "Files",
|
"search_bar.files_tab": "Files",
|
||||||
"search_bar.messages_tab": "Messages",
|
"search_bar.messages_tab": "Messages",
|
||||||
"search_bar.search": "Search",
|
"search_bar.search": "Search",
|
||||||
|
@ -17,12 +17,12 @@ import type {Scheme} from '@mattermost/types/schemes';
|
|||||||
import type {Team, TeamMembership} from '@mattermost/types/teams';
|
import type {Team, TeamMembership} from '@mattermost/types/teams';
|
||||||
import type {UserProfile, UserNotifyProps} from '@mattermost/types/users';
|
import type {UserProfile, UserNotifyProps} from '@mattermost/types/users';
|
||||||
|
|
||||||
import General from 'mattermost-redux/constants/general';
|
|
||||||
import {generateId} from 'mattermost-redux/utils/helpers';
|
|
||||||
|
|
||||||
export const DEFAULT_SERVER = 'http://localhost:8065';
|
export const DEFAULT_SERVER = 'http://localhost:8065';
|
||||||
const PASSWORD = 'password1';
|
const PASSWORD = 'password1';
|
||||||
|
|
||||||
|
import General from 'mattermost-redux/constants/general';
|
||||||
|
import {generateId} from 'mattermost-redux/utils/helpers';
|
||||||
|
|
||||||
const {DEFAULT_LOCALE} = General;
|
const {DEFAULT_LOCALE} = General;
|
||||||
|
|
||||||
class TestHelper {
|
class TestHelper {
|
||||||
@ -452,6 +452,8 @@ class TestHelper {
|
|||||||
return {
|
return {
|
||||||
desktop: 'default',
|
desktop: 'default',
|
||||||
desktop_sound: 'off',
|
desktop_sound: 'off',
|
||||||
|
desktop_threads: 'default',
|
||||||
|
push_threads: 'default',
|
||||||
email: 'default',
|
email: 'default',
|
||||||
mark_unread: 'mention',
|
mark_unread: 'mention',
|
||||||
push: 'default',
|
push: 'default',
|
||||||
|
@ -20,12 +20,14 @@ export type ChannelStats = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ChannelNotifyProps = {
|
export type ChannelNotifyProps = {
|
||||||
|
desktop_threads: 'default' | 'all' | 'mention' | 'none';
|
||||||
desktop: 'default' | 'all' | 'mention' | 'none';
|
desktop: 'default' | 'all' | 'mention' | 'none';
|
||||||
desktop_sound: 'on' | 'off';
|
desktop_sound: 'on' | 'off';
|
||||||
desktop_notification_sound?: 'Bing' | 'Crackle' | 'Down' | 'Hello' | 'Ripple' | 'Upstairs';
|
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';
|
||||||
|
push_threads: 'default' | 'all' | 'mention' | 'none';
|
||||||
ignore_channel_mentions: 'default' | 'off' | 'on';
|
ignore_channel_mentions: 'default' | 'off' | 'on';
|
||||||
channel_auto_follow_threads: 'off' | 'on';
|
channel_auto_follow_threads: 'off' | 'on';
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user