[MM-56695] Consolidate desktop and mobile Notifications to one and a new desktop notification sound settings (#26198)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
a81ee87832
Коммит
9698cfcc19
@@ -11,7 +11,7 @@ exports[`components/SettingItemMax should match snapshot 1`] = `
|
||||
title
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -72,7 +72,7 @@ exports[`components/SettingItemMax should match snapshot, on clientError 1`] = `
|
||||
title
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -143,7 +143,7 @@ exports[`components/SettingItemMax should match snapshot, on serverError 1`] = `
|
||||
title
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -214,7 +214,7 @@ exports[`components/SettingItemMax should match snapshot, with new saveTextButto
|
||||
title
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -270,7 +270,7 @@ exports[`components/SettingItemMax should match snapshot, without submit 1`] = `
|
||||
title
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-12"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import type {ReactNode} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
@@ -44,7 +45,7 @@ type Props = {
|
||||
submitExtra?: ReactNode;
|
||||
saving?: boolean;
|
||||
title?: ReactNode;
|
||||
width?: string;
|
||||
isFullWidth?: boolean;
|
||||
cancelButtonText?: ReactNode;
|
||||
shiftEnter?: boolean;
|
||||
saveButtonText?: string;
|
||||
@@ -177,14 +178,6 @@ export default class SettingItemMax extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
const inputs = this.props.inputs;
|
||||
let widthClass;
|
||||
if (this.props.width === 'full') {
|
||||
widthClass = 'col-sm-12';
|
||||
} else if (this.props.width === 'medium') {
|
||||
widthClass = 'col-sm-10 col-sm-offset-2';
|
||||
} else {
|
||||
widthClass = 'col-sm-9 col-sm-offset-3';
|
||||
}
|
||||
|
||||
let title;
|
||||
if (this.props.title) {
|
||||
@@ -231,7 +224,12 @@ export default class SettingItemMax extends React.PureComponent<Props> {
|
||||
className={`section-max form-horizontal ${this.props.containerStyle}`}
|
||||
>
|
||||
{title}
|
||||
<div className={widthClass}>
|
||||
<div
|
||||
className={classNames('sectionContent', {
|
||||
'col-sm-12': this.props.isFullWidth,
|
||||
'col-sm-10 col-sm-offset-2': !this.props.isFullWidth,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
ref={this.settingList}
|
||||
|
||||
@@ -256,7 +256,6 @@ export class ManageLanguage extends React.PureComponent<Props, State> {
|
||||
defaultMessage='Language'
|
||||
/>
|
||||
}
|
||||
width='medium'
|
||||
submit={this.changeLanguage}
|
||||
saving={this.state.isSaving}
|
||||
inputs={[input]}
|
||||
|
||||
@@ -245,7 +245,6 @@ export default class ManageTimezones extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
}
|
||||
containerStyle='timezone-container'
|
||||
width='medium'
|
||||
submit={this.changeTimezone}
|
||||
saving={this.state.isSaving}
|
||||
inputs={inputs}
|
||||
|
||||
@@ -309,7 +309,7 @@ export default class ThemeSetting extends React.PureComponent<Props, State> {
|
||||
disableEnterSubmit={true}
|
||||
saving={this.state.isSaving}
|
||||
serverError={serverError}
|
||||
width='full'
|
||||
isFullWidth={true}
|
||||
updateSection={this.handleUpdateSection}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -34,7 +34,7 @@ Object {
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
Notification Settings
|
||||
Notification settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
@@ -88,15 +88,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopTitle"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop Notifications
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopTitle desktopEdit"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopEdit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -107,9 +107,44 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopDesc"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
For all activity, without sound
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopNotificationSoundTitle"
|
||||
>
|
||||
Desktop notification sounds
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopNotificationSoundTitle desktopNotificationSoundEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopNotificationSoundEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopNotificationSoundDesc"
|
||||
>
|
||||
No sound
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -125,7 +160,7 @@ Object {
|
||||
class="section-min__title"
|
||||
id="emailTitle"
|
||||
>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
@@ -158,15 +193,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="pushTitle"
|
||||
id="keywordsAndMentionsTitle"
|
||||
>
|
||||
Mobile Push Notifications
|
||||
Keywords that trigger notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="pushTitle pushEdit"
|
||||
aria-labelledby="keywordsAndMentionsTitle keywordsAndMentionsEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="pushEdit"
|
||||
id="keywordsAndMentionsEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -177,42 +212,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="pushDesc"
|
||||
>
|
||||
Never
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithNotificationTitle"
|
||||
>
|
||||
Keywords That Trigger Notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithNotificationTitle keysWithNotificationEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithNotificationEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithNotificationDesc"
|
||||
id="keywordsAndMentionsDesc"
|
||||
>
|
||||
"@some-user"
|
||||
</div>
|
||||
@@ -228,15 +228,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithHighlightTitle"
|
||||
id="keywordsAndHighlightTitle"
|
||||
>
|
||||
Keywords That Get Highlighted (Without Notifications)
|
||||
Keywords that get highlighted (without notifications)
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithHighlightTitle keysWithHighlightEdit"
|
||||
aria-labelledby="keywordsAndHighlightTitle keywordsAndHighlightEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithHighlightEdit"
|
||||
id="keywordsAndHighlightEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -247,7 +247,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithHighlightDesc"
|
||||
id="keywordsAndHighlightDesc"
|
||||
>
|
||||
None
|
||||
</div>
|
||||
@@ -292,7 +292,7 @@ Object {
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
Notification Settings
|
||||
Notification settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
@@ -346,15 +346,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopTitle"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop Notifications
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopTitle desktopEdit"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopEdit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -365,9 +365,44 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopDesc"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
For all activity, without sound
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopNotificationSoundTitle"
|
||||
>
|
||||
Desktop notification sounds
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopNotificationSoundTitle desktopNotificationSoundEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopNotificationSoundEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopNotificationSoundDesc"
|
||||
>
|
||||
No sound
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -383,7 +418,7 @@ Object {
|
||||
class="section-min__title"
|
||||
id="emailTitle"
|
||||
>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
@@ -416,15 +451,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="pushTitle"
|
||||
id="keywordsAndMentionsTitle"
|
||||
>
|
||||
Mobile Push Notifications
|
||||
Keywords that trigger notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="pushTitle pushEdit"
|
||||
aria-labelledby="keywordsAndMentionsTitle keywordsAndMentionsEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="pushEdit"
|
||||
id="keywordsAndMentionsEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -435,42 +470,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="pushDesc"
|
||||
>
|
||||
Never
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithNotificationTitle"
|
||||
>
|
||||
Keywords That Trigger Notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithNotificationTitle keysWithNotificationEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithNotificationEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithNotificationDesc"
|
||||
id="keywordsAndMentionsDesc"
|
||||
>
|
||||
"@some-user"
|
||||
</div>
|
||||
@@ -486,15 +486,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithHighlightTitle"
|
||||
id="keywordsAndHighlightTitle"
|
||||
>
|
||||
Keywords That Get Highlighted (Without Notifications)
|
||||
Keywords that get highlighted (without notifications)
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithHighlightTitle keysWithHighlightEdit"
|
||||
aria-labelledby="keywordsAndHighlightTitle keywordsAndHighlightEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithHighlightEdit"
|
||||
id="keywordsAndHighlightEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -505,7 +505,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithHighlightDesc"
|
||||
id="keywordsAndHighlightDesc"
|
||||
>
|
||||
None
|
||||
</div>
|
||||
@@ -609,7 +609,7 @@ Object {
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
Notification Settings
|
||||
Notification settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
@@ -663,15 +663,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopTitle"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop Notifications
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopTitle desktopEdit"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopEdit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -682,9 +682,44 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopDesc"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
For all activity, without sound
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopNotificationSoundTitle"
|
||||
>
|
||||
Desktop notification sounds
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopNotificationSoundTitle desktopNotificationSoundEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopNotificationSoundEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopNotificationSoundDesc"
|
||||
>
|
||||
No sound
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -700,7 +735,7 @@ Object {
|
||||
class="section-min__title"
|
||||
id="emailTitle"
|
||||
>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
@@ -733,15 +768,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="pushTitle"
|
||||
id="keywordsAndMentionsTitle"
|
||||
>
|
||||
Mobile Push Notifications
|
||||
Keywords that trigger notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="pushTitle pushEdit"
|
||||
aria-labelledby="keywordsAndMentionsTitle keywordsAndMentionsEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="pushEdit"
|
||||
id="keywordsAndMentionsEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -752,42 +787,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="pushDesc"
|
||||
>
|
||||
Never
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithNotificationTitle"
|
||||
>
|
||||
Keywords That Trigger Notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithNotificationTitle keysWithNotificationEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithNotificationEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithNotificationDesc"
|
||||
id="keywordsAndMentionsDesc"
|
||||
>
|
||||
"@some-user"
|
||||
</div>
|
||||
@@ -806,9 +806,9 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title isDisabled"
|
||||
id="keysWithHighlightTitle"
|
||||
id="keywordsAndHighlightTitle"
|
||||
>
|
||||
Keywords That Get Highlighted (Without Notifications)
|
||||
Keywords that get highlighted (without notifications)
|
||||
</h4>
|
||||
<span
|
||||
class="RestrictedIndicator__icon-tooltip-container"
|
||||
@@ -828,7 +828,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe isDisabled"
|
||||
id="keysWithHighlightDesc"
|
||||
id="keywordsAndHighlightDesc"
|
||||
>
|
||||
None
|
||||
</div>
|
||||
@@ -870,7 +870,7 @@ Object {
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
Notification Settings
|
||||
Notification settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
@@ -924,15 +924,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopTitle"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop Notifications
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopTitle desktopEdit"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopEdit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -943,9 +943,44 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopDesc"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
For all activity, without sound
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopNotificationSoundTitle"
|
||||
>
|
||||
Desktop notification sounds
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopNotificationSoundTitle desktopNotificationSoundEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopNotificationSoundEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopNotificationSoundDesc"
|
||||
>
|
||||
No sound
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -961,7 +996,7 @@ Object {
|
||||
class="section-min__title"
|
||||
id="emailTitle"
|
||||
>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
@@ -994,15 +1029,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="pushTitle"
|
||||
id="keywordsAndMentionsTitle"
|
||||
>
|
||||
Mobile Push Notifications
|
||||
Keywords that trigger notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="pushTitle pushEdit"
|
||||
aria-labelledby="keywordsAndMentionsTitle keywordsAndMentionsEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="pushEdit"
|
||||
id="keywordsAndMentionsEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -1013,42 +1048,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="pushDesc"
|
||||
>
|
||||
Never
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithNotificationTitle"
|
||||
>
|
||||
Keywords That Trigger Notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithNotificationTitle keysWithNotificationEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithNotificationEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithNotificationDesc"
|
||||
id="keywordsAndMentionsDesc"
|
||||
>
|
||||
"@some-user"
|
||||
</div>
|
||||
@@ -1067,9 +1067,9 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title isDisabled"
|
||||
id="keysWithHighlightTitle"
|
||||
id="keywordsAndHighlightTitle"
|
||||
>
|
||||
Keywords That Get Highlighted (Without Notifications)
|
||||
Keywords that get highlighted (without notifications)
|
||||
</h4>
|
||||
<span
|
||||
class="RestrictedIndicator__icon-tooltip-container"
|
||||
@@ -1089,7 +1089,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe isDisabled"
|
||||
id="keysWithHighlightDesc"
|
||||
id="keywordsAndHighlightDesc"
|
||||
>
|
||||
None
|
||||
</div>
|
||||
@@ -1190,7 +1190,7 @@ Object {
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
Notification Settings
|
||||
Notification settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
@@ -1244,15 +1244,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopTitle"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop Notifications
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopTitle desktopEdit"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopEdit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -1263,9 +1263,44 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopDesc"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
For all activity, without sound
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopNotificationSoundTitle"
|
||||
>
|
||||
Desktop notification sounds
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopNotificationSoundTitle desktopNotificationSoundEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopNotificationSoundEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopNotificationSoundDesc"
|
||||
>
|
||||
No sound
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -1281,7 +1316,7 @@ Object {
|
||||
class="section-min__title"
|
||||
id="emailTitle"
|
||||
>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
@@ -1314,15 +1349,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="pushTitle"
|
||||
id="keywordsAndMentionsTitle"
|
||||
>
|
||||
Mobile Push Notifications
|
||||
Keywords that trigger notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="pushTitle pushEdit"
|
||||
aria-labelledby="keywordsAndMentionsTitle keywordsAndMentionsEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="pushEdit"
|
||||
id="keywordsAndMentionsEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -1333,42 +1368,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="pushDesc"
|
||||
>
|
||||
Never
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithNotificationTitle"
|
||||
>
|
||||
Keywords That Trigger Notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithNotificationTitle keysWithNotificationEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithNotificationEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithNotificationDesc"
|
||||
id="keywordsAndMentionsDesc"
|
||||
>
|
||||
"@some-user"
|
||||
</div>
|
||||
@@ -1413,7 +1413,7 @@ Object {
|
||||
class="fa fa-angle-left"
|
||||
/>
|
||||
</div>
|
||||
Notification Settings
|
||||
Notification settings
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
@@ -1467,15 +1467,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopTitle"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop Notifications
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopTitle desktopEdit"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopEdit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -1486,9 +1486,44 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopDesc"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
For all activity, without sound
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopNotificationSoundTitle"
|
||||
>
|
||||
Desktop notification sounds
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopNotificationSoundTitle desktopNotificationSoundEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopNotificationSoundEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopNotificationSoundDesc"
|
||||
>
|
||||
No sound
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -1504,7 +1539,7 @@ Object {
|
||||
class="section-min__title"
|
||||
id="emailTitle"
|
||||
>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
@@ -1537,15 +1572,15 @@ Object {
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="pushTitle"
|
||||
id="keywordsAndMentionsTitle"
|
||||
>
|
||||
Mobile Push Notifications
|
||||
Keywords that trigger notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="pushTitle pushEdit"
|
||||
aria-labelledby="keywordsAndMentionsTitle keywordsAndMentionsEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="pushEdit"
|
||||
id="keywordsAndMentionsEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
@@ -1556,42 +1591,7 @@ Object {
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="pushDesc"
|
||||
>
|
||||
Never
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="divider-light"
|
||||
/>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="keysWithNotificationTitle"
|
||||
>
|
||||
Keywords That Trigger Notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="keysWithNotificationTitle keysWithNotificationEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="keysWithNotificationEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="keysWithNotificationDesc"
|
||||
id="keywordsAndMentionsDesc"
|
||||
>
|
||||
"@some-user"
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DesktopNotificationSettings should match snapshot, on max setting 1`] = `
|
||||
<div>
|
||||
<section
|
||||
class="section-max form-horizontal "
|
||||
>
|
||||
<h4
|
||||
class="col-sm-12 section-title"
|
||||
id="settingTitle"
|
||||
>
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<div
|
||||
class="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
class="setting-list"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<fieldset
|
||||
id="sendDesktopNotificationsSection"
|
||||
>
|
||||
<legend
|
||||
class="form-legend"
|
||||
>
|
||||
Send notifications for:
|
||||
</legend>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="all"
|
||||
/>
|
||||
All new messages
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="mention"
|
||||
/>
|
||||
Mentions, direct messages, and group messages
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="none"
|
||||
/>
|
||||
Nothing
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<br />
|
||||
<div
|
||||
class="checkbox single-checkbox"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
Notify me about replies to threads I'm following
|
||||
</label>
|
||||
</div>
|
||||
<hr />
|
||||
<div
|
||||
class="checkbox single-checkbox"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
Use different settings for my mobile devices
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<label
|
||||
class="singleSelectLabel"
|
||||
for="pushMobileNotificationSelectInput"
|
||||
id="pushMobileNotificationsLabel"
|
||||
>
|
||||
Trigger mobile notifications when I am:
|
||||
</label>
|
||||
<div
|
||||
class="react-select singleSelect css-2b097c-container"
|
||||
>
|
||||
<div
|
||||
class="react-select__control css-yk16xz-control"
|
||||
>
|
||||
<div
|
||||
class="react-select__value-container react-select__value-container--has-value css-1hwfws3"
|
||||
>
|
||||
<div
|
||||
class="react-select__single-value css-1uccc91-singleValue"
|
||||
>
|
||||
Offline
|
||||
</div>
|
||||
<input
|
||||
class="css-62g3xt-dummyInput"
|
||||
id="pushMobileNotificationSelectInput"
|
||||
readonly=""
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-6q0nyr-Svg"
|
||||
focusable="false"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
>
|
||||
<path
|
||||
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<hr />
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
data-testid="saveSetting"
|
||||
id="saveSetting"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Save
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-tertiary"
|
||||
id="cancelSetting"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`DesktopNotificationSettings should match snapshot, on min setting 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="section-min"
|
||||
>
|
||||
<div
|
||||
class="secion-min__header"
|
||||
>
|
||||
<h4
|
||||
class="section-min__title"
|
||||
id="desktopAndMobileTitle"
|
||||
>
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-labelledby="desktopAndMobileTitle desktopAndMobileEdit"
|
||||
class="color--link style--none section-min__edit"
|
||||
id="desktopAndMobileEdit"
|
||||
>
|
||||
<i
|
||||
class="icon-pencil-outline"
|
||||
title="Edit Icon"
|
||||
/>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="section-min__describe"
|
||||
id="desktopAndMobileDesc"
|
||||
>
|
||||
Configure desktop and mobile settings
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`DesktopNotificationSettings should not show desktop thread notification checkbox when collapsed threads are not enabled 1`] = `
|
||||
<div>
|
||||
<section
|
||||
class="section-max form-horizontal "
|
||||
>
|
||||
<h4
|
||||
class="col-sm-12 section-title"
|
||||
id="settingTitle"
|
||||
>
|
||||
Desktop and mobile notifications
|
||||
</h4>
|
||||
<div
|
||||
class="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
class="setting-list"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<fieldset
|
||||
id="sendDesktopNotificationsSection"
|
||||
>
|
||||
<legend
|
||||
class="form-legend"
|
||||
>
|
||||
Send notifications for:
|
||||
</legend>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="all"
|
||||
/>
|
||||
All new messages
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="mention"
|
||||
/>
|
||||
Mentions, direct messages, and group messages
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="radio"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="none"
|
||||
/>
|
||||
Nothing
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<hr />
|
||||
<div
|
||||
class="checkbox single-checkbox"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
Use different settings for my mobile devices
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<label
|
||||
class="singleSelectLabel"
|
||||
for="pushMobileNotificationSelectInput"
|
||||
id="pushMobileNotificationsLabel"
|
||||
>
|
||||
Trigger mobile notifications when I am:
|
||||
</label>
|
||||
<div
|
||||
class="react-select singleSelect css-2b097c-container"
|
||||
>
|
||||
<div
|
||||
class="react-select__control css-yk16xz-control"
|
||||
>
|
||||
<div
|
||||
class="react-select__value-container react-select__value-container--has-value css-1hwfws3"
|
||||
>
|
||||
<div
|
||||
class="react-select__single-value css-1uccc91-singleValue"
|
||||
>
|
||||
Offline
|
||||
</div>
|
||||
<input
|
||||
class="css-62g3xt-dummyInput"
|
||||
id="pushMobileNotificationSelectInput"
|
||||
readonly=""
|
||||
tabindex="0"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="react-select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="css-6q0nyr-Svg"
|
||||
focusable="false"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
>
|
||||
<path
|
||||
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="setting-list-item"
|
||||
>
|
||||
<hr />
|
||||
<button
|
||||
class="btn btn-primary "
|
||||
data-testid="saveSetting"
|
||||
id="saveSetting"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Save
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-tertiary"
|
||||
id="cancelSetting"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,312 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
import Constants, {NotificationLevels} from 'utils/constants';
|
||||
|
||||
import type {SelectOption, Props} from './index';
|
||||
import DesktopNotificationSettings, {
|
||||
shouldShowDesktopThreadsSection,
|
||||
shouldShowMobileThreadsSection,
|
||||
getValueOfSendMobileNotificationForSelect,
|
||||
getValueOfSendMobileNotificationWhenSelect,
|
||||
shouldShowTriggerMobileNotificationsSection,
|
||||
} from './index';
|
||||
|
||||
const validNotificationLevels = Object.values(NotificationLevels);
|
||||
|
||||
describe('DesktopNotificationSettings', () => {
|
||||
const baseProps: Props = {
|
||||
active: true,
|
||||
updateSection: jest.fn(),
|
||||
onSubmit: jest.fn(),
|
||||
onCancel: jest.fn(),
|
||||
saving: false,
|
||||
error: '',
|
||||
setParentState: jest.fn(),
|
||||
areAllSectionsInactive: false,
|
||||
isCollapsedThreadsEnabled: true,
|
||||
desktopActivity: NotificationLevels.DEFAULT,
|
||||
pushActivity: NotificationLevels.DEFAULT,
|
||||
pushStatus: Constants.UserStatuses.OFFLINE,
|
||||
desktopThreads: NotificationLevels.DEFAULT,
|
||||
pushThreads: NotificationLevels.DEFAULT,
|
||||
sendPushNotifications: true,
|
||||
desktopAndMobileSettingsDifferent: false,
|
||||
};
|
||||
|
||||
test('should match snapshot, on max setting', () => {
|
||||
const {container} = renderWithContext(
|
||||
<DesktopNotificationSettings {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on min setting', () => {
|
||||
const props = {...baseProps, active: false};
|
||||
const {container} = renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not show desktop thread notification checkbox when collapsed threads are not enabled', () => {
|
||||
const props = {...baseProps, isCollapsedThreadsEnabled: false};
|
||||
const {container} = renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Notify me about replies to threads I\'m following')).toBeNull();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should not show desktop thread notification checkbox when desktop is all', () => {
|
||||
const props = {...baseProps, desktopActivity: NotificationLevels.ALL};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Notify me about replies to threads I\'m following')).toBeNull();
|
||||
});
|
||||
|
||||
test('should not show desktop thread notification checkbox when desktop is none', () => {
|
||||
const props = {...baseProps, desktopActivity: NotificationLevels.NONE};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Notify me about replies to threads I\'m following')).toBeNull();
|
||||
});
|
||||
|
||||
test('should show desktop thread notification checkbox when desktop is mention', () => {
|
||||
const props = {...baseProps, desktopActivity: NotificationLevels.MENTION};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Notify me about replies to threads I\'m following')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should show mobile notification when checkbox for use different mobile settings is checked', () => {
|
||||
const props = {...baseProps, desktopAndMobileSettingsDifferent: true};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Send mobile notifications for:')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should not show mobile notification when checkbox for use different mobile settings is not checked', () => {
|
||||
const props = {...baseProps, desktopAndMobileSettingsDifferent: false};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Send mobile notifications for:')).toBeNull();
|
||||
});
|
||||
|
||||
test('should shown notify me about mobile threads when mobile setting is mention and desktop setting is anything', () => {
|
||||
const {rerender} = renderWithContext(
|
||||
<DesktopNotificationSettings
|
||||
{...baseProps}
|
||||
desktopAndMobileSettingsDifferent={true}
|
||||
pushActivity={NotificationLevels.MENTION}
|
||||
desktopActivity={NotificationLevels.NONE}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Notify me on mobile about replies to threads I\'m following')).toBeInTheDocument();
|
||||
|
||||
rerender(
|
||||
<DesktopNotificationSettings
|
||||
{...baseProps}
|
||||
desktopAndMobileSettingsDifferent={true}
|
||||
pushActivity={NotificationLevels.MENTION}
|
||||
desktopActivity={NotificationLevels.ALL}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Notify me on mobile about replies to threads I\'m following')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should not show notify me about mobile threads when mobile setting is anything other than mention', () => {
|
||||
const {rerender} = renderWithContext(
|
||||
<DesktopNotificationSettings
|
||||
{...baseProps}
|
||||
desktopAndMobileSettingsDifferent={true}
|
||||
pushActivity={NotificationLevels.NONE}
|
||||
desktopActivity={NotificationLevels.NONE}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Notify me on mobile about replies to threads I\'m following')).toBeNull();
|
||||
|
||||
rerender(
|
||||
<DesktopNotificationSettings
|
||||
{...baseProps}
|
||||
desktopAndMobileSettingsDifferent={true}
|
||||
pushActivity={NotificationLevels.ALL}
|
||||
desktopActivity={NotificationLevels.ALL}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Notify me on mobile about replies to threads I\'m following')).toBeNull();
|
||||
});
|
||||
|
||||
test('should show trigger mobile notifications section when desktop setting is mention', () => {
|
||||
const props = {...baseProps, desktopActivity: NotificationLevels.MENTION, desktopAndMobileSettingsDifferent: false};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Trigger mobile notifications when I am:')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should not show trigger mobile notifications section when desktop setting is none', () => {
|
||||
const props = {...baseProps, desktopActivity: NotificationLevels.NONE, desktopAndMobileSettingsDifferent: false};
|
||||
renderWithContext(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Trigger mobile notifications when I am:')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowDesktopThreadsSection', () => {
|
||||
test('should not show when collapsed threads are not enabled', () => {
|
||||
expect(shouldShowDesktopThreadsSection(false, 'hello' as any)).toBe(false);
|
||||
});
|
||||
|
||||
test('should not show when desktop setting is either none or all', () => {
|
||||
expect(shouldShowDesktopThreadsSection(true, NotificationLevels.NONE)).toBe(false);
|
||||
expect(shouldShowDesktopThreadsSection(true, NotificationLevels.ALL)).toBe(false);
|
||||
});
|
||||
|
||||
test('should show when desktop setting is mention', () => {
|
||||
expect(shouldShowDesktopThreadsSection(true, NotificationLevels.MENTION)).toBe(true);
|
||||
expect(shouldShowDesktopThreadsSection(true, NotificationLevels.DEFAULT)).toBe(true);
|
||||
});
|
||||
|
||||
test('should show by default when desktop setting is undefined', () => {
|
||||
expect(shouldShowDesktopThreadsSection(true, undefined as any)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowMobileThreadsSection', () => {
|
||||
test('should return false if sendPushNotifications is false', () => {
|
||||
const result = shouldShowMobileThreadsSection(false, true, true, 'any' as any);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false if isCollapsedThreadsEnabled is false', () => {
|
||||
const result = shouldShowMobileThreadsSection(true, false, true, 'any' as any);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false if desktop and mobile settings are same', () => {
|
||||
const result = shouldShowMobileThreadsSection(true, true, false, 'any' as any);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false if pushActivity is all', () => {
|
||||
const result = shouldShowMobileThreadsSection(true, true, true, NotificationLevels.ALL);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false if pushActivity is none', () => {
|
||||
const result = shouldShowMobileThreadsSection(true, true, true, NotificationLevels.NONE);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getValueOfSendMobileNotificationForSelect', () => {
|
||||
test('should return the middle option when input is undefined', () => {
|
||||
expect(getValueOfSendMobileNotificationForSelect(undefined as any)).not.toBeUndefined();
|
||||
|
||||
const result = getValueOfSendMobileNotificationForSelect(undefined as any) as SelectOption;
|
||||
expect(result.value).toBe(NotificationLevels.MENTION);
|
||||
});
|
||||
|
||||
test('should return the middle option when input is not a valid option', () => {
|
||||
expect(getValueOfSendMobileNotificationForSelect('invalid' as any)).not.toBeUndefined();
|
||||
|
||||
const result = getValueOfSendMobileNotificationForSelect('invalid' as any) as SelectOption;
|
||||
expect(result.value).toBe(NotificationLevels.MENTION);
|
||||
});
|
||||
|
||||
test('should return the same option when input is a valid option', () => {
|
||||
validNotificationLevels.
|
||||
filter((level) => level !== NotificationLevels.DEFAULT).
|
||||
forEach((level) => {
|
||||
expect(getValueOfSendMobileNotificationForSelect(level)).not.toBeUndefined();
|
||||
|
||||
const result = getValueOfSendMobileNotificationForSelect(level) as SelectOption;
|
||||
expect(result.value).toBe(level);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowTriggerMobileNotificationsSection', () => {
|
||||
test('should not show when push notifications are off', () => {
|
||||
expect(shouldShowTriggerMobileNotificationsSection(false, NotificationLevels.ALL, NotificationLevels.ALL, true)).toBe(false);
|
||||
});
|
||||
|
||||
test('should show if either of desktop or mobile settings are not defined', () => {
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, undefined as any, NotificationLevels.ALL, true)).toBe(true);
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.ALL, undefined as any, true)).toBe(true);
|
||||
});
|
||||
|
||||
test('should show if desktop is either mention or all for same mobile setting', () => {
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.MENTION, NotificationLevels.MENTION, false)).toBe(true);
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.ALL, NotificationLevels.ALL, false)).toBe(true);
|
||||
});
|
||||
|
||||
test('should not show if desktop is none for same mobile setting', () => {
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.NONE, NotificationLevels.NONE, false)).toBe(false);
|
||||
});
|
||||
|
||||
test('should show for any desktop setting if mobile setting is mention', () => {
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.ALL, NotificationLevels.MENTION, true)).toBe(true);
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.MENTION, NotificationLevels.MENTION, true)).toBe(true);
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.NONE, NotificationLevels.MENTION, true)).toBe(true);
|
||||
});
|
||||
|
||||
test('should not show for any desktop setting if mobile setting is none', () => {
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.ALL, NotificationLevels.NONE, true)).toBe(false);
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.MENTION, NotificationLevels.NONE, true)).toBe(false);
|
||||
expect(shouldShowTriggerMobileNotificationsSection(true, NotificationLevels.NONE, NotificationLevels.NONE, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getValueOfSendMobileNotificationWhenSelect', () => {
|
||||
test('When input is undefined it should return the last option', () => {
|
||||
expect(getValueOfSendMobileNotificationWhenSelect(undefined)).not.toBeUndefined();
|
||||
|
||||
const result = getValueOfSendMobileNotificationWhenSelect(undefined) as SelectOption;
|
||||
expect(result.value).toBe(Constants.UserStatuses.OFFLINE);
|
||||
});
|
||||
|
||||
test('when input is defined but is not a valid option it should return the last option', () => {
|
||||
// We are purposely testing with an invalid value hence the 'any'
|
||||
expect(getValueOfSendMobileNotificationWhenSelect('invalid' as any)).not.toBeUndefined();
|
||||
|
||||
const result = getValueOfSendMobileNotificationWhenSelect('invalid' as any) as SelectOption;
|
||||
expect(result.value).toBe(Constants.UserStatuses.OFFLINE);
|
||||
});
|
||||
|
||||
test('When input is a valid option it should return the same option', () => {
|
||||
expect(getValueOfSendMobileNotificationWhenSelect(Constants.UserStatuses.ONLINE)).not.toBeUndefined();
|
||||
|
||||
const result = getValueOfSendMobileNotificationWhenSelect(Constants.UserStatuses.ONLINE) as SelectOption;
|
||||
expect(result.value).toBe(Constants.UserStatuses.ONLINE);
|
||||
|
||||
expect(getValueOfSendMobileNotificationWhenSelect(Constants.UserStatuses.AWAY)).not.toBeUndefined();
|
||||
|
||||
const result2 = getValueOfSendMobileNotificationWhenSelect(Constants.UserStatuses.AWAY) as SelectOption;
|
||||
expect(result2.value).toBe(Constants.UserStatuses.AWAY);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,582 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {Fragment, useCallback, useEffect, useMemo, useRef, memo} from 'react';
|
||||
import type {ChangeEvent, ReactNode} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import ReactSelect from 'react-select';
|
||||
import type {ValueType, OptionsType} from 'react-select';
|
||||
|
||||
import type {UserNotifyProps} from '@mattermost/types/users';
|
||||
|
||||
import SettingItemMax from 'components/setting_item_max';
|
||||
import SettingItemMin from 'components/setting_item_min';
|
||||
import type SettingItemMinComponent from 'components/setting_item_min';
|
||||
|
||||
import Constants, {NotificationLevels, UserSettingsNotificationSections} from 'utils/constants';
|
||||
|
||||
import type {Props as UserSettingsNotificationsProps} from '../user_settings_notifications';
|
||||
|
||||
export type SelectOption = {
|
||||
label: ReactNode;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type Props = {
|
||||
active: boolean;
|
||||
updateSection: (section: string) => void;
|
||||
onSubmit: () => void;
|
||||
onCancel: () => void;
|
||||
saving: boolean;
|
||||
error: string;
|
||||
setParentState: (key: string, value: string | boolean) => void;
|
||||
areAllSectionsInactive: boolean;
|
||||
isCollapsedThreadsEnabled: boolean;
|
||||
desktopActivity: UserNotifyProps['desktop'];
|
||||
sendPushNotifications: UserSettingsNotificationsProps['sendPushNotifications'];
|
||||
pushActivity: UserNotifyProps['push'];
|
||||
pushStatus: UserNotifyProps['push_status'];
|
||||
desktopThreads: UserNotifyProps['desktop_threads'];
|
||||
pushThreads: UserNotifyProps['push_threads'];
|
||||
desktopAndMobileSettingsDifferent: boolean;
|
||||
};
|
||||
|
||||
function DesktopAndMobileNotificationSettings({
|
||||
active,
|
||||
updateSection,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
saving,
|
||||
error,
|
||||
setParentState,
|
||||
areAllSectionsInactive,
|
||||
isCollapsedThreadsEnabled,
|
||||
desktopActivity,
|
||||
sendPushNotifications,
|
||||
pushActivity,
|
||||
pushStatus,
|
||||
desktopThreads,
|
||||
pushThreads,
|
||||
desktopAndMobileSettingsDifferent,
|
||||
}: Props) {
|
||||
const editButtonRef = useRef<SettingItemMinComponent>(null);
|
||||
const previousActiveRef = useRef(active);
|
||||
|
||||
// Focus back on the edit button, after this section was closed after it was opened
|
||||
useEffect(() => {
|
||||
if (previousActiveRef.current && !active && areAllSectionsInactive) {
|
||||
editButtonRef.current?.focus();
|
||||
}
|
||||
|
||||
previousActiveRef.current = active;
|
||||
}, [active, areAllSectionsInactive]);
|
||||
|
||||
const handleChangeForSendDesktopNotificationsRadio = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
setParentState('desktopActivity', value);
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForDesktopThreadsCheckbox = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.checked ? NotificationLevels.ALL : NotificationLevels.MENTION;
|
||||
setParentState('desktopThreads', value);
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForDifferentMobileNotificationsCheckbox = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.checked;
|
||||
setParentState('desktopAndMobileSettingsDifferent', value);
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForSendMobileNotificationsSelect = useCallback((selectedOption: ValueType<SelectOption>) => {
|
||||
if (selectedOption && 'value' in selectedOption) {
|
||||
setParentState('pushActivity', selectedOption.value);
|
||||
}
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForMobileThreadsCheckbox = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.checked ? NotificationLevels.ALL : NotificationLevels.MENTION;
|
||||
setParentState('pushThreads', value);
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForTriggerMobileNotificationsSelect = useCallback((selectedOption: ValueType<SelectOption>) => {
|
||||
if (selectedOption && 'value' in selectedOption) {
|
||||
setParentState('pushStatus', selectedOption.value);
|
||||
}
|
||||
}, [setParentState]);
|
||||
|
||||
const maximizedSettingsInputs = useMemo(() => {
|
||||
const maximizedSettingInputs = [];
|
||||
|
||||
const sendDesktopNotificationsSection = (
|
||||
<fieldset
|
||||
id='sendDesktopNotificationsSection'
|
||||
key='sendDesktopNotificationsSection'
|
||||
>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.sendDesktopNotificationFor'
|
||||
defaultMessage='Send notifications for:'
|
||||
/>
|
||||
</legend>
|
||||
{optionsOfSendNotifications.map((optionOfSendNotifications) => (
|
||||
<div
|
||||
key={optionOfSendNotifications.value}
|
||||
className='radio'
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
checked={desktopActivity === optionOfSendNotifications.value}
|
||||
value={optionOfSendNotifications.value}
|
||||
onChange={handleChangeForSendDesktopNotificationsRadio}
|
||||
/>
|
||||
{optionOfSendNotifications.label}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</fieldset>
|
||||
);
|
||||
maximizedSettingInputs.push(sendDesktopNotificationsSection);
|
||||
|
||||
if (shouldShowDesktopThreadsSection(isCollapsedThreadsEnabled, desktopActivity)) {
|
||||
const desktopThreadNotificationSection = (
|
||||
<Fragment key='desktopThreadNotificationSection'>
|
||||
<br/>
|
||||
<div className='checkbox single-checkbox'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={desktopThreads === NotificationLevels.ALL}
|
||||
onChange={handleChangeForDesktopThreadsCheckbox}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.notifyForDesktopthreads'
|
||||
defaultMessage={'Notify me about replies to threads I\'m following'}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(desktopThreadNotificationSection);
|
||||
}
|
||||
|
||||
if (sendPushNotifications) {
|
||||
const differentMobileNotificationsSection = (
|
||||
<Fragment key='differentMobileNotificationsSection'>
|
||||
<hr/>
|
||||
<div className='checkbox single-checkbox'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={desktopAndMobileSettingsDifferent}
|
||||
onChange={handleChangeForDifferentMobileNotificationsCheckbox}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.differentMobileNotificationsTitle'
|
||||
defaultMessage='Use different settings for my mobile devices'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(differentMobileNotificationsSection);
|
||||
}
|
||||
|
||||
if (shouldShowSendMobileNotificationsSection(sendPushNotifications, desktopAndMobileSettingsDifferent)) {
|
||||
const sendMobileNotificationsSection = (
|
||||
<React.Fragment key='sendMobileNotificationsSection'>
|
||||
<br/>
|
||||
<label
|
||||
id='sendMobileNotificationsLabel'
|
||||
htmlFor='sendMobileNotificationsSelectInput'
|
||||
className='singleSelectLabel'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.sendMobileNotificationsFor'
|
||||
defaultMessage='Send mobile notifications for:'
|
||||
/>
|
||||
</label>
|
||||
<ReactSelect
|
||||
inputId='sendMobileNotificationsSelectInput'
|
||||
aria-labelledby='sendMobileNotificationsLabel'
|
||||
className='react-select singleSelect'
|
||||
classNamePrefix='react-select'
|
||||
options={optionsOfSendNotifications}
|
||||
clearable={false}
|
||||
isClearable={false}
|
||||
isSearchable={false}
|
||||
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
|
||||
value={getValueOfSendMobileNotificationForSelect(pushActivity)}
|
||||
onChange={handleChangeForSendMobileNotificationsSelect}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(sendMobileNotificationsSection);
|
||||
}
|
||||
|
||||
if (shouldShowMobileThreadsSection(sendPushNotifications, isCollapsedThreadsEnabled, desktopAndMobileSettingsDifferent, pushActivity)) {
|
||||
const threadNotificationSection = (
|
||||
<Fragment key='threadNotificationSection'>
|
||||
<br/>
|
||||
<div className='checkbox single-checkbox'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={pushThreads === NotificationLevels.ALL}
|
||||
onChange={handleChangeForMobileThreadsCheckbox}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.notifyForMobilethreads'
|
||||
defaultMessage={'Notify me on mobile about replies to threads I\'m following'}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(threadNotificationSection);
|
||||
}
|
||||
|
||||
if (shouldShowTriggerMobileNotificationsSection(sendPushNotifications, desktopActivity, pushActivity, desktopAndMobileSettingsDifferent)) {
|
||||
const triggerMobileNotificationsSection = (
|
||||
<React.Fragment key='triggerMobileNotificationsSection'>
|
||||
<br/>
|
||||
<label
|
||||
id='pushMobileNotificationsLabel'
|
||||
htmlFor='pushMobileNotificationSelectInput'
|
||||
className='singleSelectLabel'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.pushNotification'
|
||||
defaultMessage='Trigger mobile notifications when I am:'
|
||||
/>
|
||||
</label>
|
||||
<ReactSelect
|
||||
inputId='pushMobileNotificationSelectInput'
|
||||
aria-labelledby='pushMobileNotificationsLabel'
|
||||
className='react-select singleSelect'
|
||||
classNamePrefix='react-select'
|
||||
options={optionsOfSendMobileNotificationsWhenSelect}
|
||||
clearable={false}
|
||||
isClearable={false}
|
||||
isSearchable={false}
|
||||
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
|
||||
value={getValueOfSendMobileNotificationWhenSelect(pushStatus)}
|
||||
onChange={handleChangeForTriggerMobileNotificationsSelect}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(triggerMobileNotificationsSection);
|
||||
}
|
||||
|
||||
if (!sendPushNotifications) {
|
||||
const disabledPushNotificationsSection = (
|
||||
<>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.pushNotificationsDisabled'
|
||||
defaultMessage={'Mobile push notifications haven\'t been enabled by your system administrator.'}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
maximizedSettingInputs.push(disabledPushNotificationsSection);
|
||||
}
|
||||
|
||||
return maximizedSettingInputs;
|
||||
},
|
||||
[
|
||||
desktopActivity,
|
||||
handleChangeForSendDesktopNotificationsRadio,
|
||||
isCollapsedThreadsEnabled,
|
||||
desktopThreads,
|
||||
handleChangeForDesktopThreadsCheckbox,
|
||||
sendPushNotifications,
|
||||
desktopAndMobileSettingsDifferent,
|
||||
handleChangeForDifferentMobileNotificationsCheckbox,
|
||||
pushActivity,
|
||||
handleChangeForSendMobileNotificationsSelect,
|
||||
pushThreads,
|
||||
handleChangeForMobileThreadsCheckbox,
|
||||
pushStatus,
|
||||
handleChangeForTriggerMobileNotificationsSelect,
|
||||
]);
|
||||
|
||||
function handleChangeForMaxSection(section: string) {
|
||||
updateSection(section);
|
||||
}
|
||||
|
||||
function handleChangeForMinSection(section: string) {
|
||||
updateSection(section);
|
||||
onCancel();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
return (
|
||||
<SettingItemMax
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={'user.settings.notifications.desktopAndMobile.title'}
|
||||
defaultMessage='Desktop and mobile notifications'
|
||||
/>
|
||||
}
|
||||
inputs={maximizedSettingsInputs}
|
||||
submit={onSubmit}
|
||||
saving={saving}
|
||||
serverError={error}
|
||||
updateSection={handleChangeForMaxSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
ref={editButtonRef}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.title'
|
||||
defaultMessage='Desktop and mobile notifications'
|
||||
/>
|
||||
}
|
||||
describe={getCollapsedText(desktopActivity, pushActivity)}
|
||||
section={UserSettingsNotificationSections.DESKTOP_AND_MOBILE}
|
||||
updateSection={handleChangeForMinSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function NoIndicatorSeparatorComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const optionsOfSendNotifications = [
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.allNewMessages'
|
||||
defaultMessage='All new messages'
|
||||
/>
|
||||
),
|
||||
value: NotificationLevels.ALL,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.onlyMentions'
|
||||
defaultMessage='Mentions, direct messages, and group messages'
|
||||
/>
|
||||
),
|
||||
value: NotificationLevels.MENTION,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.nothing'
|
||||
defaultMessage='Nothing'
|
||||
/>
|
||||
),
|
||||
value: NotificationLevels.NONE,
|
||||
},
|
||||
];
|
||||
|
||||
export function shouldShowDesktopThreadsSection(isCollapsedThreadsEnabled: boolean, desktopActivity: UserNotifyProps['desktop']) {
|
||||
if (!isCollapsedThreadsEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desktopActivity === NotificationLevels.ALL || desktopActivity === NotificationLevels.NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function shouldShowMobileThreadsSection(sendPushNotifications: UserSettingsNotificationsProps['sendPushNotifications'], isCollapsedThreadsEnabled: boolean, desktopAndMobileSettingsDifferent: boolean, pushActivity: UserNotifyProps['push']) {
|
||||
if (!sendPushNotifications) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isCollapsedThreadsEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!desktopAndMobileSettingsDifferent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pushActivity === NotificationLevels.ALL || pushActivity === NotificationLevels.NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function shouldShowSendMobileNotificationsSection(sendPushNotifications: UserSettingsNotificationsProps['sendPushNotifications'], desktopAndMobileSettingsDifferent: boolean) {
|
||||
if (!sendPushNotifications) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desktopAndMobileSettingsDifferent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getValueOfSendMobileNotificationForSelect(pushActivity: UserNotifyProps['push']): ValueType<SelectOption> {
|
||||
if (!pushActivity) {
|
||||
return optionsOfSendNotifications[1];
|
||||
}
|
||||
|
||||
const option = optionsOfSendNotifications.find((option) => option.value === pushActivity);
|
||||
if (!option) {
|
||||
return optionsOfSendNotifications[1];
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
export function shouldShowTriggerMobileNotificationsSection(sendPushNotifications: UserSettingsNotificationsProps['sendPushNotifications'], desktopActivity: UserNotifyProps['desktop'], pushActivity: UserNotifyProps['push'], desktopAndMobileSettingsDifferent: boolean): boolean {
|
||||
if (!sendPushNotifications) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!desktopActivity || !pushActivity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!desktopAndMobileSettingsDifferent) {
|
||||
if (desktopActivity === NotificationLevels.NONE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pushActivity === NotificationLevels.NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const optionsOfSendMobileNotificationsWhenSelect: OptionsType<SelectOption> = [
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.online'
|
||||
defaultMessage='Online, away, or offline'
|
||||
/>
|
||||
),
|
||||
value: Constants.UserStatuses.ONLINE,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.away'
|
||||
defaultMessage='Away or offline'
|
||||
/>
|
||||
),
|
||||
value: Constants.UserStatuses.AWAY,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.offline'
|
||||
defaultMessage='Offline'
|
||||
/>
|
||||
),
|
||||
value: Constants.UserStatuses.OFFLINE,
|
||||
},
|
||||
];
|
||||
|
||||
export function getValueOfSendMobileNotificationWhenSelect(pushStatus?: UserNotifyProps['push_status']): ValueType<SelectOption> {
|
||||
if (!pushStatus) {
|
||||
return optionsOfSendMobileNotificationsWhenSelect[2];
|
||||
}
|
||||
|
||||
const option = optionsOfSendMobileNotificationsWhenSelect.find((option) => option.value === pushStatus);
|
||||
if (!option) {
|
||||
return optionsOfSendMobileNotificationsWhenSelect[2];
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
function getCollapsedText(desktopActivity: UserNotifyProps['desktop'], pushActivity: UserNotifyProps['push']): ReactNode {
|
||||
if (desktopActivity === NotificationLevels.ALL) {
|
||||
if (pushActivity === NotificationLevels.ALL) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.allForDesktopAndMobile'
|
||||
defaultMessage='All new messages'
|
||||
/>
|
||||
);
|
||||
} else if (pushActivity === NotificationLevels.MENTION) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.allDesktopButMobileMentions'
|
||||
defaultMessage='All new messages on desktop; mentions, direct messages, and group messages on mobile'
|
||||
/>
|
||||
);
|
||||
} else if (pushActivity === NotificationLevels.NONE) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.allDesktopButMobileNone'
|
||||
defaultMessage='All new messages on desktop; never on mobile'
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (desktopActivity === NotificationLevels.MENTION) {
|
||||
if (pushActivity === NotificationLevels.ALL) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.mentionsDesktopButMobileAll'
|
||||
defaultMessage='Mentions, direct messages, and group messages on desktop; all new messages on mobile'
|
||||
/>
|
||||
);
|
||||
} else if (pushActivity === NotificationLevels.MENTION) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.mentionsForDesktopAndMobile'
|
||||
defaultMessage='Mentions, direct messages, and group messages'
|
||||
/>
|
||||
);
|
||||
} else if (pushActivity === NotificationLevels.NONE) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.mentionsForDesktopButMobileNone'
|
||||
defaultMessage='Mentions, direct messages, and group messages on desktop; never on mobile'
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (desktopActivity === NotificationLevels.NONE) {
|
||||
if (pushActivity === NotificationLevels.ALL) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.noneDesktopButMobileAll'
|
||||
defaultMessage='Never on desktop; all new messages on mobile'
|
||||
/>
|
||||
);
|
||||
} else if (pushActivity === NotificationLevels.MENTION) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.noneDesktopButMobileMentions'
|
||||
defaultMessage='Never on desktop; mentions, direct messages, and group messages on mobile'
|
||||
/>
|
||||
);
|
||||
} else if (pushActivity === NotificationLevels.NONE) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.noneForDesktopAndMobile'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopAndMobile.noValidSettings'
|
||||
defaultMessage='Configure desktop and mobile settings'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(DesktopAndMobileNotificationSettings);
|
||||
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -1,155 +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 type {ComponentProps} from 'react';
|
||||
|
||||
import {NotificationLevels} from 'utils/constants';
|
||||
|
||||
import DesktopNotificationSettings from './desktop_notification_settings';
|
||||
|
||||
jest.mock('utils/notification_sounds', () => {
|
||||
const original = jest.requireActual('utils/notification_sounds');
|
||||
return {
|
||||
...original,
|
||||
hasSoundOptions: jest.fn(() => true),
|
||||
};
|
||||
});
|
||||
|
||||
describe('components/user_settings/notifications/DesktopNotificationSettings', () => {
|
||||
const baseProps: ComponentProps<typeof DesktopNotificationSettings> = {
|
||||
active: true,
|
||||
updateSection: jest.fn(),
|
||||
onSubmit: jest.fn(),
|
||||
onCancel: jest.fn(),
|
||||
saving: false,
|
||||
error: '',
|
||||
setParentState: jest.fn(),
|
||||
areAllSectionsInactive: false,
|
||||
isCollapsedThreadsEnabled: false,
|
||||
activity: NotificationLevels.MENTION,
|
||||
threads: NotificationLevels.ALL,
|
||||
sound: 'false',
|
||||
callsSound: 'false',
|
||||
selectedSound: 'Bing',
|
||||
callsSelectedSound: 'Dynamic',
|
||||
isCallsRingingEnabled: false,
|
||||
};
|
||||
|
||||
test('should match snapshot, on max setting', () => {
|
||||
const wrapper = shallow(
|
||||
<DesktopNotificationSettings {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on max setting with sound enabled', () => {
|
||||
const props = {...baseProps, sound: 'true'};
|
||||
const wrapper = shallow(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on max setting with Calls enabled', () => {
|
||||
const props = {...baseProps, isCallsRingingEnabled: true};
|
||||
const wrapper = shallow(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on max setting with Calls enabled, calls sound true', () => {
|
||||
const props = {...baseProps, isCallsRingingEnabled: true, callsSound: 'true'};
|
||||
const wrapper = shallow(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on min setting', () => {
|
||||
const props = {...baseProps, active: false};
|
||||
const wrapper = shallow(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should call props.updateSection and props.onCancel on handleMinUpdateSection', () => {
|
||||
const props = {...baseProps, updateSection: jest.fn(), onCancel: jest.fn()};
|
||||
const wrapper = shallow<DesktopNotificationSettings>(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().handleMinUpdateSection('');
|
||||
expect(props.updateSection).toHaveBeenCalledTimes(1);
|
||||
expect(props.updateSection).toHaveBeenCalledWith('');
|
||||
expect(props.onCancel).toHaveBeenCalledTimes(1);
|
||||
expect(props.onCancel).toHaveBeenCalledWith();
|
||||
|
||||
wrapper.instance().handleMinUpdateSection('desktop');
|
||||
expect(props.updateSection).toHaveBeenCalledTimes(2);
|
||||
expect(props.updateSection).toHaveBeenCalledWith('desktop');
|
||||
expect(props.onCancel).toHaveBeenCalledTimes(2);
|
||||
expect(props.onCancel).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
test('should call props.updateSection on handleMaxUpdateSection', () => {
|
||||
const props = {...baseProps, updateSection: jest.fn()};
|
||||
const wrapper = shallow<DesktopNotificationSettings>(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().handleMaxUpdateSection('');
|
||||
expect(props.updateSection).toHaveBeenCalledTimes(1);
|
||||
expect(props.updateSection).toHaveBeenCalledWith('');
|
||||
|
||||
wrapper.instance().handleMaxUpdateSection('desktop');
|
||||
expect(props.updateSection).toHaveBeenCalledTimes(2);
|
||||
expect(props.updateSection).toHaveBeenCalledWith('desktop');
|
||||
});
|
||||
|
||||
test('should call props.setParentState on handleOnChange', () => {
|
||||
const props = {...baseProps, setParentState: jest.fn()};
|
||||
const wrapper = shallow<DesktopNotificationSettings>(
|
||||
<DesktopNotificationSettings {...props}/>,
|
||||
);
|
||||
|
||||
wrapper.instance().handleOnChange({
|
||||
currentTarget: {getAttribute: (key: string) => {
|
||||
return {'data-key': 'dataKey', 'data-value': 'dataValue'}[key];
|
||||
}},
|
||||
} as unknown as React.ChangeEvent<HTMLInputElement>);
|
||||
|
||||
expect(props.setParentState).toHaveBeenCalledTimes(1);
|
||||
expect(props.setParentState).toHaveBeenCalledWith('dataKey', 'dataValue');
|
||||
});
|
||||
|
||||
test('should match snapshot, on buildMaximizedSetting', () => {
|
||||
const wrapper = shallow<DesktopNotificationSettings>(
|
||||
<DesktopNotificationSettings {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper.instance().buildMaximizedSetting()).toMatchSnapshot();
|
||||
|
||||
wrapper.setProps({activity: NotificationLevels.NONE});
|
||||
expect(wrapper.instance().buildMaximizedSetting()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, on buildMinimizedSetting', () => {
|
||||
const wrapper = shallow<DesktopNotificationSettings>(
|
||||
<DesktopNotificationSettings {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper.instance().buildMinimizedSetting()).toMatchSnapshot();
|
||||
|
||||
wrapper.setProps({activity: NotificationLevels.NONE});
|
||||
expect(wrapper.instance().buildMinimizedSetting()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,547 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {type ChangeEvent, type RefObject, type ReactNode} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import ReactSelect, {type ValueType} from 'react-select';
|
||||
|
||||
import SettingItemMax from 'components/setting_item_max';
|
||||
import SettingItemMin from 'components/setting_item_min';
|
||||
import type SettingItemMinComponent from 'components/setting_item_min';
|
||||
|
||||
import {NotificationLevels} from 'utils/constants';
|
||||
import * as NotificationSounds from 'utils/notification_sounds';
|
||||
import {a11yFocus} from 'utils/utils';
|
||||
|
||||
type SelectedOption = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
active: boolean;
|
||||
updateSection: (section: string) => void;
|
||||
onSubmit: () => void;
|
||||
onCancel: () => void;
|
||||
saving: boolean;
|
||||
error: string;
|
||||
setParentState: (key: string, value: string | boolean) => void;
|
||||
areAllSectionsInactive: boolean;
|
||||
isCollapsedThreadsEnabled: boolean;
|
||||
activity: string;
|
||||
threads?: string;
|
||||
sound: string;
|
||||
callsSound: string;
|
||||
selectedSound: string;
|
||||
callsSelectedSound: string;
|
||||
isCallsRingingEnabled: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
selectedOption: SelectedOption;
|
||||
callsSelectedOption: SelectedOption;
|
||||
blurDropdown: boolean;
|
||||
};
|
||||
|
||||
export default class DesktopNotificationSettings extends React.PureComponent<Props, State> {
|
||||
dropdownSoundRef: RefObject<ReactSelect>;
|
||||
callsDropdownRef: RefObject<ReactSelect>;
|
||||
editButtonRef: RefObject<SettingItemMinComponent>;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedOption: {value: props.selectedSound, label: props.selectedSound},
|
||||
callsSelectedOption: {value: props.callsSelectedSound, label: props.callsSelectedSound},
|
||||
blurDropdown: false,
|
||||
};
|
||||
|
||||
this.dropdownSoundRef = React.createRef();
|
||||
this.callsDropdownRef = React.createRef();
|
||||
this.editButtonRef = React.createRef();
|
||||
}
|
||||
|
||||
focusEditButton(): void {
|
||||
this.editButtonRef.current?.focus();
|
||||
}
|
||||
|
||||
handleMinUpdateSection = (section: string): void => {
|
||||
this.props.updateSection(section);
|
||||
this.props.onCancel();
|
||||
};
|
||||
|
||||
handleMaxUpdateSection = (section: string): void => this.props.updateSection(section);
|
||||
|
||||
handleOnChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
const key = e.currentTarget.getAttribute('data-key');
|
||||
const value = e.currentTarget.getAttribute('data-value');
|
||||
if (key && value) {
|
||||
this.props.setParentState(key, value);
|
||||
a11yFocus(e.currentTarget);
|
||||
}
|
||||
if (key === 'callsDesktopSound' && value === 'false') {
|
||||
NotificationSounds.stopTryNotificationRing();
|
||||
}
|
||||
};
|
||||
|
||||
handleThreadsOnChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
const value = e.target.checked ? NotificationLevels.ALL : NotificationLevels.MENTION;
|
||||
this.props.setParentState('desktopThreads', value);
|
||||
};
|
||||
|
||||
setDesktopNotificationSound: ReactSelect['onChange'] = (selectedOption: ValueType<SelectedOption>): void => {
|
||||
if (selectedOption && 'value' in selectedOption) {
|
||||
this.props.setParentState('desktopNotificationSound', selectedOption.value);
|
||||
this.setState({selectedOption});
|
||||
NotificationSounds.tryNotificationSound(selectedOption.value);
|
||||
}
|
||||
};
|
||||
|
||||
setCallsNotificationRing: ReactSelect['onChange'] = (selectedOption: ValueType<SelectedOption>): void => {
|
||||
if (selectedOption && 'value' in selectedOption) {
|
||||
this.props.setParentState('callsNotificationSound', selectedOption.value);
|
||||
this.setState({callsSelectedOption: selectedOption});
|
||||
NotificationSounds.tryNotificationRing(selectedOption.value);
|
||||
}
|
||||
};
|
||||
|
||||
blurDropdown(): void {
|
||||
if (!this.state.blurDropdown) {
|
||||
this.setState({blurDropdown: true});
|
||||
if (this.dropdownSoundRef.current) {
|
||||
this.dropdownSoundRef.current.blur();
|
||||
}
|
||||
if (this.callsDropdownRef.current) {
|
||||
this.callsDropdownRef.current.blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildMaximizedSetting = (): JSX.Element => {
|
||||
const inputs = [];
|
||||
|
||||
const activityRadio = [false, false, false];
|
||||
if (this.props.activity === NotificationLevels.MENTION) {
|
||||
activityRadio[1] = true;
|
||||
} else if (this.props.activity === NotificationLevels.NONE) {
|
||||
activityRadio[2] = true;
|
||||
} else {
|
||||
activityRadio[0] = true;
|
||||
}
|
||||
|
||||
let soundSection;
|
||||
let notificationSelection;
|
||||
let threadsNotificationSelection;
|
||||
let callsSection;
|
||||
let callsNotificationSelection;
|
||||
if (this.props.activity !== NotificationLevels.NONE) {
|
||||
const soundRadio = [false, false];
|
||||
if (this.props.sound === 'false') {
|
||||
soundRadio[1] = true;
|
||||
} else {
|
||||
soundRadio[0] = true;
|
||||
}
|
||||
|
||||
if (this.props.sound === 'true') {
|
||||
const sounds = Array.from(NotificationSounds.notificationSounds.keys());
|
||||
const options = sounds.map((sound) => {
|
||||
return {value: sound, label: sound};
|
||||
});
|
||||
|
||||
notificationSelection = (<div className='pt-2'>
|
||||
<ReactSelect
|
||||
className='react-select notification-sound-dropdown'
|
||||
classNamePrefix='react-select'
|
||||
id='displaySoundNotification'
|
||||
options={options}
|
||||
clearable={false}
|
||||
onChange={this.setDesktopNotificationSound}
|
||||
value={this.state.selectedOption}
|
||||
isSearchable={false}
|
||||
ref={this.dropdownSoundRef}
|
||||
components={{SingleValue: (props) => <div data-testid='displaySoundNotificationValue'>{props.children}</div>}}
|
||||
/></div>);
|
||||
}
|
||||
|
||||
if (this.props.isCallsRingingEnabled) {
|
||||
const callsSoundRadio = [false, false];
|
||||
if (this.props.callsSound === 'false') {
|
||||
callsSoundRadio[1] = true;
|
||||
} else {
|
||||
callsSoundRadio[0] = true;
|
||||
}
|
||||
|
||||
if (this.props.callsSound === 'true') {
|
||||
const callsSounds = Array.from(NotificationSounds.callsNotificationSounds.keys());
|
||||
const callsOptions = callsSounds.map((sound) => {
|
||||
return {value: sound, label: sound};
|
||||
});
|
||||
|
||||
callsNotificationSelection = (<div className='pt-2'>
|
||||
<ReactSelect
|
||||
className='react-select notification-sound-dropdown'
|
||||
classNamePrefix='react-select'
|
||||
id='displayCallsSoundNotification'
|
||||
options={callsOptions}
|
||||
clearable={false}
|
||||
onChange={this.setCallsNotificationRing}
|
||||
value={this.state.callsSelectedOption}
|
||||
isSearchable={false}
|
||||
ref={this.callsDropdownRef}
|
||||
components={{SingleValue: (props) => <div data-testid='displayCallsSoundNotificationValue'>{props.children}</div>}}
|
||||
/></div>);
|
||||
}
|
||||
|
||||
callsSection = (
|
||||
<>
|
||||
<hr/>
|
||||
<fieldset>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.calls_sound'
|
||||
defaultMessage='Notification sound for incoming calls'
|
||||
/>
|
||||
</legend>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='callsSoundOn'
|
||||
type='radio'
|
||||
name='callsNotificationSounds'
|
||||
checked={callsSoundRadio[0]}
|
||||
data-key={'callsDesktopSound'}
|
||||
data-value={'true'}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='soundOff'
|
||||
type='radio'
|
||||
name='callsNotificationSounds'
|
||||
checked={callsSoundRadio[1]}
|
||||
data-key={'callsDesktopSound'}
|
||||
data-value={'false'}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
{callsNotificationSelection}
|
||||
</fieldset>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (NotificationSounds.hasSoundOptions()) {
|
||||
soundSection = (
|
||||
<fieldset>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.sound'
|
||||
defaultMessage='Notification sound'
|
||||
/>
|
||||
</legend>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='soundOn'
|
||||
type='radio'
|
||||
name='notificationSounds'
|
||||
checked={soundRadio[0]}
|
||||
data-key={'desktopSound'}
|
||||
data-value={'true'}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='soundOff'
|
||||
type='radio'
|
||||
name='notificationSounds'
|
||||
checked={soundRadio[1]}
|
||||
data-key={'desktopSound'}
|
||||
data-value={'false'}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
{notificationSelection}
|
||||
<div className='mt-5'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.sounds_info'
|
||||
defaultMessage='Notification sounds are available on Firefox, Edge, Safari, Chrome and Mattermost Desktop Apps.'
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
);
|
||||
} else {
|
||||
soundSection = (
|
||||
<fieldset>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.sound'
|
||||
defaultMessage='Notification sound'
|
||||
/>
|
||||
</legend>
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.soundConfig'
|
||||
defaultMessage='Please configure notification sounds in your browser settings'
|
||||
/>
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.isCollapsedThreadsEnabled && NotificationLevels.MENTION === this.props.activity) {
|
||||
threadsNotificationSelection = (
|
||||
<>
|
||||
<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={this.props.threads === NotificationLevels.ALL}
|
||||
onChange={this.handleThreadsOnChange}
|
||||
/>
|
||||
<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>
|
||||
<hr/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
inputs.push(
|
||||
<div key='userNotificationLevelOption'>
|
||||
<fieldset>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop'
|
||||
defaultMessage='Send desktop notifications'
|
||||
/>
|
||||
</legend>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='desktopNotificationAllActivity'
|
||||
type='radio'
|
||||
name='desktopNotificationLevel'
|
||||
checked={activityRadio[0]}
|
||||
data-key={'desktopActivity'}
|
||||
data-value={NotificationLevels.ALL}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.allActivity'
|
||||
defaultMessage='For all activity'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='desktopNotificationMentions'
|
||||
type='radio'
|
||||
name='desktopNotificationLevel'
|
||||
checked={activityRadio[1]}
|
||||
data-key={'desktopActivity'}
|
||||
data-value={NotificationLevels.MENTION}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.onlyMentions'
|
||||
defaultMessage='Only for mentions, direct messages, and group messages'
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='desktopNotificationNever'
|
||||
type='radio'
|
||||
name='desktopNotificationLevel'
|
||||
checked={activityRadio[2]}
|
||||
data-key={'desktopActivity'}
|
||||
data-value={NotificationLevels.NONE}
|
||||
onChange={this.handleOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='mt-5'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.info'
|
||||
defaultMessage='Desktop notifications are available on Edge, Firefox, Safari, Chrome and Mattermost Desktop Apps.'
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
<hr/>
|
||||
{threadsNotificationSelection}
|
||||
{soundSection}
|
||||
{callsSection}
|
||||
</div>,
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingItemMax
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={'user.settings.notifications.desktop.title'}
|
||||
defaultMessage={'Desktop Notifications'}
|
||||
/>
|
||||
}
|
||||
inputs={inputs}
|
||||
submit={this.props.onSubmit}
|
||||
saving={this.props.saving}
|
||||
serverError={this.props.error}
|
||||
updateSection={this.handleMaxUpdateSection}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
buildMinimizedSetting = () => {
|
||||
const hasSoundOption = NotificationSounds.hasSoundOptions();
|
||||
let collapsedDescription: ReactNode = null;
|
||||
|
||||
if (this.props.activity === NotificationLevels.MENTION) {
|
||||
if (hasSoundOption && this.props.sound !== 'false') {
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.mentionsSound'
|
||||
defaultMessage='For mentions and direct messages, with sound'
|
||||
/>
|
||||
);
|
||||
} else if (hasSoundOption && this.props.sound === 'false') {
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.mentionsNoSound'
|
||||
defaultMessage='For mentions and direct messages, without sound'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.mentionsSoundHidden'
|
||||
defaultMessage='For mentions and direct messages'
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (this.props.activity === NotificationLevels.NONE) {
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
if (hasSoundOption && this.props.sound !== 'false') { //eslint-disable-line no-lonely-if
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.allSound'
|
||||
defaultMessage='For all activity, with sound'
|
||||
/>
|
||||
);
|
||||
} else if (hasSoundOption && this.props.sound === 'false') {
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.allNoSound'
|
||||
defaultMessage='For all activity, without sound'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
collapsedDescription = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktop.allSoundHidden'
|
||||
defaultMessage='For all activity'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
ref={this.editButtonRef}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={'user.settings.notifications.desktop.title'}
|
||||
defaultMessage={'Desktop Notifications'}
|
||||
/>
|
||||
}
|
||||
describe={collapsedDescription}
|
||||
section={'desktop'}
|
||||
updateSection={this.handleMinUpdateSection}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
this.blurDropdown();
|
||||
if (prevProps.active && !this.props.active && this.props.areAllSectionsInactive) {
|
||||
this.focusEditButton();
|
||||
}
|
||||
if (this.props.selectedSound !== prevProps.selectedSound) {
|
||||
this.setState({selectedOption: {value: this.props.selectedSound, label: this.props.selectedSound}});
|
||||
}
|
||||
if (this.props.callsSelectedSound !== prevProps.callsSelectedSound) {
|
||||
this.setState({callsSelectedOption: {value: this.props.callsSelectedSound, label: this.props.callsSelectedSound}});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.active) {
|
||||
return this.buildMaximizedSetting();
|
||||
}
|
||||
|
||||
return this.buildMinimizedSetting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ChangeEvent, ReactNode} from 'react';
|
||||
import React, {memo, useEffect, useRef, Fragment, useMemo, useCallback} from 'react';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
import type {ValueType} from 'react-select';
|
||||
import ReactSelect from 'react-select';
|
||||
|
||||
import type {UserNotifyProps} from '@mattermost/types/users';
|
||||
|
||||
import SettingItemMax from 'components/setting_item_max';
|
||||
import SettingItemMin from 'components/setting_item_min';
|
||||
import type SettingItemMinComponent from 'components/setting_item_min';
|
||||
|
||||
import {UserSettingsNotificationSections} from 'utils/constants';
|
||||
import {
|
||||
callsNotificationSounds,
|
||||
notificationSounds,
|
||||
stopTryNotificationRing,
|
||||
tryNotificationSound,
|
||||
tryNotificationRing,
|
||||
} from 'utils/notification_sounds';
|
||||
|
||||
import type {Props as UserSettingsNotificationsProps} from '../user_settings_notifications';
|
||||
|
||||
export type SelectOption = {
|
||||
value: string;
|
||||
label: ReactNode;
|
||||
};
|
||||
|
||||
export type Props = {
|
||||
active: boolean;
|
||||
updateSection: (section: string) => void;
|
||||
onSubmit: () => void;
|
||||
onCancel: () => void;
|
||||
saving: boolean;
|
||||
error: string;
|
||||
setParentState: (key: string, value: string | boolean) => void;
|
||||
areAllSectionsInactive: boolean;
|
||||
desktopSound: UserNotifyProps['desktop_sound'];
|
||||
desktopNotificationSound: UserNotifyProps['desktop_notification_sound'];
|
||||
isCallsRingingEnabled: UserSettingsNotificationsProps['isCallsRingingEnabled'];
|
||||
callsDesktopSound: UserNotifyProps['calls_desktop_sound'];
|
||||
callsNotificationSound: UserNotifyProps['calls_notification_sound'];
|
||||
};
|
||||
|
||||
function DesktopNotificationSoundsSettings({
|
||||
active,
|
||||
updateSection,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
saving,
|
||||
error,
|
||||
setParentState,
|
||||
areAllSectionsInactive,
|
||||
desktopSound,
|
||||
desktopNotificationSound,
|
||||
isCallsRingingEnabled,
|
||||
callsDesktopSound,
|
||||
callsNotificationSound,
|
||||
}: Props) {
|
||||
const intl = useIntl();
|
||||
|
||||
const editButtonRef = useRef<SettingItemMinComponent>(null);
|
||||
const previousActiveRef = useRef(active);
|
||||
|
||||
// Focus back on the edit button, after this section was closed after it was opened
|
||||
useEffect(() => {
|
||||
if (previousActiveRef.current && !active && areAllSectionsInactive) {
|
||||
editButtonRef.current?.focus();
|
||||
}
|
||||
|
||||
previousActiveRef.current = active;
|
||||
}, [active, areAllSectionsInactive]);
|
||||
|
||||
const handleChangeForMessageNotificationSoundCheckbox = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.checked ? 'true' : 'false';
|
||||
setParentState('desktopSound', value);
|
||||
|
||||
if (value === 'false') {
|
||||
stopTryNotificationRing();
|
||||
}
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForIncomginCallSoundCheckbox = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.checked ? 'true' : 'false';
|
||||
setParentState('callsDesktopSound', value);
|
||||
|
||||
if (value === 'false') {
|
||||
stopTryNotificationRing();
|
||||
}
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForMessageNotificationSoundSelect = useCallback((selectedOption: ValueType<SelectOption>) => {
|
||||
stopTryNotificationRing();
|
||||
|
||||
if (selectedOption && 'value' in selectedOption) {
|
||||
setParentState('desktopNotificationSound', selectedOption.value);
|
||||
tryNotificationSound(selectedOption.value);
|
||||
}
|
||||
}, [setParentState]);
|
||||
|
||||
const handleChangeForIncomingCallSoundSelect = useCallback((selectedOption: ValueType<SelectOption>) => {
|
||||
stopTryNotificationRing();
|
||||
|
||||
if (selectedOption && 'value' in selectedOption) {
|
||||
setParentState('callsNotificationSound', selectedOption.value);
|
||||
tryNotificationRing(selectedOption.value);
|
||||
}
|
||||
}, [setParentState]);
|
||||
|
||||
const maximizedSettingInputs = useMemo(() => {
|
||||
const maximizedSettingInputs = [];
|
||||
|
||||
const isMessageNotificationSoundChecked = desktopSound === 'true';
|
||||
const messageSoundSection = (
|
||||
<Fragment key='messageSoundSection'>
|
||||
<div className='checkbox inlineCheckboxSelect'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={desktopSound === 'true'}
|
||||
onChange={handleChangeForMessageNotificationSoundCheckbox}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.messageNotificationSound'
|
||||
defaultMessage='Message notification sound'
|
||||
/>
|
||||
</label>
|
||||
<ReactSelect
|
||||
id='messageNotificationSoundSelect'
|
||||
inputId='messageNotificationSoundSelectInput'
|
||||
className='react-select inlineSelect'
|
||||
classNamePrefix='react-select'
|
||||
options={optionsOfMessageNotificationSoundsSelect}
|
||||
clearable={false}
|
||||
isClearable={false}
|
||||
isSearchable={false}
|
||||
isDisabled={!isMessageNotificationSoundChecked}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'user.settings.notifications.desktopNotificationSound.soundSelectPlaceholder',
|
||||
defaultMessage: 'Select a sound',
|
||||
})}
|
||||
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
|
||||
value={getValueOfMessageNotificationSoundsSelect(desktopNotificationSound)}
|
||||
onChange={handleChangeForMessageNotificationSoundSelect}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(messageSoundSection);
|
||||
|
||||
if (isCallsRingingEnabled) {
|
||||
const isIncomingCallSoundChecked = callsDesktopSound === 'true';
|
||||
const callSoundSection = (
|
||||
<Fragment key='callSoundSection'>
|
||||
<br/>
|
||||
<div className='checkbox inlineCheckboxSelect'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={isIncomingCallSoundChecked}
|
||||
onChange={handleChangeForIncomginCallSoundCheckbox}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.incomingCallSound'
|
||||
defaultMessage='Incoming call sound'
|
||||
/>
|
||||
</label>
|
||||
<ReactSelect
|
||||
id='incomingCallSoundNotificationSelect'
|
||||
inputId='incomingCallSoundNotificationSelectInput'
|
||||
className='react-select inlineSelect'
|
||||
classNamePrefix='react-select'
|
||||
options={optionsOfIncomingCallSoundsSelect}
|
||||
clearable={false}
|
||||
isClearable={false}
|
||||
isSearchable={false}
|
||||
isDisabled={!isIncomingCallSoundChecked}
|
||||
components={{IndicatorSeparator: NoIndicatorSeparatorComponent}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'user.settings.notifications.desktopNotificationSound.soundSelectPlaceholder',
|
||||
defaultMessage: 'Select a sound',
|
||||
})}
|
||||
value={getValueOfIncomingCallSoundsSelect(callsNotificationSound)}
|
||||
onChange={handleChangeForIncomingCallSoundSelect}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
maximizedSettingInputs.push(callSoundSection);
|
||||
}
|
||||
return maximizedSettingInputs;
|
||||
},
|
||||
[
|
||||
desktopSound,
|
||||
handleChangeForMessageNotificationSoundCheckbox,
|
||||
handleChangeForMessageNotificationSoundSelect,
|
||||
desktopNotificationSound,
|
||||
isCallsRingingEnabled,
|
||||
callsDesktopSound,
|
||||
handleChangeForIncomginCallSoundCheckbox,
|
||||
callsNotificationSound,
|
||||
handleChangeForIncomingCallSoundSelect,
|
||||
]);
|
||||
|
||||
function handleChangeForMaxSection(section: string) {
|
||||
stopTryNotificationRing();
|
||||
updateSection(section);
|
||||
}
|
||||
|
||||
function handleChangeForMinSection(section: string) {
|
||||
stopTryNotificationRing();
|
||||
updateSection(section);
|
||||
onCancel();
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
stopTryNotificationRing();
|
||||
onSubmit();
|
||||
}
|
||||
|
||||
if (active) {
|
||||
return (
|
||||
<SettingItemMax
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSounds.title'
|
||||
defaultMessage='Desktop notification sounds'
|
||||
/>
|
||||
}
|
||||
inputs={maximizedSettingInputs}
|
||||
submit={handleSubmit}
|
||||
saving={saving}
|
||||
serverError={error}
|
||||
updateSection={handleChangeForMaxSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItemMin
|
||||
ref={editButtonRef}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSounds.title'
|
||||
defaultMessage='Desktop notification sounds'
|
||||
/>
|
||||
}
|
||||
describe={getCollapsedText(isCallsRingingEnabled, desktopSound, desktopNotificationSound, callsDesktopSound, callsNotificationSound)}
|
||||
section={UserSettingsNotificationSections.DESKTOP_NOTIFICATION_SOUND}
|
||||
updateSection={handleChangeForMinSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function NoIndicatorSeparatorComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const notificationSoundKeys = Array.from(notificationSounds.keys());
|
||||
|
||||
const optionsOfMessageNotificationSoundsSelect: SelectOption[] = notificationSoundKeys.map((soundName) => {
|
||||
if (soundName === 'Bing') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundBing'
|
||||
defaultMessage='Bing'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Crackle') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundCrackle'
|
||||
defaultMessage='Crackle'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Down') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundDown'
|
||||
defaultMessage='Down'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Hello') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundHello'
|
||||
defaultMessage='Hello'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Ripple') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundRipple'
|
||||
defaultMessage='Ripple'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Upstairs') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundUpstairs'
|
||||
defaultMessage='Upstairs'
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: '',
|
||||
label: '',
|
||||
};
|
||||
});
|
||||
|
||||
function getValueOfMessageNotificationSoundsSelect(soundName?: string) {
|
||||
const soundOption = optionsOfMessageNotificationSoundsSelect.find((option) => option.value === soundName);
|
||||
|
||||
if (!soundOption) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return soundOption;
|
||||
}
|
||||
|
||||
const callNotificationSoundKeys = Array.from(callsNotificationSounds.keys());
|
||||
|
||||
const optionsOfIncomingCallSoundsSelect: SelectOption[] = callNotificationSoundKeys.map((soundName) => {
|
||||
if (soundName === 'Dynamic') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundDynamic'
|
||||
defaultMessage='Dynamic'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Calm') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundCalm'
|
||||
defaultMessage='Calm'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Urgent') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundUrgent'
|
||||
defaultMessage='Urgent'
|
||||
/>
|
||||
),
|
||||
};
|
||||
} else if (soundName === 'Cheerful') {
|
||||
return {
|
||||
value: soundName,
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.soundCheerful'
|
||||
defaultMessage='Cheerful'
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: '',
|
||||
label: '',
|
||||
};
|
||||
});
|
||||
|
||||
function getValueOfIncomingCallSoundsSelect(soundName?: string) {
|
||||
const soundOption = optionsOfIncomingCallSoundsSelect.find((option) => option.value === soundName);
|
||||
|
||||
if (!soundOption) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return soundOption;
|
||||
}
|
||||
|
||||
function getCollapsedText(
|
||||
isCallsRingingEnabled: UserSettingsNotificationsProps['isCallsRingingEnabled'],
|
||||
desktopSound: UserNotifyProps['desktop_sound'],
|
||||
desktopNotificationSound: UserNotifyProps['desktop_notification_sound'],
|
||||
callsDesktopSound: UserNotifyProps['calls_desktop_sound'],
|
||||
callsNotificationSound: UserNotifyProps['calls_notification_sound'],
|
||||
) {
|
||||
const desktopNotificationSoundIsSelected = notificationSoundKeys.includes(desktopNotificationSound as string);
|
||||
const callNotificationSoundIsSelected = callNotificationSoundKeys.includes(callsNotificationSound as string);
|
||||
|
||||
let hasCallsSound: boolean | null = null;
|
||||
if (isCallsRingingEnabled && callNotificationSoundIsSelected) {
|
||||
if (callsDesktopSound === 'true') {
|
||||
hasCallsSound = true;
|
||||
} else {
|
||||
hasCallsSound = false;
|
||||
}
|
||||
}
|
||||
|
||||
let hasDesktopSound: boolean | null = null;
|
||||
if (desktopNotificationSoundIsSelected) {
|
||||
if (desktopSound === 'true') {
|
||||
hasDesktopSound = true;
|
||||
} else {
|
||||
hasDesktopSound = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDesktopSound !== null && hasCallsSound !== null) {
|
||||
if (hasDesktopSound && hasCallsSound) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.hasDesktopAndCallsSound'
|
||||
defaultMessage='"{desktopSound}" for messages, "{callsSound}" for calls'
|
||||
values={{
|
||||
desktopSound: desktopNotificationSound,
|
||||
callsSound: callsNotificationSound,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else if (!hasDesktopSound && hasCallsSound) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.noDesktopAndhasCallsSound'
|
||||
defaultMessage='No sound for messages, "{callsSound}" for calls'
|
||||
values={{callsSound: callsNotificationSound}}
|
||||
/>
|
||||
);
|
||||
} else if (hasDesktopSound && !hasCallsSound) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.hasDesktopAndNoCallsSound'
|
||||
defaultMessage='"{desktopSound}" for messages, no sound for calls'
|
||||
values={{desktopSound: desktopNotificationSound}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.noDesktopAndNoCallsSound'
|
||||
defaultMessage='No sound'
|
||||
/>
|
||||
);
|
||||
} else if (hasDesktopSound !== null && hasCallsSound === null) {
|
||||
if (hasDesktopSound) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.hasDesktopSound'
|
||||
defaultMessage='"{desktopSound}" for messages'
|
||||
values={{desktopSound: desktopNotificationSound}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.noDesktopSound'
|
||||
defaultMessage='No sound'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.desktopNotificationSound.noValidSound'
|
||||
defaultMessage='Configure desktop notification sounds'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(DesktopNotificationSoundsSettings);
|
||||
@@ -95,7 +95,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
submit={[Function]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -109,16 +109,16 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
id="settingTitle"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
>
|
||||
<span>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</span>
|
||||
</FormattedMessage>
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-9 col-sm-offset-3"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -289,7 +289,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
section="email"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -308,7 +308,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
section="email"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -327,7 +327,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
section="email"
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -479,7 +479,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
submit={[Function]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -493,16 +493,16 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
id="settingTitle"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
>
|
||||
<span>
|
||||
Email Notifications
|
||||
Email notifications
|
||||
</span>
|
||||
</FormattedMessage>
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-9 col-sm-offset-3"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -744,7 +744,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
serverError=""
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -824,7 +824,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
submit={[Function]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -898,16 +898,8 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
<React.Fragment>
|
||||
<hr />
|
||||
<fieldset>
|
||||
<legend
|
||||
className="form-legend"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Thread reply notifications"
|
||||
id="user.settings.notifications.threads.desktop"
|
||||
/>
|
||||
</legend>
|
||||
<div
|
||||
className="checkbox"
|
||||
className="checkbox single-checkbox"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
@@ -918,19 +910,10 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
type="checkbox"
|
||||
/>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Notify me about threads I'm following"
|
||||
id="user.settings.notifications.threads.allActivity"
|
||||
defaultMessage="Notify me about replies to threads I’m following"
|
||||
id="user.settings.notifications.email.notifyForthreads"
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
</div>
|
||||
<div
|
||||
className="mt-5"
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="When enabled, any reply to a thread you're following will send an email notification."
|
||||
id="user.settings.notifications.email_threads"
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</React.Fragment>,
|
||||
@@ -942,7 +925,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
submit={[Function]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
@@ -1022,7 +1005,7 @@ exports[`components/user_settings/notifications/EmailNotificationSetting should
|
||||
submit={[Function]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Email Notifications"
|
||||
defaultMessage="Email notifications"
|
||||
id="user.settings.notifications.emailNotifications"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -237,8 +237,8 @@ export default class EmailNotificationSetting extends React.PureComponent<Props,
|
||||
ref={this.editButtonRef}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={'user.settings.notifications.emailNotifications'}
|
||||
defaultMessage={'Email Notifications'}
|
||||
id='user.settings.notifications.emailNotifications'
|
||||
defaultMessage='Email notifications'
|
||||
/>
|
||||
}
|
||||
describe={description}
|
||||
@@ -254,8 +254,8 @@ export default class EmailNotificationSetting extends React.PureComponent<Props,
|
||||
<SettingItemMax
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={'user.settings.notifications.emailNotifications'}
|
||||
defaultMessage={'Email Notifications'}
|
||||
id='user.settings.notifications.emailNotifications'
|
||||
defaultMessage='Email notifications'
|
||||
/>
|
||||
}
|
||||
inputs={[
|
||||
@@ -334,13 +334,7 @@ export default class EmailNotificationSetting extends React.PureComponent<Props,
|
||||
<React.Fragment key='userNotificationEmailThreadsOptions'>
|
||||
<hr/>
|
||||
<fieldset>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.threads.desktop'
|
||||
defaultMessage='Thread reply notifications'
|
||||
/>
|
||||
</legend>
|
||||
<div className='checkbox'>
|
||||
<div className='checkbox single-checkbox'>
|
||||
<label>
|
||||
<input
|
||||
id='desktopThreadsNotificationAllActivity'
|
||||
@@ -350,17 +344,10 @@ export default class EmailNotificationSetting extends React.PureComponent<Props,
|
||||
onChange={this.handleThreadsOnChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.threads.allActivity'
|
||||
defaultMessage={'Notify me about threads I\'m following'}
|
||||
id='user.settings.notifications.email.notifyForthreads'
|
||||
defaultMessage={'Notify me about replies to threads I’m following'}
|
||||
/>
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='mt-5'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.email_threads'
|
||||
defaultMessage={'When enabled, any reply to a thread you\'re following will send an email notification.'}
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</React.Fragment>
|
||||
@@ -371,8 +358,8 @@ export default class EmailNotificationSetting extends React.PureComponent<Props,
|
||||
<SettingItemMax
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={'user.settings.notifications.emailNotifications'}
|
||||
defaultMessage={'Email Notifications'}
|
||||
id='user.settings.notifications.emailNotifications'
|
||||
defaultMessage='Email notifications'
|
||||
/>
|
||||
}
|
||||
inputs={[
|
||||
|
||||
@@ -48,12 +48,11 @@ exports[`components/user_settings/notifications/ManageAutoResponder should match
|
||||
submit={[MockFunction]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Automatic Direct Message Replies"
|
||||
defaultMessage="Automatic direct message replies"
|
||||
id="user.settings.notifications.autoResponder"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
width="medium"
|
||||
>
|
||||
<section
|
||||
className="section-max form-horizontal "
|
||||
@@ -63,16 +62,16 @@ exports[`components/user_settings/notifications/ManageAutoResponder should match
|
||||
id="settingTitle"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Automatic Direct Message Replies"
|
||||
defaultMessage="Automatic direct message replies"
|
||||
id="user.settings.notifications.autoResponder"
|
||||
>
|
||||
<span>
|
||||
Automatic Direct Message Replies
|
||||
Automatic direct message replies
|
||||
</span>
|
||||
</FormattedMessage>
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-10 col-sm-offset-2"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
@@ -262,12 +261,11 @@ exports[`components/user_settings/notifications/ManageAutoResponder should match
|
||||
submit={[MockFunction]}
|
||||
title={
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Automatic Direct Message Replies"
|
||||
defaultMessage="Automatic direct message replies"
|
||||
id="user.settings.notifications.autoResponder"
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
width="medium"
|
||||
>
|
||||
<section
|
||||
className="section-max form-horizontal "
|
||||
@@ -277,16 +275,16 @@ exports[`components/user_settings/notifications/ManageAutoResponder should match
|
||||
id="settingTitle"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Automatic Direct Message Replies"
|
||||
defaultMessage="Automatic direct message replies"
|
||||
id="user.settings.notifications.autoResponder"
|
||||
>
|
||||
<span>
|
||||
Automatic Direct Message Replies
|
||||
Automatic direct message replies
|
||||
</span>
|
||||
</FormattedMessage>
|
||||
</h4>
|
||||
<div
|
||||
className="col-sm-10 col-sm-offset-2"
|
||||
className="sectionContent col-sm-10 col-sm-offset-2"
|
||||
>
|
||||
<div
|
||||
className="setting-list"
|
||||
|
||||
@@ -106,10 +106,9 @@ export default class ManageAutoResponder extends React.PureComponent<Props> {
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.autoResponder'
|
||||
defaultMessage='Automatic Direct Message Replies'
|
||||
defaultMessage='Automatic direct message replies'
|
||||
/>
|
||||
}
|
||||
width='medium'
|
||||
shiftEnter={true}
|
||||
submit={this.props.submit}
|
||||
saving={this.props.saving}
|
||||
|
||||
@@ -5,9 +5,10 @@ import React from 'react';
|
||||
import {type IntlShape} from 'react-intl';
|
||||
|
||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||
import {NotificationLevels} from 'utils/constants';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import UserSettingsNotifications from './user_settings_notifications';
|
||||
import UserSettingsNotifications, {areDesktopAndMobileSettingsDifferent} from './user_settings_notifications';
|
||||
|
||||
describe('components/user_settings/display/UserSettingsDisplay', () => {
|
||||
const defaultProps = {
|
||||
@@ -20,7 +21,7 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
||||
isCollapsedThreadsEnabled: true,
|
||||
sendPushNotifications: false,
|
||||
enableAutoResponder: false,
|
||||
isCallsRingingEnabled: true,
|
||||
isCallsRingingEnabled: false,
|
||||
intl: {} as IntlShape,
|
||||
isEnterpriseOrCloudOrSKUStarterFree: false,
|
||||
isEnterpriseReady: true,
|
||||
@@ -62,3 +63,27 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
|
||||
expect(screen.getByText('Reply notifications')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('areDesktopAndMobileSettingsDifferent', () => {
|
||||
test('should return true when desktop and push notification levels are different', () => {
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.MENTION, NotificationLevels.ALL, NotificationLevels.NONE, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.NONE, NotificationLevels.ALL, NotificationLevels.MENTION, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.NONE, NotificationLevels.MENTION, NotificationLevels.NONE, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.MENTION, NotificationLevels.NONE, NotificationLevels.NONE, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.NONE, NotificationLevels.NONE, NotificationLevels.NONE, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.MENTION, NotificationLevels.ALL, NotificationLevels.MENTION, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.NONE, NotificationLevels.ALL, NotificationLevels.NONE, true)).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false when desktop and push notification levels are the same', () => {
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.ALL, NotificationLevels.ALL, NotificationLevels.ALL, true)).toBe(false);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.MENTION, NotificationLevels.MENTION, NotificationLevels.MENTION, NotificationLevels.MENTION, false)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true any of desktop or push settings are undefined', () => {
|
||||
expect(areDesktopAndMobileSettingsDifferent(undefined as any, NotificationLevels.ALL, NotificationLevels.ALL, NotificationLevels.ALL, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, undefined as any, NotificationLevels.ALL, NotificationLevels.ALL, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.ALL, undefined as any, NotificationLevels.ALL, true)).toBe(true);
|
||||
expect(areDesktopAndMobileSettingsDifferent(NotificationLevels.ALL, NotificationLevels.ALL, NotificationLevels.ALL, undefined as any, true)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,21 +11,19 @@ import type {Styles as ReactSelectStyles, ValueType} from 'react-select';
|
||||
import CreatableReactSelect from 'react-select/creatable';
|
||||
|
||||
import {LightbulbOutlineIcon} from '@mattermost/compass-icons/components';
|
||||
import type {ServerError} from '@mattermost/types/errors';
|
||||
import type {UserNotifyProps, UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||
|
||||
import ExternalLink from 'components/external_link';
|
||||
import SettingItem from 'components/setting_item';
|
||||
import SettingItemMax from 'components/setting_item_max';
|
||||
import RestrictedIndicator from 'components/widgets/menu/menu_items/restricted_indicator';
|
||||
|
||||
import Constants, {NotificationLevels, MattermostFeatures, LicenseSkus} from 'utils/constants';
|
||||
import Constants, {NotificationLevels, MattermostFeatures, LicenseSkus, UserSettingsNotificationSections} from 'utils/constants';
|
||||
import {stopTryNotificationRing} from 'utils/notification_sounds';
|
||||
import {a11yFocus} from 'utils/utils';
|
||||
|
||||
import DesktopNotificationSettings from './desktop_notification_setting/desktop_notification_settings';
|
||||
import DesktopAndMobileNotificationSettings from './desktop_and_mobile_notification_setting';
|
||||
import DesktopNotificationSoundsSettings from './desktop_notification_sounds_setting';
|
||||
import EmailNotificationSetting from './email_notification_setting';
|
||||
import ManageAutoResponder from './manage_auto_responder/manage_auto_responder';
|
||||
|
||||
@@ -50,7 +48,7 @@ type OwnProps = {
|
||||
collapseModal: () => void;
|
||||
}
|
||||
|
||||
type Props = PropsFromRedux & OwnProps & WrappedComponentProps;
|
||||
export type Props = PropsFromRedux & OwnProps & WrappedComponentProps;
|
||||
|
||||
type State = {
|
||||
enableEmail: UserNotifyProps['email'];
|
||||
@@ -77,6 +75,7 @@ type State = {
|
||||
notifyCommentsLevel: UserNotifyProps['comments'];
|
||||
isSaving: boolean;
|
||||
serverError: string;
|
||||
desktopAndMobileSettingsDifferent: boolean;
|
||||
};
|
||||
|
||||
function getDefaultStateFromProps(props: Props): State {
|
||||
@@ -97,6 +96,7 @@ function getDefaultStateFromProps(props: Props): State {
|
||||
id: 'user.settings.notifications.autoResponderDefault',
|
||||
defaultMessage: 'Hello, I am out of office and unable to respond to messages.',
|
||||
});
|
||||
let desktopAndMobileSettingsDifferent = true;
|
||||
|
||||
if (props.user.notify_props) {
|
||||
if (props.user.notify_props.desktop) {
|
||||
@@ -143,6 +143,10 @@ function getDefaultStateFromProps(props: Props): State {
|
||||
if (props.user.notify_props.auto_responder_message) {
|
||||
autoResponderMessage = props.user.notify_props.auto_responder_message;
|
||||
}
|
||||
|
||||
if (props.user.notify_props.desktop && props.user.notify_props.push) {
|
||||
desktopAndMobileSettingsDifferent = areDesktopAndMobileSettingsDifferent(props.user.notify_props.desktop, props.user.notify_props.push, props.user.notify_props?.desktop_threads, props.user.notify_props?.push_threads, props.isCollapsedThreadsEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
let usernameKey = false;
|
||||
@@ -211,6 +215,7 @@ function getDefaultStateFromProps(props: Props): State {
|
||||
notifyCommentsLevel: comments,
|
||||
isSaving: false,
|
||||
serverError: '',
|
||||
desktopAndMobileSettingsDifferent,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -235,8 +240,6 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
data.desktop = this.state.desktopActivity;
|
||||
data.desktop_threads = this.state.desktopThreads;
|
||||
data.email_threads = this.state.emailThreads;
|
||||
data.push_threads = this.state.pushThreads;
|
||||
data.push = this.state.pushActivity;
|
||||
data.push_status = this.state.pushStatus;
|
||||
data.comments = this.state.notifyCommentsLevel;
|
||||
data.auto_responder_active = this.state.autoResponderActive ? 'true' : 'false';
|
||||
@@ -244,6 +247,14 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
data.first_name = this.state.firstNameKey ? 'true' : 'false';
|
||||
data.channel = this.state.channelKey ? 'true' : 'false';
|
||||
|
||||
if (this.state.desktopAndMobileSettingsDifferent) {
|
||||
data.push = this.state.pushActivity;
|
||||
data.push_threads = this.state.pushThreads;
|
||||
} else {
|
||||
data.push = this.state.desktopActivity;
|
||||
data.push_threads = this.state.desktopThreads;
|
||||
}
|
||||
|
||||
if (!data.auto_responder_message || data.auto_responder_message === '') {
|
||||
data.auto_responder_message = this.props.intl.formatMessage({
|
||||
id: 'user.settings.notifications.autoResponderDefault',
|
||||
@@ -273,7 +284,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
this.setState({isSaving: true});
|
||||
stopTryNotificationRing();
|
||||
|
||||
const {data: updatedUser, error} = await this.props.updateMe({notify_props: data}) as ActionResult<Partial<UserProfile>, ServerError>; // Fix in MM-46907
|
||||
const {data: updatedUser, error} = await this.props.updateMe({notify_props: data});
|
||||
if (updatedUser) {
|
||||
this.handleUpdateSection('');
|
||||
this.setState(getDefaultStateFromProps(this.props));
|
||||
@@ -305,26 +316,11 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
this.setState((prevState) => ({...prevState, ...data}));
|
||||
};
|
||||
|
||||
handleNotifyPushThread = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
const pushThreads = e.target.checked ? NotificationLevels.ALL : NotificationLevels.MENTION;
|
||||
this.setState({pushThreads});
|
||||
};
|
||||
|
||||
handleNotifyCommentsRadio = (notifyCommentsLevel: UserNotifyProps['comments'], e?: React.ChangeEvent): void => {
|
||||
this.setState({notifyCommentsLevel});
|
||||
a11yFocus(e?.currentTarget as HTMLElement);
|
||||
};
|
||||
|
||||
handlePushRadio = (pushActivity: UserNotifyProps['push'], e?: React.ChangeEvent): void => {
|
||||
this.setState({pushActivity});
|
||||
a11yFocus(e?.currentTarget as HTMLElement);
|
||||
};
|
||||
|
||||
handlePushStatusRadio = (pushStatus: UserNotifyProps['push_status'], e?: React.ChangeEvent): void => {
|
||||
this.setState({pushStatus});
|
||||
a11yFocus(e?.currentTarget as HTMLElement);
|
||||
};
|
||||
|
||||
handleEmailRadio = (enableEmail: UserNotifyProps['email']): void => {
|
||||
this.setState({enableEmail});
|
||||
};
|
||||
@@ -472,311 +468,10 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
this.props.closeModal();
|
||||
};
|
||||
|
||||
createPushNotificationSection = () => {
|
||||
const active = this.props.activeSection === 'push';
|
||||
const inputs = [];
|
||||
let submit = null;
|
||||
let max = null;
|
||||
|
||||
if (active) {
|
||||
if (this.props.sendPushNotifications) {
|
||||
const pushActivityRadio = [false, false, false];
|
||||
if (this.state.pushActivity === NotificationLevels.ALL) {
|
||||
pushActivityRadio[0] = true;
|
||||
} else if (this.state.pushActivity === NotificationLevels.NONE) {
|
||||
pushActivityRadio[2] = true;
|
||||
} else {
|
||||
pushActivityRadio[1] = true;
|
||||
}
|
||||
|
||||
const pushStatusRadio = [false, false, false];
|
||||
if (this.state.pushStatus === Constants.UserStatuses.ONLINE) {
|
||||
pushStatusRadio[0] = true;
|
||||
} else if (this.state.pushStatus === Constants.UserStatuses.AWAY) {
|
||||
pushStatusRadio[1] = true;
|
||||
} else {
|
||||
pushStatusRadio[2] = true;
|
||||
}
|
||||
|
||||
let pushThreadsNotificationSelection = null;
|
||||
if (this.props.isCollapsedThreadsEnabled && this.state.pushActivity === NotificationLevels.MENTION) {
|
||||
pushThreadsNotificationSelection = (
|
||||
<React.Fragment key='userNotificationPushThreadsOptions'>
|
||||
<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={this.state.pushThreads === NotificationLevels.ALL}
|
||||
onChange={this.handleNotifyPushThread}
|
||||
/>
|
||||
<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>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
let pushStatusSettings;
|
||||
if (this.state.pushActivity !== NotificationLevels.NONE) {
|
||||
pushStatusSettings = (
|
||||
<React.Fragment key='userNotificationPushStatusOptions'>
|
||||
<hr/>
|
||||
<fieldset>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.push_notification.status'
|
||||
defaultMessage='Trigger push notifications when'
|
||||
/>
|
||||
</legend>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='pushNotificationOnline'
|
||||
type='radio'
|
||||
name='pushNotificationStatus'
|
||||
checked={pushStatusRadio[0]}
|
||||
onChange={this.handlePushStatusRadio.bind(this, Constants.UserStatuses.ONLINE)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.online'
|
||||
defaultMessage='Online, away or offline'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='pushNotificationAway'
|
||||
type='radio'
|
||||
name='pushNotificationStatus'
|
||||
checked={pushStatusRadio[1]}
|
||||
onChange={this.handlePushStatusRadio.bind(this, Constants.UserStatuses.AWAY)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.away'
|
||||
defaultMessage='Away or offline'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='pushNotificationOffline'
|
||||
type='radio'
|
||||
name='pushNotificationStatus'
|
||||
checked={pushStatusRadio[2]}
|
||||
onChange={this.handlePushStatusRadio.bind(this, Constants.UserStatuses.OFFLINE)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.offline'
|
||||
defaultMessage='Offline'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='mt-5'>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.status_info'
|
||||
defaultMessage='Notification alerts are only pushed to your mobile device when your availability matches the selection above.'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
inputs.push(
|
||||
<div>
|
||||
<fieldset key='userNotificationLevelOption'>
|
||||
<legend className='form-legend'>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.send'
|
||||
defaultMessage='Send mobile push notifications'
|
||||
/>
|
||||
</legend>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='pushNotificationAllActivity'
|
||||
type='radio'
|
||||
name='pushNotificationLevel'
|
||||
checked={pushActivityRadio[0]}
|
||||
onChange={this.handlePushRadio.bind(this, NotificationLevels.ALL)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.allActivity'
|
||||
defaultMessage='For all activity'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='pushNotificationMentions'
|
||||
type='radio'
|
||||
name='pushNotificationLevel'
|
||||
checked={pushActivityRadio[1]}
|
||||
onChange={this.handlePushRadio.bind(this, NotificationLevels.MENTION)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.onlyMentions'
|
||||
defaultMessage='For mentions and direct messages'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
id='pushNotificationNever'
|
||||
type='radio'
|
||||
name='pushNotificationLevel'
|
||||
checked={pushActivityRadio[2]}
|
||||
onChange={this.handlePushRadio.bind(this, NotificationLevels.NONE)}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='mt-5'>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.info'
|
||||
defaultMessage='Notification alerts are pushed to your mobile device when there is activity in Mattermost.'
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>,
|
||||
pushStatusSettings,
|
||||
pushThreadsNotificationSelection,
|
||||
);
|
||||
|
||||
submit = this.handleSubmit;
|
||||
} else {
|
||||
inputs.push(
|
||||
<div
|
||||
key='oauthEmailInfo'
|
||||
className='pt-2'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.disabled_long'
|
||||
defaultMessage='Push notifications have not been enabled by your System Administrator.'
|
||||
/>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
max = (
|
||||
<SettingItemMax
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.push', defaultMessage: 'Mobile Push Notifications'})}
|
||||
inputs={inputs}
|
||||
submit={submit}
|
||||
serverError={this.state.serverError}
|
||||
updateSection={this.handleUpdateSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let describe: JSX.Element;
|
||||
if (this.state.pushActivity === NotificationLevels.ALL) {
|
||||
if (this.state.pushStatus === Constants.UserStatuses.AWAY) {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.allActivityAway'
|
||||
defaultMessage='For all activity when away or offline'
|
||||
/>
|
||||
);
|
||||
} else if (this.state.pushStatus === Constants.UserStatuses.OFFLINE) {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.allActivityOffline'
|
||||
defaultMessage='For all activity when offline'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.allActivityOnline'
|
||||
defaultMessage='For all activity when online, away or offline'
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (this.state.pushActivity === NotificationLevels.NONE) {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.never'
|
||||
defaultMessage='Never'
|
||||
/>
|
||||
);
|
||||
} else if (this.props.sendPushNotifications) {
|
||||
if (this.state.pushStatus === Constants.UserStatuses.AWAY) { //eslint-disable-line no-lonely-if
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.onlyMentionsAway'
|
||||
defaultMessage='For mentions and direct messages when away or offline'
|
||||
/>
|
||||
);
|
||||
} else if (this.state.pushStatus === Constants.UserStatuses.OFFLINE) {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.onlyMentionsOffline'
|
||||
defaultMessage='For mentions and direct messages when offline'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.onlyMentionsOnline'
|
||||
defaultMessage='For mentions and direct messages when online, away or offline'
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
describe = (
|
||||
<FormattedMessage
|
||||
id='user.settings.push_notification.disabled'
|
||||
defaultMessage='Push notifications are not enabled'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingItem
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.push', defaultMessage: 'Mobile Push Notifications'})}
|
||||
active={active}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
describe={describe}
|
||||
section={'push'}
|
||||
updateSection={this.handleUpdateSection}
|
||||
max={max}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
createKeywordsWithNotificationSection = () => {
|
||||
const serverError = this.state.serverError;
|
||||
const user = this.props.user;
|
||||
const isSectionExpanded = this.props.activeSection === 'keysWithNotification';
|
||||
const isSectionExpanded = this.props.activeSection === UserSettingsNotificationSections.KEYWORDS_MENTIONS;
|
||||
|
||||
let expandedSection = null;
|
||||
if (isSectionExpanded) {
|
||||
@@ -901,7 +596,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
|
||||
expandedSection = (
|
||||
<SettingItemMax
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithNotification.title', defaultMessage: 'Keywords That Trigger Notifications'})}
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithNotification.title', defaultMessage: 'Keywords that trigger notifications'})}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
saving={this.state.isSaving}
|
||||
@@ -932,8 +627,8 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<SettingItem
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithNotification.title', defaultMessage: 'Keywords That Trigger Notifications'})}
|
||||
section='keysWithNotification'
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithNotification.title', defaultMessage: 'Keywords that trigger notifications'})}
|
||||
section={UserSettingsNotificationSections.KEYWORDS_MENTIONS}
|
||||
active={isSectionExpanded}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
describe={collapsedDescription}
|
||||
@@ -943,7 +638,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
createKeywordsWithHighlightSection = () => {
|
||||
const isSectionExpanded = this.props.activeSection === 'keysWithHighlight';
|
||||
const isSectionExpanded = this.props.activeSection === UserSettingsNotificationSections.KEYWORDS_HIGHLIGHT;
|
||||
|
||||
let expandedSection = null;
|
||||
if (isSectionExpanded) {
|
||||
@@ -990,7 +685,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
|
||||
expandedSection = (
|
||||
<SettingItemMax
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithHighlight.title', defaultMessage: 'Keywords That Get Highlighted (Without Notifications)'})}
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithHighlight.title', defaultMessage: 'Keywords that get highlighted (without notifications)'})}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
saving={this.state.isSaving}
|
||||
@@ -1063,8 +758,8 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<SettingItem
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithHighlight.title', defaultMessage: 'Keywords That Get Highlighted (Without Notifications)'})}
|
||||
section='keysWithHighlight'
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.keywordsWithHighlight.title', defaultMessage: 'Keywords that get highlighted (without notifications)'})}
|
||||
section={UserSettingsNotificationSections.KEYWORDS_HIGHLIGHT}
|
||||
active={isSectionExpanded}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
describe={collapsedDescription}
|
||||
@@ -1078,9 +773,8 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
createCommentsSection = () => {
|
||||
const serverError = this.state.serverError;
|
||||
|
||||
const active = this.props.activeSection === 'comments';
|
||||
let max = null;
|
||||
if (active) {
|
||||
if (this.props.activeSection === UserSettingsNotificationSections.REPLY_NOTIFCATIONS) {
|
||||
const commentsActive = [false, false, false];
|
||||
if (this.state.notifyCommentsLevel === 'never') {
|
||||
commentsActive[2] = true;
|
||||
@@ -1097,7 +791,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
<legend className='form-legend hidden-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.comments'
|
||||
defaultMessage='Reply Notifications'
|
||||
defaultMessage='Reply notifications'
|
||||
/>
|
||||
</legend>
|
||||
<div className='radio'>
|
||||
@@ -1161,7 +855,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
|
||||
max = (
|
||||
<SettingItemMax
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.comments', defaultMessage: 'Reply Notifications'})}
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.comments', defaultMessage: 'Reply notifications'})}
|
||||
extraInfo={extraInfo}
|
||||
inputs={inputs}
|
||||
submit={this.handleSubmit}
|
||||
@@ -1199,9 +893,9 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
return (
|
||||
<SettingItem
|
||||
title={this.props.intl.formatMessage({id: 'user.settings.notifications.comments', defaultMessage: 'Reply notifications'})}
|
||||
active={active}
|
||||
active={this.props.activeSection === UserSettingsNotificationSections.REPLY_NOTIFCATIONS}
|
||||
describe={describe}
|
||||
section={'comments'}
|
||||
section={UserSettingsNotificationSections.REPLY_NOTIFCATIONS}
|
||||
updateSection={this.handleUpdateSection}
|
||||
max={max}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
@@ -1224,16 +918,16 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<SettingItem
|
||||
active={this.props.activeSection === 'auto-responder'}
|
||||
active={this.props.activeSection === UserSettingsNotificationSections.AUTO_RESPONDER}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.autoResponder'
|
||||
defaultMessage='Automatic Direct Message Replies'
|
||||
defaultMessage='Automatic direct message replies'
|
||||
/>
|
||||
}
|
||||
describe={describe}
|
||||
section={'auto-responder'}
|
||||
section={UserSettingsNotificationSections.AUTO_RESPONDER}
|
||||
updateSection={this.handleUpdateSection}
|
||||
max={(
|
||||
<div>
|
||||
@@ -1254,12 +948,13 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const pushNotificationSection = this.createPushNotificationSection();
|
||||
const keywordsWithNotificationSection = this.createKeywordsWithNotificationSection();
|
||||
const keywordsWithHighlightSection = this.createKeywordsWithHighlightSection();
|
||||
const commentsSection = this.createCommentsSection();
|
||||
const autoResponderSection = this.createAutoResponderSection();
|
||||
|
||||
const areAllSectionsInactive = this.props.activeSection === '';
|
||||
|
||||
return (
|
||||
<div id='notificationSettings'>
|
||||
<SettingMobileHeader
|
||||
@@ -1268,7 +963,7 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
text={
|
||||
<FormattedMessage
|
||||
id='user.settings.notifications.title'
|
||||
defaultMessage='Notification Settings'
|
||||
defaultMessage='Notification settings'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -1302,42 +997,56 @@ class NotificationsTab extends React.PureComponent<Props, State> {
|
||||
}
|
||||
/>
|
||||
<div className='divider-dark first'/>
|
||||
<DesktopNotificationSettings
|
||||
active={this.props.activeSection === 'desktop'}
|
||||
<DesktopAndMobileNotificationSettings
|
||||
active={this.props.activeSection === UserSettingsNotificationSections.DESKTOP_AND_MOBILE}
|
||||
updateSection={this.handleUpdateSection}
|
||||
onSubmit={this.handleSubmit}
|
||||
onCancel={this.handleCancel}
|
||||
saving={this.state.isSaving}
|
||||
error={this.state.serverError}
|
||||
setParentState={this.setStateValue}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
areAllSectionsInactive={areAllSectionsInactive}
|
||||
isCollapsedThreadsEnabled={this.props.isCollapsedThreadsEnabled}
|
||||
activity={this.state.desktopActivity}
|
||||
threads={this.state.desktopThreads}
|
||||
sound={this.state.desktopSound}
|
||||
callsSound={this.state.callsDesktopSound}
|
||||
selectedSound={this.state.desktopNotificationSound || 'default'}
|
||||
callsSelectedSound={this.state.callsNotificationSound || 'default'}
|
||||
desktopActivity={this.state.desktopActivity}
|
||||
pushActivity={this.state.pushActivity}
|
||||
sendPushNotifications={this.props.sendPushNotifications}
|
||||
pushStatus={this.state.pushStatus}
|
||||
desktopThreads={this.state.desktopThreads}
|
||||
pushThreads={this.state.pushThreads}
|
||||
desktopAndMobileSettingsDifferent={this.state.desktopAndMobileSettingsDifferent}
|
||||
/>
|
||||
<div className='divider-light'/>
|
||||
<DesktopNotificationSoundsSettings
|
||||
active={this.props.activeSection === UserSettingsNotificationSections.DESKTOP_NOTIFICATION_SOUND}
|
||||
updateSection={this.handleUpdateSection}
|
||||
onSubmit={this.handleSubmit}
|
||||
onCancel={this.handleCancel}
|
||||
saving={this.state.isSaving}
|
||||
error={this.state.serverError}
|
||||
setParentState={this.setStateValue}
|
||||
areAllSectionsInactive={areAllSectionsInactive}
|
||||
desktopSound={this.state.desktopSound}
|
||||
desktopNotificationSound={this.state.desktopNotificationSound}
|
||||
isCallsRingingEnabled={this.props.isCallsRingingEnabled}
|
||||
callsDesktopSound={this.state.callsDesktopSound}
|
||||
callsNotificationSound={this.state.callsNotificationSound}
|
||||
/>
|
||||
<div className='divider-light'/>
|
||||
<EmailNotificationSetting
|
||||
active={this.props.activeSection === 'email'}
|
||||
active={this.props.activeSection === UserSettingsNotificationSections.EMAIL}
|
||||
updateSection={this.handleUpdateSection}
|
||||
onSubmit={this.handleSubmit}
|
||||
onCancel={this.handleCancel}
|
||||
saving={this.state.isSaving}
|
||||
error={this.state.serverError}
|
||||
setParentState={this.setStateValue}
|
||||
areAllSectionsInactive={this.props.activeSection === ''}
|
||||
areAllSectionsInactive={areAllSectionsInactive}
|
||||
isCollapsedThreadsEnabled={this.props.isCollapsedThreadsEnabled}
|
||||
enableEmail={this.state.enableEmail === 'true'}
|
||||
onChange={this.handleEmailRadio}
|
||||
threads={this.state.emailThreads || ''}
|
||||
/>
|
||||
<div className='divider-light'/>
|
||||
{pushNotificationSection}
|
||||
<div className='divider-light'/>
|
||||
{keywordsWithNotificationSection}
|
||||
{(!this.props.isEnterpriseOrCloudOrSKUStarterFree && this.props.isEnterpriseReady) && (
|
||||
<>
|
||||
@@ -1414,4 +1123,39 @@ const customKeywordsSelectorStyles: ReactSelectStyles = {
|
||||
})),
|
||||
};
|
||||
|
||||
const validNotificationLevels = Object.values(NotificationLevels);
|
||||
|
||||
export function areDesktopAndMobileSettingsDifferent(
|
||||
desktopActivity: UserNotifyProps['desktop'],
|
||||
pushActivity: UserNotifyProps['push'],
|
||||
desktopThreads?: UserNotifyProps['desktop_threads'],
|
||||
pushThreads?: UserNotifyProps['push_threads'],
|
||||
isCollapsedThreadsEnabled?: boolean,
|
||||
): boolean {
|
||||
if (!desktopActivity || !pushActivity || !desktopThreads || !pushThreads) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
!validNotificationLevels.includes(desktopActivity) ||
|
||||
!validNotificationLevels.includes(pushActivity) ||
|
||||
!validNotificationLevels.includes(desktopThreads) ||
|
||||
!validNotificationLevels.includes(pushThreads)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (desktopActivity === pushActivity) {
|
||||
if (isCollapsedThreadsEnabled) {
|
||||
if (desktopThreads === pushThreads) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default injectIntl(NotificationsTab);
|
||||
|
||||
@@ -77,7 +77,6 @@ exports[`MfaSection rendering when section is expanded and MFA is active and enf
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
width="medium"
|
||||
/>
|
||||
`;
|
||||
|
||||
@@ -118,7 +117,6 @@ exports[`MfaSection rendering when section is expanded and MFA is active but not
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
width="medium"
|
||||
/>
|
||||
`;
|
||||
|
||||
@@ -159,7 +157,6 @@ exports[`MfaSection rendering when section is expanded and MFA is not active 1`]
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
width="medium"
|
||||
/>
|
||||
`;
|
||||
|
||||
@@ -200,6 +197,5 @@ exports[`MfaSection rendering when section is expanded with a server error 1`] =
|
||||
/>
|
||||
}
|
||||
updateSection={[MockFunction]}
|
||||
width="medium"
|
||||
/>
|
||||
`;
|
||||
|
||||
@@ -219,7 +219,6 @@ export default class MfaSection extends React.PureComponent<Props, State> {
|
||||
extraInfo={this.renderHelpText()}
|
||||
serverError={this.state.serverError}
|
||||
updateSection={this.props.updateSection}
|
||||
width='medium'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -627,7 +627,7 @@ export default class UserAccessTokenSection extends React.PureComponent<Props, S
|
||||
infoPosition='top'
|
||||
serverError={this.state.serverError}
|
||||
updateSection={this.props.updateSection}
|
||||
width='full'
|
||||
isFullWidth={true}
|
||||
saving={this.state.saving}
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
|
||||
@@ -918,7 +918,7 @@ export class SecurityTab extends React.PureComponent<Props, State> {
|
||||
inputs={inputs}
|
||||
serverError={this.state.serverError}
|
||||
updateSection={this.handleUpdateSection}
|
||||
width='full'
|
||||
isFullWidth={true}
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
id='user.settings.security.close'
|
||||
|
||||
Ссылка в новой задаче
Block a user