[MM-57081] Spacing, font and cutoff issues on Channel notifications preferences modal (#26478)

Этот коммит содержится в:
M-ZubairAhmed
2024-03-21 10:25:10 +00:00
коммит произвёл GitHub
родитель e43ed713d8
Коммит 959e333031
25 изменённых файлов: 609 добавлений и 856 удалений

Просмотреть файл

@@ -53,10 +53,15 @@ describe('CRT Desktop notifications', () => {
cy.visit(testChannelUrl); cy.visit(testChannelUrl);
cy.uiOpenChannelMenu('Notification Preferences'); cy.uiOpenChannelMenu('Notification Preferences');
cy.get('[data-testid="muteChannel"]').click().then(() => {
cy.get('.AlertBanner--app').should('be.visible'); // # Click on Mute Channel to Unmute Channel
}); cy.findByText('Mute channel').should('be.visible').click({force: true});
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// * Verify that channel is muted alert is visible
cy.findByText('This channel is muted').should('be.visible');
// # Save the changes
cy.findByText('Save').should('be.visible').click();
// Setup notification spy // Setup notification spy
spyNotificationAs('notifySpy', 'granted'); spyNotificationAs('notifySpy', 'granted');
@@ -64,8 +69,8 @@ describe('CRT Desktop notifications', () => {
// # Set users notification settings // # Set users notification settings
cy.uiOpenChannelMenu('Notification Preferences'); cy.uiOpenChannelMenu('Notification Preferences');
// # click on Mute Channel to Unmute Channel // # Click on Mute Channel to Unmute Channel
cy.get('[data-testid="muteChannel"]').click(); cy.findByText('Mute channel').should('be.visible').click({force: true});
// # Click "Desktop Notifications" // # Click "Desktop Notifications"
cy.findByText('Desktop Notifications').should('be.visible'); cy.findByText('Desktop Notifications').should('be.visible');
@@ -83,15 +88,16 @@ describe('CRT Desktop notifications', () => {
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.visible').click(); cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.visible').click();
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-none').should('be.checked'); cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-none').should('be.checked');
// # click on Save button // # Save the changes
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click(); cy.findByText('Save').should('be.visible').click();
// # Set users notification settings // # Set users notification settings
cy.uiOpenChannelMenu('Notification Preferences'); cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.checked'); cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-none').should('be.checked');
cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-all').scrollIntoView().should('be.visible').click(); cy.get('.channel-notifications-settings-modal__body').get('#desktopNotification-all').scrollIntoView().should('be.visible').click();
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click(); // # Save the changes
cy.findByText('Save').should('be.visible').click();
// # Post a root message as other user // # Post a root message as other user
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => { cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
@@ -137,6 +143,7 @@ describe('CRT Desktop notifications', () => {
it('MM-T4417_2 Click on sameMobileSettingsDesktop and check if additional settings still appears', () => { it('MM-T4417_2 Click on sameMobileSettingsDesktop and check if additional settings still appears', () => {
cy.visit(testChannelUrl); cy.visit(testChannelUrl);
cy.uiOpenChannelMenu('Notification Preferences'); cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click().then(() => { cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click().then(() => {
cy.get('[data-testid="desktopReplyThreads"]').should('be.visible').click(); cy.get('[data-testid="desktopReplyThreads"]').should('be.visible').click();
}); });
@@ -157,8 +164,8 @@ describe('CRT Desktop notifications', () => {
cy.get('[data-testid="autoFollowThreads"]').should('be.visible').click(); cy.get('[data-testid="autoFollowThreads"]').should('be.visible').click();
// # click on Save button // # Save the changes
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click(); cy.findByText('Save').should('be.visible').click();
}); });
it('MM-T4417_3 Trigger notifications only on mention replies when channel setting is unchecked', () => { it('MM-T4417_3 Trigger notifications only on mention replies when channel setting is unchecked', () => {
@@ -168,7 +175,9 @@ describe('CRT Desktop notifications', () => {
spyNotificationAs('notifySpy', 'granted'); spyNotificationAs('notifySpy', 'granted');
cy.uiOpenChannelMenu('Notification Preferences'); cy.uiOpenChannelMenu('Notification Preferences');
cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click(); cy.get('.channel-notifications-settings-modal__body').scrollTo('center').get('#desktopNotification-mention').should('be.visible').click();
cy.get('.channel-notifications-settings-modal__save-btn').should('be.visible').click();
// # Save the changes
cy.findByText('Save').should('be.visible').click();
// # Post a root message as other user // # Post a root message as other user
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => { cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {

Просмотреть файл

@@ -1,147 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React, {useCallback, useState} from 'react';
import {useIntl} from 'react-intl';
import {
AlertOutlineIcon,
CheckIcon,
CloseIcon,
InformationOutlineIcon,
} from '@mattermost/compass-icons/components';
import OverlayTrigger from 'components/overlay_trigger';
import Tooltip from 'components/tooltip';
import Constants from 'utils/constants';
import './alert_banner.scss';
export type ModeType = 'danger' | 'warning' | 'info' | 'success';
export type AlertBannerProps = {
id?: string;
mode: ModeType;
title?: React.ReactNode;
customIcon?: React.ReactNode;
message?: React.ReactNode;
children?: React.ReactNode;
className?: string;
hideIcon?: boolean;
actionButtonLeft?: React.ReactNode;
actionButtonRight?: React.ReactNode;
footerMessage?: React.ReactNode;
closeBtnTooltip?: React.ReactNode;
onDismiss?: () => void;
variant?: 'sys' | 'app';
}
const AlertBanner = ({
id,
mode,
title,
customIcon,
message,
className,
variant = 'sys',
onDismiss,
actionButtonLeft,
actionButtonRight,
closeBtnTooltip,
footerMessage,
hideIcon,
children,
}: AlertBannerProps) => {
const {formatMessage} = useIntl();
const closeText = formatMessage({id: 'alert_banner.tooltipCloseBtn', defaultMessage: 'Close'});
const [tooltipId] = useState(`alert_banner_close_btn_tooltip_${Math.random()}`);
const bannerIcon = useCallback(() => {
if (customIcon) {
return customIcon;
}
if (mode === 'danger' || mode === 'warning') {
return (
<AlertOutlineIcon
size={24}
/>);
} else if (mode === 'success') {
return (
<CheckIcon
size={24}
/>);
}
return (
<InformationOutlineIcon
size={24}
/>);
}, [mode, customIcon]);
return (
<div
data-testid={id}
className={classNames(
'AlertBanner',
mode,
className,
`AlertBanner--${variant}`,
)}
>
{!hideIcon && (
<div className='AlertBanner__icon'>
{bannerIcon()}
</div>
)}
<div className='AlertBanner__body'>
{title && <div className='AlertBanner__title'>{title}</div>}
{message && (
<div
className={classNames({
AlertBanner__message: Boolean(title),
})}
>
{message}
</div>
)}
{children}
{(actionButtonLeft || actionButtonRight) && (
<div className='AlertBanner__actionButtons'>
{actionButtonLeft}
{actionButtonRight}
</div>
)}
{
footerMessage && (
<div className='AlertBanner__footerMessage'>
{footerMessage}
</div>
)
}
</div>
{onDismiss && (
<OverlayTrigger
trigger={['hover', 'focus']}
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='left'
overlay={closeBtnTooltip || (
<Tooltip id={tooltipId}>{closeText}</Tooltip>
)}
>
<button
aria-label={closeText}
className='AlertBanner__closeButton'
onClick={onDismiss}
>
<CloseIcon
size={18}
/>
</button>
</OverlayTrigger>
)}
</div>
);
};
export default AlertBanner;

Просмотреть файл

@@ -3,7 +3,6 @@
.AlertBanner { .AlertBanner {
display: flex; display: flex;
overflow: hidden;
align-items: flex-start; align-items: flex-start;
padding: 14px; padding: 14px;
border: 1px solid; border: 1px solid;

Просмотреть файл

@@ -4,7 +4,7 @@
import {shallow} from 'enzyme'; import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import AlertBanner from './alert_banner'; import AlertBanner from 'components/alert_banner';
describe('Components/AlertBanner', () => { describe('Components/AlertBanner', () => {
test('should match snapshot', () => { test('should match snapshot', () => {

Просмотреть файл

@@ -0,0 +1,139 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import type {ReactNode} from 'react';
import React, {useMemo} from 'react';
import {useIntl} from 'react-intl';
import {
AlertOutlineIcon,
CheckIcon,
CloseIcon,
InformationOutlineIcon,
} from '@mattermost/compass-icons/components';
import WithTooltip from 'components/with_tooltip';
import './alert_banner.scss';
export type ModeType = 'danger' | 'warning' | 'info' | 'success';
export type AlertBannerProps = {
id?: string;
mode: ModeType;
title?: ReactNode;
customIcon?: ReactNode;
message?: ReactNode;
children?: ReactNode;
className?: string;
hideIcon?: boolean;
actionButtonLeft?: ReactNode;
actionButtonRight?: ReactNode;
footerMessage?: ReactNode;
closeBtnTooltip?: string;
onDismiss?: () => void;
variant?: 'sys' | 'app';
}
const AlertBanner = ({
id,
mode,
title,
customIcon,
message,
className,
variant = 'sys',
onDismiss,
actionButtonLeft,
actionButtonRight,
closeBtnTooltip,
footerMessage,
hideIcon,
children,
}: AlertBannerProps) => {
const {formatMessage} = useIntl();
const bannerIcon = useMemo(() => {
if (customIcon) {
return customIcon;
}
if (mode === 'danger' || mode === 'warning') {
return <AlertOutlineIcon size={24}/>;
} else if (mode === 'success') {
return <CheckIcon size={24}/>;
}
return <InformationOutlineIcon size={24}/>;
}, [mode, customIcon]);
const dismissButton = useMemo(() => {
return (
<button
className='AlertBanner__closeButton'
aria-label={formatMessage({id: 'alert_banner.tooltipCloseBtn', defaultMessage: 'Close'})}
onClick={onDismiss}
>
<CloseIcon size={18}/>
</button>
);
}, [onDismiss]);
return (
<div
data-testid={id}
className={classNames(
'AlertBanner',
mode,
className,
`AlertBanner--${variant}`,
)}
>
{!hideIcon && (
<div className='AlertBanner__icon'>
{bannerIcon}
</div>
)}
<div className='AlertBanner__body'>
{title &&
<div className='AlertBanner__title'>
{title}
</div>
}
{message && (
<div
className={classNames({AlertBanner__message: Boolean(title)})}
>
{message}
</div>
)}
{children}
{(actionButtonLeft || actionButtonRight) && (
<div className='AlertBanner__actionButtons'>
{actionButtonLeft}
{actionButtonRight}
</div>
)}
{footerMessage && (
<div className='AlertBanner__footerMessage'>
{footerMessage}
</div>
)}
</div>
{onDismiss && closeBtnTooltip && (
<WithTooltip
id={`alertBannerTooltip_${id}`}
title={closeBtnTooltip}
placement='left'
>
{dismissButton}
</WithTooltip>
)}
{onDismiss && !closeBtnTooltip && (
dismissButton
)}
</div>
);
};
export default AlertBanner;

Просмотреть файл

@@ -52,20 +52,11 @@ Object {
class="mm-modal-header__ctr" class="mm-modal-header__ctr"
> >
<button <button
class="style--none mm-modal-header__close-btn" class="btn btn-icon"
> >
<svg <i
fill="currentcolor" class="icon icon-close"
height="24" />
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
</button> </button>
</div> </div>
</header> </header>
@@ -76,16 +67,16 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Mute or ignore Mute or ignore
</h4> </h4>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -108,14 +99,13 @@ Object {
/> />
Mute channel Mute channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
class="mm-modal-generic-section-item__description" class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description" data-testid="mm-modal-generic-section-item__description"
> >
Turns off notifications for this channel. Youll still see badges if youre mentioned. Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p> </p>
</div> </div>
<div <div
@@ -139,7 +129,6 @@ Object {
/> />
Ignore mentions for @channel, @here and @all Ignore mentions for @channel, @here and @all
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
@@ -158,25 +147,21 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Desktop Notifications
class="mm-modal-generic-section__title" </h4>
>
Desktop Notifications
</h4>
</div>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App. Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -244,16 +229,12 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Mobile Notifications
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
<button <button
class="channel-notifications-settings-modal__reset-btn" class="channel-notifications-settings-modal__reset-btn"
> >
@@ -271,15 +252,15 @@ Object {
</svg> </svg>
Reset to default Reset to default
</button> </button>
</div> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Notification alerts are pushed to your mobile device when there is activity in Mattermost. Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -302,7 +283,6 @@ Object {
/> />
Use the same notification settings as desktop Use the same notification settings as desktop
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
</div> </div>
@@ -370,12 +350,12 @@ Object {
> >
<button <button
class="channel-notifications-settings-modal__cancel-btn" class="btn btn-tertiary btn-md"
> >
Cancel Cancel
</button> </button>
<button <button
class="channel-notifications-settings-modal__save-btn" class="btn btn-primary btn-md"
> >
Save Save
</button> </button>
@@ -496,20 +476,11 @@ Object {
class="mm-modal-header__ctr" class="mm-modal-header__ctr"
> >
<button <button
class="style--none mm-modal-header__close-btn" class="btn btn-icon"
> >
<svg <i
fill="currentcolor" class="icon icon-close"
height="24" />
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
</button> </button>
</div> </div>
</header> </header>
@@ -520,16 +491,16 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Mute or ignore Mute or ignore
</h4> </h4>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -552,14 +523,13 @@ Object {
/> />
Mute channel Mute channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
class="mm-modal-generic-section-item__description" class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description" data-testid="mm-modal-generic-section-item__description"
> >
Turns off notifications for this channel. Youll still see badges if youre mentioned. Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p> </p>
</div> </div>
<div <div
@@ -583,7 +553,6 @@ Object {
/> />
Ignore mentions for @channel, @here and @all Ignore mentions for @channel, @here and @all
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
@@ -602,16 +571,12 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Desktop Notifications
class="mm-modal-generic-section__title"
>
Desktop Notifications
</h4>
<button <button
class="channel-notifications-settings-modal__reset-btn" class="channel-notifications-settings-modal__reset-btn"
> >
@@ -629,15 +594,15 @@ Object {
</svg> </svg>
Reset to default Reset to default
</button> </button>
</div> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App. Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -705,16 +670,12 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Mobile Notifications
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
<button <button
class="channel-notifications-settings-modal__reset-btn" class="channel-notifications-settings-modal__reset-btn"
> >
@@ -732,15 +693,15 @@ Object {
</svg> </svg>
Reset to default Reset to default
</button> </button>
</div> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Notification alerts are pushed to your mobile device when there is activity in Mattermost. Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -763,7 +724,6 @@ Object {
/> />
Use the same notification settings as desktop Use the same notification settings as desktop
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
</div> </div>
@@ -831,12 +791,12 @@ Object {
> >
<button <button
class="channel-notifications-settings-modal__cancel-btn" class="btn btn-tertiary btn-md"
> >
Cancel Cancel
</button> </button>
<button <button
class="channel-notifications-settings-modal__save-btn" class="btn btn-primary btn-md"
> >
Save Save
</button> </button>
@@ -957,20 +917,11 @@ Object {
class="mm-modal-header__ctr" class="mm-modal-header__ctr"
> >
<button <button
class="style--none mm-modal-header__close-btn" class="btn btn-icon"
> >
<svg <i
fill="currentcolor" class="icon icon-close"
height="24" />
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
</button> </button>
</div> </div>
</header> </header>
@@ -981,16 +932,16 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Mute or ignore Mute or ignore
</h4> </h4>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1013,14 +964,13 @@ Object {
/> />
Mute channel Mute channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
class="mm-modal-generic-section-item__description" class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description" data-testid="mm-modal-generic-section-item__description"
> >
Turns off notifications for this channel. Youll still see badges if youre mentioned. Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p> </p>
</div> </div>
<div <div
@@ -1044,7 +994,6 @@ Object {
/> />
Ignore mentions for @channel, @here and @all Ignore mentions for @channel, @here and @all
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
@@ -1063,25 +1012,21 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Desktop Notifications
class="mm-modal-generic-section__title" </h4>
>
Desktop Notifications
</h4>
</div>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App. Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1149,16 +1094,12 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Mobile Notifications
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
<button <button
class="channel-notifications-settings-modal__reset-btn" class="channel-notifications-settings-modal__reset-btn"
> >
@@ -1176,15 +1117,15 @@ Object {
</svg> </svg>
Reset to default Reset to default
</button> </button>
</div> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Notification alerts are pushed to your mobile device when there is activity in Mattermost. Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1207,7 +1148,6 @@ Object {
/> />
Use the same notification settings as desktop Use the same notification settings as desktop
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
</div> </div>
@@ -1275,12 +1215,12 @@ Object {
> >
<button <button
class="channel-notifications-settings-modal__cancel-btn" class="btn btn-tertiary btn-md"
> >
Cancel Cancel
</button> </button>
<button <button
class="channel-notifications-settings-modal__save-btn" class="btn btn-primary btn-md"
> >
Save Save
</button> </button>
@@ -1401,20 +1341,11 @@ Object {
class="mm-modal-header__ctr" class="mm-modal-header__ctr"
> >
<button <button
class="style--none mm-modal-header__close-btn" class="btn btn-icon"
> >
<svg <i
fill="currentcolor" class="icon icon-close"
height="24" />
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
</button> </button>
</div> </div>
</header> </header>
@@ -1425,16 +1356,16 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Mute or ignore Mute or ignore
</h4> </h4>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1457,14 +1388,13 @@ Object {
/> />
Mute channel Mute channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
class="mm-modal-generic-section-item__description" class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description" data-testid="mm-modal-generic-section-item__description"
> >
Turns off notifications for this channel. Youll still see badges if youre mentioned. Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p> </p>
</div> </div>
<div <div
@@ -1488,7 +1418,6 @@ Object {
/> />
Ignore mentions for @channel, @here and @all Ignore mentions for @channel, @here and @all
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
@@ -1502,6 +1431,7 @@ Object {
</section> </section>
<div <div
class="AlertBanner info AlertBanner--app" class="AlertBanner info AlertBanner--app"
data-testid="channelNotificationsMutedBanner"
> >
<div <div
class="AlertBanner__icon" class="AlertBanner__icon"
@@ -1540,12 +1470,12 @@ Object {
> >
<button <button
class="channel-notifications-settings-modal__cancel-btn" class="btn btn-tertiary btn-md"
> >
Cancel Cancel
</button> </button>
<button <button
class="channel-notifications-settings-modal__save-btn" class="btn btn-primary btn-md"
> >
Save Save
</button> </button>
@@ -1666,20 +1596,11 @@ Object {
class="mm-modal-header__ctr" class="mm-modal-header__ctr"
> >
<button <button
class="style--none mm-modal-header__close-btn" class="btn btn-icon"
> >
<svg <i
fill="currentcolor" class="icon icon-close"
height="24" />
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
</button> </button>
</div> </div>
</header> </header>
@@ -1690,16 +1611,16 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Mute or ignore Mute or ignore
</h4> </h4>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1722,14 +1643,13 @@ Object {
/> />
Mute channel Mute channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
class="mm-modal-generic-section-item__description" class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description" data-testid="mm-modal-generic-section-item__description"
> >
Turns off notifications for this channel. Youll still see badges if youre mentioned. Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p> </p>
</div> </div>
<div <div
@@ -1753,7 +1673,6 @@ Object {
/> />
Ignore mentions for @channel, @here and @all Ignore mentions for @channel, @here and @all
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
@@ -1772,25 +1691,21 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Desktop Notifications
class="mm-modal-generic-section__title" </h4>
>
Desktop Notifications
</h4>
</div>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App. Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1858,16 +1773,12 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Mobile Notifications
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
<button <button
class="channel-notifications-settings-modal__reset-btn" class="channel-notifications-settings-modal__reset-btn"
> >
@@ -1885,15 +1796,15 @@ Object {
</svg> </svg>
Reset to default Reset to default
</button> </button>
</div> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Notification alerts are pushed to your mobile device when there is activity in Mattermost. Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -1916,7 +1827,6 @@ Object {
/> />
Use the same notification settings as desktop Use the same notification settings as desktop
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
</div> </div>
@@ -1928,12 +1838,12 @@ Object {
> >
<button <button
class="channel-notifications-settings-modal__cancel-btn" class="btn btn-tertiary btn-md"
> >
Cancel Cancel
</button> </button>
<button <button
class="channel-notifications-settings-modal__save-btn" class="btn btn-primary btn-md"
> >
Save Save
</button> </button>
@@ -2054,20 +1964,11 @@ Object {
class="mm-modal-header__ctr" class="mm-modal-header__ctr"
> >
<button <button
class="style--none mm-modal-header__close-btn" class="btn btn-icon"
> >
<svg <i
fill="currentcolor" class="icon icon-close"
height="24" />
version="1.1"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
/>
</svg>
</button> </button>
</div> </div>
</header> </header>
@@ -2078,16 +1979,16 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Mute or ignore Mute or ignore
</h4> </h4>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -2110,14 +2011,13 @@ Object {
/> />
Mute channel Mute channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
class="mm-modal-generic-section-item__description" class="mm-modal-generic-section-item__description"
data-testid="mm-modal-generic-section-item__description" data-testid="mm-modal-generic-section-item__description"
> >
Turns off notifications for this channel. Youll still see badges if youre mentioned. Turns off notifications for this channel. You'll still see badges if you're mentioned.
</p> </p>
</div> </div>
<div <div
@@ -2141,7 +2041,6 @@ Object {
/> />
Ignore mentions for @channel, @here and @all Ignore mentions for @channel, @here and @all
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
<p <p
@@ -2160,25 +2059,21 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Desktop Notifications
class="mm-modal-generic-section__title" </h4>
>
Desktop Notifications
</h4>
</div>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Available on Chrome, Edge, Firefox, and the Mattermost Desktop App. Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -2246,16 +2141,12 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<div <h4
class="mm-modal-generic-section__row" class="modalSectionTitle"
> >
<h4 Mobile Notifications
class="mm-modal-generic-section__title"
>
Mobile Notifications
</h4>
<button <button
class="channel-notifications-settings-modal__reset-btn" class="channel-notifications-settings-modal__reset-btn"
> >
@@ -2273,15 +2164,15 @@ Object {
</svg> </svg>
Reset to default Reset to default
</button> </button>
</div> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
Notification alerts are pushed to your mobile device when there is activity in Mattermost. Notification alerts are pushed to your mobile device when there is activity in Mattermost.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -2304,7 +2195,6 @@ Object {
/> />
Use the same notification settings as desktop Use the same notification settings as desktop
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
</div> </div>
@@ -2373,21 +2263,21 @@ Object {
class="mm-modal-generic-section" class="mm-modal-generic-section"
> >
<div <div
class="mm-modal-generic-section__title-description-ctr" class="modalSectionHeader"
> >
<h4 <h4
class="mm-modal-generic-section__title" class="modalSectionTitle"
> >
Follow all threads in this channel Follow all threads in this channel
</h4> </h4>
<p <p
class="mm-modal-generic-section__description" class="modalSectionDescription"
> >
When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view. When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.
</p> </p>
</div> </div>
<div <div
class="mm-modal-generic-section__content" class="modalSectionContent"
> >
<div <div
class="mm-modal-generic-section-item" class="mm-modal-generic-section-item"
@@ -2410,7 +2300,6 @@ Object {
/> />
Automatically follow threads in this channel Automatically follow threads in this channel
</label> </label>
<br />
</fieldset> </fieldset>
</div> </div>
</div> </div>
@@ -2422,12 +2311,12 @@ Object {
> >
<button <button
class="channel-notifications-settings-modal__cancel-btn" class="btn btn-tertiary btn-md"
> >
Cancel Cancel
</button> </button>
<button <button
class="channel-notifications-settings-modal__save-btn" class="btn btn-primary btn-md"
> >
Save Save
</button> </button>

Просмотреть файл

@@ -31,7 +31,7 @@
max-width: 1024px; max-width: 1024px;
min-height: 150px; min-height: 150px;
flex-direction: column; flex-direction: column;
padding: 32px; padding: 28px 32px;
gap: 24px; gap: 24px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
@@ -72,48 +72,6 @@
color: var(--error-text); color: var(--error-text);
} }
&__save-btn {
padding: 12px 20px 12px 20px;
border: none;
border-radius: 4px;
background: var(--button-bg);
color: var(--button-color);
font-size: 14px;
font-weight: 600;
line-height: 14px;
text-transform: capitalize;
&:hover,
&:active {
background: rgba(var(--button-bg-rgb), 0.12);
background: linear-gradient(0deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08)), var(--button-bg);
}
&:focus {
box-sizing: border-box;
padding: 10px 18px;
border: 2px solid var(--sidebar-text-active-border);
box-shadow: none;
}
}
&__cancel-btn {
padding: 12px 20px 12px 20px;
border: none;
border-radius: 4px;
background: rgba(var(--button-bg-rgb), 0.08);
color: var(--button-bg);
font-size: 14px;
font-weight: 600;
line-height: 14px;
text-transform: capitalize;
&:hover,
&:active {
background: rgba(var(--button-bg-rgb), 0.12);
}
}
&__divider { &__divider {
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08); border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.08);
} }

Просмотреть файл

@@ -43,10 +43,8 @@ type Props = PropsFromRedux & {
}; };
function getUseSameDesktopSetting(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) { function getUseSameDesktopSetting(currentUserNotifyProps: UserNotifyProps, channelMemberNotifyProps?: ChannelMemberNotifyProps) {
const isSameAsDesktop = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop === channelMemberNotifyProps?.push : const isSameAsDesktop = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop === channelMemberNotifyProps?.push : currentUserNotifyProps.push === currentUserNotifyProps.desktop;
currentUserNotifyProps.push === currentUserNotifyProps.desktop; const isSameAsDesktopThreads = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop_threads === channelMemberNotifyProps?.push_threads : currentUserNotifyProps.push_threads === currentUserNotifyProps.desktop_threads;
const isSameAsDesktopThreads = channelMemberNotifyProps ? channelMemberNotifyProps?.desktop_threads === channelMemberNotifyProps?.push_threads :
currentUserNotifyProps.push_threads === currentUserNotifyProps.desktop_threads;
return isSameAsDesktop && isSameAsDesktopThreads; return isSameAsDesktop && isSameAsDesktopThreads;
} }
@@ -106,16 +104,34 @@ export default function ChannelNotificationsModal(props: Props) {
setSettings((prevSettings) => ({...prevSettings, push: prevSettings.desktop, push_threads: prevSettings.desktop_threads})); setSettings((prevSettings) => ({...prevSettings, push: prevSettings.desktop, push_threads: prevSettings.desktop_threads}));
}, []); }, []);
const MuteIgnoreSectionContent = ( const MuteOrIgnoreSectionContent = (
<> <>
<CheckboxSettingItem <CheckboxSettingItem
description={utils.MuteChannelDesc} inputFieldTitle={
<FormattedMessage
id='channel_notifications.muteChannelTitle'
defaultMessage='Mute channel'
/>
}
description={formatMessage({
id: 'channel_notifications.muteChannelDesc',
defaultMessage: 'Turns off notifications for this channel. You\'ll still see badges if you\'re mentioned.',
})}
inputFieldValue={settings.mark_unread === 'mention'} inputFieldValue={settings.mark_unread === 'mention'}
inputFieldData={utils.MuteChannelInputFieldData} inputFieldData={utils.MuteChannelInputFieldData}
handleChange={(e) => handleChange({mark_unread: e ? 'mention' : 'all'})} handleChange={(e) => handleChange({mark_unread: e ? 'mention' : 'all'})}
/> />
<CheckboxSettingItem <CheckboxSettingItem
description={utils.IgnoreMentionsDesc} inputFieldTitle={
<FormattedMessage
id='channel_notifications.ignoreMentionsTitle'
defaultMessage='Ignore mentions for @channel, @here and @all'
/>
}
description={formatMessage({
id: 'channel_notifications.ignoreMentionsDesc',
defaultMessage: 'When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel',
})}
inputFieldValue={settings.ignore_channel_mentions === 'on'} inputFieldValue={settings.ignore_channel_mentions === 'on'}
inputFieldData={utils.IgnoreMentionsInputFieldData} inputFieldData={utils.IgnoreMentionsInputFieldData}
handleChange={(e) => handleChange({ignore_channel_mentions: e ? 'on' : 'off'})} handleChange={(e) => handleChange({ignore_channel_mentions: e ? 'on' : 'off'})}
@@ -126,16 +142,28 @@ export default function ChannelNotificationsModal(props: Props) {
const DesktopNotificationsSectionContent = ( const DesktopNotificationsSectionContent = (
<> <>
<RadioSettingItem <RadioSettingItem
title={utils.NotifyMeTitle} title={formatMessage({
id: 'channel_notifications.NotifyMeTitle',
defaultMessage: 'Notify me about…',
})}
inputFieldValue={settings.desktop} inputFieldValue={settings.desktop}
inputFieldData={utils.desktopNotificationInputFieldData(props.currentUser.notify_props.desktop)} inputFieldData={utils.desktopNotificationInputFieldData(props.currentUser.notify_props.desktop)}
handleChange={(e) => handleChange({desktop: e.target.value})} handleChange={(e) => handleChange({desktop: e.target.value})}
/> />
{props.collapsedReplyThreads && settings.desktop === 'mention' && {props.collapsedReplyThreads && settings.desktop === 'mention' &&
<CheckboxSettingItem <CheckboxSettingItem
title={utils.ThreadsReplyTitle} title={formatMessage({
id: 'channel_notifications.ThreadsReplyTitle',
defaultMessage: 'Thread reply notifications',
})}
inputFieldValue={settings.desktop_threads === 'all'} inputFieldValue={settings.desktop_threads === 'all'}
inputFieldData={utils.DesktopReplyThreadsInputFieldData} inputFieldData={utils.DesktopReplyThreadsInputFieldData}
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.threadsReplyTitle'
defaultMessage="Notify me about replies to threads I\'m following"
/>
}
handleChange={(e) => handleChange({desktop_threads: e ? 'all' : 'mention'})} handleChange={(e) => handleChange({desktop_threads: e ? 'all' : 'mention'})}
/>} />}
</> </>
@@ -144,6 +172,12 @@ export default function ChannelNotificationsModal(props: Props) {
const MobileNotificationsSectionContent = ( const MobileNotificationsSectionContent = (
<> <>
<CheckboxSettingItem <CheckboxSettingItem
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.sameMobileSettingsDesktop'
defaultMessage='Use the same notification settings as desktop'
/>
}
inputFieldValue={mobileSettingsSameAsDesktop} inputFieldValue={mobileSettingsSameAsDesktop}
inputFieldData={utils.sameMobileSettingsDesktopInputFieldData} inputFieldData={utils.sameMobileSettingsDesktopInputFieldData}
handleChange={() => handleMobileSettingsChange()} handleChange={() => handleMobileSettingsChange()}
@@ -151,14 +185,26 @@ export default function ChannelNotificationsModal(props: Props) {
{!mobileSettingsSameAsDesktop && ( {!mobileSettingsSameAsDesktop && (
<> <>
<RadioSettingItem <RadioSettingItem
title={utils.NotifyMeTitle} title={formatMessage({
id: 'channel_notifications.NotifyMeTitle',
defaultMessage: 'Notify me about…',
})}
inputFieldValue={settings.push} inputFieldValue={settings.push}
inputFieldData={utils.mobileNotificationInputFieldData(props.currentUser.notify_props.push)} inputFieldData={utils.mobileNotificationInputFieldData(props.currentUser.notify_props.push)}
handleChange={(e) => handleChange({push: e.target.value})} handleChange={(e) => handleChange({push: e.target.value})}
/> />
{props.collapsedReplyThreads && settings.push === 'mention' && {props.collapsedReplyThreads && settings.push === 'mention' &&
<CheckboxSettingItem <CheckboxSettingItem
title={utils.ThreadsReplyTitle} title={formatMessage({
id: 'channel_notifications.ThreadsReplyTitle',
defaultMessage: 'Thread reply notifications',
})}
inputFieldTitle={
<FormattedMessage
id='channel_notifications.checkbox.threadsReplyTitle'
defaultMessage="Notify me about replies to threads I\'m following"
/>
}
inputFieldValue={settings.push_threads === 'all'} inputFieldValue={settings.push_threads === 'all'}
inputFieldData={utils.MobileReplyThreadsInputFieldData} inputFieldData={utils.MobileReplyThreadsInputFieldData}
handleChange={(e) => handleChange({push_threads: e ? 'all' : 'mention'})} handleChange={(e) => handleChange({push_threads: e ? 'all' : 'mention'})}
@@ -169,13 +215,17 @@ export default function ChannelNotificationsModal(props: Props) {
); );
const AutoFollowThreadsSectionContent = ( const AutoFollowThreadsSectionContent = (
<> <CheckboxSettingItem
<CheckboxSettingItem inputFieldTitle={
inputFieldValue={settings.channel_auto_follow_threads === 'on'} <FormattedMessage
inputFieldData={utils.AutoFollowThreadsInputFieldData} id='channel_notifications.checkbox.autoFollowThreadsTitle'
handleChange={(e) => handleChange({channel_auto_follow_threads: e ? 'on' : 'off'})} defaultMessage='Automatically follow threads in this channel'
/> />
</> }
inputFieldValue={settings.channel_auto_follow_threads === 'on'}
inputFieldData={utils.AutoFollowThreadsInputFieldData}
handleChange={(e) => handleChange({channel_auto_follow_threads: e ? 'on' : 'off'})}
/>
); );
function handleSave() { function handleSave() {
@@ -229,25 +279,38 @@ export default function ChannelNotificationsModal(props: Props) {
); );
}, [props.currentUser, settings]); }, [props.currentUser, settings]);
const settingsAndAlertBanner = settings.mark_unread === 'all' ? ( const desktopAndMobileNotificationSectionContent = settings.mark_unread === 'all' ? (
<> <>
<div className='channel-notifications-settings-modal__divider'/> <div className='channel-notifications-settings-modal__divider'/>
<ModalSection <ModalSection
title={utils.DesktopNotificationsSectionTitle} title={formatMessage({
description={utils.DesktopNotificationsSectionDesc} id: 'channel_notifications.desktopNotificationsTitle',
content={DesktopNotificationsSectionContent} defaultMessage: 'Desktop Notifications',
})}
titleSuffix={resetToDefaultBtn('desktop')} titleSuffix={resetToDefaultBtn('desktop')}
description={formatMessage({
id: 'channel_notifications.desktopNotificationsDesc',
defaultMessage: 'Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.',
})}
content={DesktopNotificationsSectionContent}
/> />
<div className='channel-notifications-settings-modal__divider'/> <div className='channel-notifications-settings-modal__divider'/>
<ModalSection <ModalSection
title={utils.MobileNotificationsSectionTitle} title={formatMessage({
description={utils.MobileNotificationsSectionDesc} id: 'channel_notifications.mobileNotificationsTitle',
content={MobileNotificationsSectionContent} defaultMessage: 'Mobile Notifications',
})}
titleSuffix={resetToDefaultBtn('push')} titleSuffix={resetToDefaultBtn('push')}
description={formatMessage({
id: 'channel_notifications.mobileNotificationsDesc',
defaultMessage: 'Notification alerts are pushed to your mobile device when there is activity in Mattermost.',
})}
content={MobileNotificationsSectionContent}
/> />
</> </>
) : ( ) : (
<AlertBanner <AlertBanner
id='channelNotificationsMutedBanner'
mode='info' mode='info'
variant='app' variant='app'
customIcon={ customIcon={
@@ -292,16 +355,25 @@ export default function ChannelNotificationsModal(props: Props) {
/> />
<main className='channel-notifications-settings-modal__body'> <main className='channel-notifications-settings-modal__body'>
<ModalSection <ModalSection
title={utils.MuteAndIgnoreSectionTitle} title={formatMessage({
content={MuteIgnoreSectionContent} id: 'channel_notifications.muteAndIgnore',
defaultMessage: 'Mute or ignore',
})}
content={MuteOrIgnoreSectionContent}
/> />
{settingsAndAlertBanner} {desktopAndMobileNotificationSectionContent}
{props.collapsedReplyThreads && {props.collapsedReplyThreads &&
<> <>
<div className='channel-notifications-settings-modal__divider'/> <div className='channel-notifications-settings-modal__divider'/>
<ModalSection <ModalSection
title={utils.AutoFollowThreadsTitle} title={formatMessage({
description={utils.AutoFollowThreadsDesc} id: 'channel_notifications.autoFollowThreadsTitle',
defaultMessage: 'Follow all threads in this channel',
})}
description={formatMessage({
id: 'channel_notifications.autoFollowThreadsDesc',
defaultMessage: 'When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.',
})}
content={AutoFollowThreadsSectionContent} content={AutoFollowThreadsSectionContent}
/> />
</> </>
@@ -314,8 +386,8 @@ export default function ChannelNotificationsModal(props: Props) {
</span> </span>
} }
<button <button
className='btn btn-tertiary btn-md'
onClick={handleHide} onClick={handleHide}
className='channel-notifications-settings-modal__cancel-btn'
> >
<FormattedMessage <FormattedMessage
id='generic_btn.cancel' id='generic_btn.cancel'
@@ -323,7 +395,7 @@ export default function ChannelNotificationsModal(props: Props) {
/> />
</button> </button>
<button <button
className={'channel-notifications-settings-modal__save-btn'} className='btn btn-primary btn-md'
onClick={handleSave} onClick={handleSave}
> >
<FormattedMessage <FormattedMessage

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React from 'react';
import {FormattedMessage, defineMessages} from 'react-intl'; import {FormattedMessage} from 'react-intl';
import type {ChannelNotifyProps} from '@mattermost/types/channels'; import type {ChannelNotifyProps} from '@mattermost/types/channels';
import type {UserNotifyProps} from '@mattermost/types/users'; import type {UserNotifyProps} from '@mattermost/types/users';
@@ -14,163 +14,47 @@ import {NotificationLevels} from 'utils/constants';
export type ChannelMemberNotifyProps = Partial<ChannelNotifyProps> & Pick<UserNotifyProps, 'desktop_threads' | 'push_threads'> export type ChannelMemberNotifyProps = Partial<ChannelNotifyProps> & Pick<UserNotifyProps, 'desktop_threads' | 'push_threads'>
const translations = defineMessages({
MuteAndIgnoreSectionTitle: {
id: 'channel_notifications.muteAndIgnore',
defaultMessage: 'Mute or ignore',
},
NotifyMeTitle: {
id: 'channel_notifications.NotifyMeTitle',
defaultMessage: 'Notify me about…',
},
ThreadsReplyTitle: {
id: 'channel_notifications.ThreadsReplyTitle',
defaultMessage: 'Thread reply notifications',
},
DesktopNotificationsSectionTitle: {
id: 'channel_notifications.desktopNotificationsTitle',
defaultMessage: 'Desktop Notifications',
},
DesktopNotificationsSectionDesc: {
id: 'channel_notifications.desktopNotificationsDesc',
defaultMessage: 'Available on Chrome, Edge, Firefox, and the Mattermost Desktop App.',
},
MobileNotificationsSectionTitle: {
id: 'channel_notifications.mobileNotificationsTitle',
defaultMessage: 'Mobile Notifications',
},
MobileNotificationsSectionDesc: {
id: 'channel_notifications.mobileNotificationsDesc',
defaultMessage: 'Notification alerts are pushed to your mobile device when there is activity in Mattermost.',
},
MuteChannelDesc: {
id: 'channel_notifications.muteChannelDesc',
defaultMessage: 'Turns off notifications for this channel. Youll still see badges if youre mentioned.',
},
IgnoreMentionsDesc: {
id: 'channel_notifications.ignoreMentionsDesc',
defaultMessage: 'When enabled, @channel, @here and @all will not trigger mentions or mention notifications in this channel',
},
MuteChannelInputFieldTitle: {
id: 'channel_notifications.muteChannelTitle',
defaultMessage: 'Mute channel',
},
DesktopReplyThreadsInputFieldTitle: {
id: 'channel_notifications.checkbox.threadsReplyTitle',
defaultMessage: 'Notify me about replies to threads I\'m following',
},
MobileReplyThreadsInputFieldTitle: {
id: 'channel_notifications.checkbox.threadsReplyTitle',
defaultMessage: 'Notify me about replies to threads I\'m following',
},
sameMobileSettingsDesktopInputFieldTitle: {
id: 'channel_notifications.checkbox.sameMobileSettingsDesktop',
defaultMessage: 'Use the same notification settings as desktop',
},
IgnoreMentionsInputFieldTitle: {
id: 'channel_notifications.ignoreMentionsTitle',
defaultMessage: 'Ignore mentions for @channel, @here and @all',
},
AutoFollowThreadsTitle: {
id: 'channel_notifications.autoFollowThreadsTitle',
defaultMessage: 'Follow all threads in this channel',
},
AutoFollowThreadsDesc: {
id: 'channel_notifications.autoFollowThreadsDesc',
defaultMessage: 'When enabled, all new replies in this channel will be automatically followed and will appear in your Threads view.',
},
AutoFollowThreadsInputFieldTitle: {
id: 'channel_notifications.checkbox.autoFollowThreadsTitle',
defaultMessage: 'Automatically follow threads in this channel',
},
});
const desktopNotificationInputFieldOptions = defineMessages({
allNewMessages: {
id: 'channel_notifications.desktopNotificationAllLabel',
defaultMessage: 'All new messages',
},
mentions: {
id: 'channel_notifications.desktopNotificationMentionLabel',
defaultMessage: 'Mentions, direct messages, and keywords only',
},
nothing: {
id: 'channel_notifications.desktopNotificationNothingLabel',
defaultMessage: 'Nothing',
},
});
const mobileNotificationInputFieldOptions = defineMessages({
allNewMessages: {
id: 'channel_notifications.MobileNotificationAllLabel',
defaultMessage: 'All new messages',
},
mentions: {
id: 'channel_notifications.MobileNotificationMentionLabel',
defaultMessage: 'Mentions, direct messages, and keywords only',
},
nothing: {
id: 'channel_notifications.MobileNotificationNothingLabel',
defaultMessage: 'Nothing',
},
});
const MuteChannelInputFieldData: FieldsetCheckbox = { const MuteChannelInputFieldData: FieldsetCheckbox = {
title: translations.MuteChannelInputFieldTitle,
name: 'mute channel', name: 'mute channel',
dataTestId: 'muteChannel', dataTestId: 'muteChannel',
}; };
const DesktopReplyThreadsInputFieldData: FieldsetCheckbox = { const DesktopReplyThreadsInputFieldData: FieldsetCheckbox = {
title: translations.DesktopReplyThreadsInputFieldTitle,
name: 'desktop reply threads', name: 'desktop reply threads',
dataTestId: 'desktopReplyThreads', dataTestId: 'desktopReplyThreads',
}; };
const MobileReplyThreadsInputFieldData: FieldsetCheckbox = { const MobileReplyThreadsInputFieldData: FieldsetCheckbox = {
title: translations.MobileReplyThreadsInputFieldTitle,
name: 'mobile reply threads', name: 'mobile reply threads',
dataTestId: 'mobileReplyThreads', dataTestId: 'mobileReplyThreads',
}; };
const sameMobileSettingsDesktopInputFieldData: FieldsetCheckbox = { export const sameMobileSettingsDesktopInputFieldData: FieldsetCheckbox = {
title: translations.sameMobileSettingsDesktopInputFieldTitle,
name: 'same mobile settings as Desktop', name: 'same mobile settings as Desktop',
dataTestId: 'sameMobileSettingsDesktop', dataTestId: 'sameMobileSettingsDesktop',
}; };
const IgnoreMentionsInputFieldData: FieldsetCheckbox = { export const IgnoreMentionsInputFieldData: FieldsetCheckbox = {
title: translations.IgnoreMentionsInputFieldTitle,
name: 'ignore mentions', name: 'ignore mentions',
dataTestId: 'ignoreMentions', dataTestId: 'ignoreMentions',
}; };
const AutoFollowThreadsInputFieldData: FieldsetCheckbox = { export const AutoFollowThreadsInputFieldData: FieldsetCheckbox = {
title: translations.AutoFollowThreadsInputFieldTitle,
name: 'auto follow threads', name: 'auto follow threads',
dataTestId: 'autoFollowThreads', dataTestId: 'autoFollowThreads',
}; };
const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio => { export const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
return { return {
options: [ options: [
{ {
dataTestId: `desktopNotification-${NotificationLevels.ALL}`, dataTestId: `desktopNotification-${NotificationLevels.ALL}`,
title: desktopNotificationInputFieldOptions.allNewMessages, title: (
<FormattedMessage
id='channel_notifications.desktopNotificationAllLabel'
defaultMessage='All new messages'
/>
),
name: `desktopNotification-${NotificationLevels.ALL}`, name: `desktopNotification-${NotificationLevels.ALL}`,
key: `desktopNotification-${NotificationLevels.ALL}`, key: `desktopNotification-${NotificationLevels.ALL}`,
value: NotificationLevels.ALL, value: NotificationLevels.ALL,
@@ -182,7 +66,12 @@ const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio
}, },
{ {
dataTestId: `desktopNotification-${NotificationLevels.MENTION}`, dataTestId: `desktopNotification-${NotificationLevels.MENTION}`,
title: desktopNotificationInputFieldOptions.mentions, title: (
<FormattedMessage
id='channel_notifications.desktopNotificationMentionLabel'
defaultMessage='Mentions, direct messages, and keywords only'
/>
),
name: `desktopNotification-${NotificationLevels.MENTION}`, name: `desktopNotification-${NotificationLevels.MENTION}`,
key: `desktopNotification-${NotificationLevels.MENTION}`, key: `desktopNotification-${NotificationLevels.MENTION}`,
value: NotificationLevels.MENTION, value: NotificationLevels.MENTION,
@@ -194,7 +83,12 @@ const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio
}, },
{ {
dataTestId: `desktopNotification-${NotificationLevels.NONE}`, dataTestId: `desktopNotification-${NotificationLevels.NONE}`,
title: desktopNotificationInputFieldOptions.nothing, title: (
<FormattedMessage
id='channel_notifications.desktopNotificationNothingLabel'
defaultMessage='Nothing'
/>
),
name: `desktopNotification-${NotificationLevels.NONE}`, name: `desktopNotification-${NotificationLevels.NONE}`,
key: `desktopNotification-${NotificationLevels.NONE}`, key: `desktopNotification-${NotificationLevels.NONE}`,
value: NotificationLevels.NONE, value: NotificationLevels.NONE,
@@ -208,12 +102,17 @@ const desktopNotificationInputFieldData = (defaultOption: string): FieldsetRadio
}; };
}; };
const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio => { export const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio => {
return { return {
options: [ options: [
{ {
dataTestId: `MobileNotification-${NotificationLevels.ALL}`, dataTestId: `MobileNotification-${NotificationLevels.ALL}`,
title: mobileNotificationInputFieldOptions.allNewMessages, title: (
<FormattedMessage
id='channel_notifications.MobileNotificationAllLabel'
defaultMessage='All new messages'
/>
),
name: `MobileNotification-${NotificationLevels.ALL}`, name: `MobileNotification-${NotificationLevels.ALL}`,
key: `MobileNotification-${NotificationLevels.ALL}`, key: `MobileNotification-${NotificationLevels.ALL}`,
value: NotificationLevels.ALL, value: NotificationLevels.ALL,
@@ -225,7 +124,12 @@ const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio
}, },
{ {
dataTestId: `MobileNotification-${NotificationLevels.MENTION}`, dataTestId: `MobileNotification-${NotificationLevels.MENTION}`,
title: mobileNotificationInputFieldOptions.mentions, title: (
<FormattedMessage
id='channel_notifications.MobileNotificationMentionLabel'
defaultMessage='Mentions, direct messages, and keywords only'
/>
),
name: `MobileNotification-${NotificationLevels.MENTION}`, name: `MobileNotification-${NotificationLevels.MENTION}`,
key: `MobileNotification-${NotificationLevels.MENTION}`, key: `MobileNotification-${NotificationLevels.MENTION}`,
value: NotificationLevels.MENTION, value: NotificationLevels.MENTION,
@@ -237,7 +141,12 @@ const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio
}, },
{ {
dataTestId: `MobileNotification-${NotificationLevels.NONE}`, dataTestId: `MobileNotification-${NotificationLevels.NONE}`,
title: mobileNotificationInputFieldOptions.nothing, title: (
<FormattedMessage
id='channel_notifications.MobileNotificationNothingLabel'
defaultMessage='Nothing'
/>
),
name: `MobileNotification-${NotificationLevels.NONE}`, name: `MobileNotification-${NotificationLevels.NONE}`,
key: `MobileNotification-${NotificationLevels.NONE}`, key: `MobileNotification-${NotificationLevels.NONE}`,
value: NotificationLevels.NONE, value: NotificationLevels.NONE,
@@ -253,22 +162,11 @@ const mobileNotificationInputFieldData = (defaultOption: string): FieldsetRadio
const utils = { const utils = {
desktopNotificationInputFieldData, desktopNotificationInputFieldData,
DesktopNotificationsSectionDesc: translations.DesktopNotificationsSectionDesc,
DesktopNotificationsSectionTitle: translations.DesktopNotificationsSectionTitle,
IgnoreMentionsDesc: translations.IgnoreMentionsDesc,
IgnoreMentionsInputFieldData, IgnoreMentionsInputFieldData,
mobileNotificationInputFieldData, mobileNotificationInputFieldData,
MobileNotificationsSectionDesc: translations.MobileNotificationsSectionDesc,
MobileNotificationsSectionTitle: translations.MobileNotificationsSectionTitle,
MuteAndIgnoreSectionTitle: translations.MuteAndIgnoreSectionTitle,
MuteChannelDesc: translations.MuteChannelDesc,
MuteChannelInputFieldData, MuteChannelInputFieldData,
NotifyMeTitle: translations.NotifyMeTitle,
DesktopReplyThreadsInputFieldData, DesktopReplyThreadsInputFieldData,
ThreadsReplyTitle: translations.ThreadsReplyTitle,
MobileReplyThreadsInputFieldData, MobileReplyThreadsInputFieldData,
AutoFollowThreadsTitle: translations.AutoFollowThreadsTitle,
AutoFollowThreadsDesc: translations.AutoFollowThreadsDesc,
AutoFollowThreadsInputFieldData, AutoFollowThreadsInputFieldData,
sameMobileSettingsDesktopInputFieldData, sameMobileSettingsDesktopInputFieldData,
}; };

Просмотреть файл

@@ -16,7 +16,6 @@ import useGetLimits from 'components/common/hooks/useGetLimits';
import useGetUsage from 'components/common/hooks/useGetUsage'; import useGetUsage from 'components/common/hooks/useGetUsage';
import useOpenPricingModal from 'components/common/hooks/useOpenPricingModal'; import useOpenPricingModal from 'components/common/hooks/useOpenPricingModal';
import NotifyAdminCTA from 'components/notify_admin_cta/notify_admin_cta'; import NotifyAdminCTA from 'components/notify_admin_cta/notify_admin_cta';
import Tooltip from 'components/tooltip';
import {CloudProducts, LicenseSkus, MattermostFeatures, Preferences} from 'utils/constants'; import {CloudProducts, LicenseSkus, MattermostFeatures, Preferences} from 'utils/constants';
import {asGBString} from 'utils/limits'; import {asGBString} from 'utils/limits';
@@ -147,19 +146,13 @@ function FileLimitStickyBanner() {
/> />
); );
const tooltip = (
<Tooltip id='file_limit_banner_snooze'>
{formatMessage({id: 'create_post.file_limit_sticky_banner.snooze_tooltip', defaultMessage: 'Snooze for {snoozeDays} days'}, {snoozeDays: snoozeCoolOffDays})}
</Tooltip>
);
return ( return (
<StyledDiv id='cloud_file_limit_banner'> <StyledDiv id='cloud_file_limit_banner'>
<AlertBanner <AlertBanner
mode={'warning'} mode={'warning'}
variant={'app'} variant={'app'}
onDismiss={snoozeBanner} onDismiss={snoozeBanner}
closeBtnTooltip={tooltip} closeBtnTooltip={formatMessage({id: 'create_post.file_limit_sticky_banner.snooze_tooltip', defaultMessage: 'Snooze for {snoozeDays} days'}, {snoozeDays: snoozeCoolOffDays})}
title={title} title={title}
message={isAdmin ? adminMessage : nonAdminMessage} message={isAdmin ? adminMessage : nonAdminMessage}
/> />

Просмотреть файл

@@ -2,27 +2,12 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React, {useCallback, useState} from 'react'; import React, {useCallback, useState} from 'react';
import {defineMessages, useIntl} from 'react-intl'; import {FormattedMessage, useIntl} from 'react-intl';
import SelectTextInput, {type SelectTextInputOption} from 'components/common/select_text_input/select_text_input'; import SelectTextInput, {type SelectTextInputOption} from 'components/common/select_text_input/select_text_input';
import CheckboxSettingItem from 'components/widgets/modals/components/checkbox_setting_item'; import CheckboxSettingItem from 'components/widgets/modals/components/checkbox_setting_item';
import {type SaveChangesPanelState} from 'components/widgets/modals/components/save_changes_panel'; import {type SaveChangesPanelState} from 'components/widgets/modals/components/save_changes_panel';
const translations = defineMessages({
AllowedDomainsTitle: {
id: 'general_tab.AllowedDomainsTitle',
defaultMessage: 'Users with a specific email domain',
},
AllowedDomainsInfo: {
id: 'general_tab.AllowedDomainsInfo',
defaultMessage: 'When enabled, users can only join the team if their email matches a specific domain (e.g. "mattermost.org")',
},
AllowedDomains: {
id: 'general_tab.allowedDomains',
defaultMessage: 'Allow only users with a specific email domain to join this team',
},
});
type Props = { type Props = {
allowedDomains: string[]; allowedDomains: string[];
setAllowedDomains: (domains: string[]) => void; setAllowedDomains: (domains: string[]) => void;
@@ -52,15 +37,28 @@ const AllowedDomainsSelect = ({allowedDomains, setAllowedDomains, setHasChanges,
setSaveChangesPanelState('editing'); setSaveChangesPanelState('editing');
setAllowedDomains(allowedDomainsOptions?.map((domain) => domain.value) || []); setAllowedDomains(allowedDomainsOptions?.map((domain) => domain.value) || []);
}, [setAllowedDomains, setHasChanges, setSaveChangesPanelState]); }, [setAllowedDomains, setHasChanges, setSaveChangesPanelState]);
return ( return (
<> <>
<CheckboxSettingItem <CheckboxSettingItem
inputFieldTitle={
<FormattedMessage
id='general_tab.allowedDomains'
defaultMessage='Allow only users with a specific email domain to join this team'
/>
}
data-testid='allowedDomainsCheckbox' data-testid='allowedDomainsCheckbox'
className='access-allowed-domains-section' className='access-allowed-domains-section'
title={translations.AllowedDomainsTitle} title={formatMessage({
description={translations.AllowedDomainsInfo} id: 'general_tab.AllowedDomainsTitle',
defaultMessage: 'Users with a specific email domain',
})}
description={formatMessage({
id: 'general_tab.AllowedDomainsInfo',
defaultMessage: 'When enabled, users can only join the team if their email matches a specific domain (e.g. "mattermost.org")',
})}
descriptionAboveContent={true} descriptionAboveContent={true}
inputFieldData={{title: translations.AllowedDomains, name: 'name'}} inputFieldData={{name: 'name'}}
inputFieldValue={showAllowedDomains} inputFieldValue={showAllowedDomains}
handleChange={handleEnableAllowedDomains} handleChange={handleEnableAllowedDomains}
/> />

Просмотреть файл

@@ -24,14 +24,6 @@ const translations = defineMessages({
id: 'team_settings.openInviteDescription.error', id: 'team_settings.openInviteDescription.error',
defaultMessage: 'There was an error generating the invite code, please try again', defaultMessage: 'There was an error generating the invite code, please try again',
}, },
CodeTitle: {
id: 'general_tab.codeTitle',
defaultMessage: 'Invite Code',
},
CodeLongDesc: {
id: 'general_tab.codeLongDesc',
defaultMessage: 'The Invite Code is part of the unique team invitation link which is sent to members youre inviting to this team. Regenerating the code creates a new invitation link and invalidates the previous link.',
},
}); });
type Props = { type Props = {
@@ -91,8 +83,15 @@ const InviteSectionInput = ({regenerateTeamInviteId}: Props) => {
return ( return (
<BaseSettingItem <BaseSettingItem
className='access-invite-section' className='access-invite-section'
title={translations.CodeTitle} title={formatMessage({
description={translations.CodeLongDesc} id: 'general_tab.codeTitle',
defaultMessage: 'Invite Code',
})}
description={formatMessage({
id: 'general_tab.codeLongDesc',
defaultMessage: 'The Invite Code is part of the unique team invitation link which is sent to members youre inviting to this team. Regenerating the code creates a new invitation link and invalidates the previous link.',
})}
content={inviteSectionInput} content={inviteSectionInput}
error={inviteIdError} error={inviteIdError}
descriptionAboveContent={true} descriptionAboveContent={true}

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React from 'react';
import {defineMessages, useIntl} from 'react-intl'; import {FormattedMessage, useIntl} from 'react-intl';
import ExternalLink from 'components/external_link'; import ExternalLink from 'components/external_link';
import BaseSettingItem from 'components/widgets/modals/components/base_setting_item'; import BaseSettingItem from 'components/widgets/modals/components/base_setting_item';
@@ -14,21 +14,6 @@ type Props = {
setAllowOpenInvite: (value: boolean) => void; setAllowOpenInvite: (value: boolean) => void;
}; };
const translations = defineMessages({
OpenInviteText: {
id: 'general_tab.openInviteText',
defaultMessage: 'Users on this server',
},
OpenInviteDesc: {
id: 'general_tab.openInviteDesc',
defaultMessage: 'When enabled, a link to this team will be included on the landing page allowing anyone with an account to join this team. Changing this setting will create a new invitation link and invalidate the previous link.',
},
OpenInviteTitle: {
id: 'general_tab.openInviteTitle',
defaultMessage: 'Allow only users with a specific email domain to join this team',
},
});
const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: Props) => { const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: Props) => {
const {formatMessage} = useIntl(); const {formatMessage} = useIntl();
if (isGroupConstrained) { if (isGroupConstrained) {
@@ -52,8 +37,14 @@ const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: P
return ( return (
<BaseSettingItem <BaseSettingItem
className='access-invite-domains-section' className='access-invite-domains-section'
title={translations.OpenInviteText} title={formatMessage({
description={translations.OpenInviteDesc} id: 'general_tab.openInviteText',
defaultMessage: 'Users on this server',
})}
description={formatMessage({
id: 'general_tab.openInviteDesc',
defaultMessage: 'When enabled, a link to this team will be included on the landing page allowing anyone with an account to join this team. Changing this setting will create a new invitation link and invalidate the previous link.',
})}
descriptionAboveContent={true} descriptionAboveContent={true}
content={groupConstrainedContent} content={groupConstrainedContent}
/> />
@@ -63,11 +54,23 @@ const OpenInvite = ({isGroupConstrained, allowOpenInvite, setAllowOpenInvite}: P
return ( return (
<CheckboxSettingItem <CheckboxSettingItem
className='access-invite-domains-section' className='access-invite-domains-section'
inputFieldData={{title: translations.OpenInviteTitle, name: 'name'}} inputFieldTitle={
<FormattedMessage
id='general_tab.openInviteTitle'
defaultMessage='Allow only users with a specific email domain to join this team'
/>
}
inputFieldData={{name: 'name'}}
inputFieldValue={allowOpenInvite} inputFieldValue={allowOpenInvite}
handleChange={setAllowOpenInvite} handleChange={setAllowOpenInvite}
title={translations.OpenInviteText} title={formatMessage({
description={translations.OpenInviteDesc} id: 'general_tab.openInviteText',
defaultMessage: 'Users on this server',
})}
description={formatMessage({
id: 'general_tab.openInviteDesc',
defaultMessage: 'When enabled, a link to this team will be included on the landing page allowing anyone with an account to join this team. Changing this setting will create a new invitation link and invalidate the previous link.',
})}
descriptionAboveContent={true} descriptionAboveContent={true}
/> />
); );

Просмотреть файл

@@ -3,7 +3,7 @@
import React, {useCallback} from 'react'; import React, {useCallback} from 'react';
import type {ChangeEvent} from 'react'; import type {ChangeEvent} from 'react';
import {defineMessages, useIntl} from 'react-intl'; import {useIntl} from 'react-intl';
import type {Team} from '@mattermost/types/teams'; import type {Team} from '@mattermost/types/teams';
@@ -12,13 +12,6 @@ import BaseSettingItem, {type BaseSettingItemProps} from 'components/widgets/mod
import Constants from 'utils/constants'; import Constants from 'utils/constants';
const translations = defineMessages({
TeamDescriptionInfo: {
id: 'general_tab.teamDescriptionInfo',
defaultMessage: 'Team description provides additional information to help users select the right team. Maximum of 50 characters.',
},
});
type Props = { type Props = {
handleDescriptionChanges: (name: string) => void; handleDescriptionChanges: (name: string) => void;
description: Team['description']; description: Team['description'];
@@ -47,7 +40,10 @@ const TeamDescriptionSection = ({handleDescriptionChanges, clientError, descript
return ( return (
<BaseSettingItem <BaseSettingItem
description={translations.TeamDescriptionInfo} description={formatMessage({
id: 'general_tab.teamDescriptionInfo',
defaultMessage: 'Team description provides additional information to help users select the right team. Maximum of 50 characters.',
})}
content={descriptionSectionInput} content={descriptionSectionInput}
className='description-setting-item' className='description-setting-item'
error={clientError} error={clientError}

Просмотреть файл

@@ -3,7 +3,7 @@
import React, {useCallback} from 'react'; import React, {useCallback} from 'react';
import type {ChangeEvent} from 'react'; import type {ChangeEvent} from 'react';
import {defineMessages, useIntl} from 'react-intl'; import {useIntl} from 'react-intl';
import type {Team} from '@mattermost/types/teams'; import type {Team} from '@mattermost/types/teams';
@@ -12,17 +12,6 @@ import BaseSettingItem, {type BaseSettingItemProps} from 'components/widgets/mod
import Constants from 'utils/constants'; import Constants from 'utils/constants';
const translations = defineMessages({
TeamInfo: {
id: 'general_tab.teamInfo',
defaultMessage: 'Team info',
},
TeamNameInfo: {
id: 'general_tab.teamNameInfo',
defaultMessage: 'This name will appear on your sign-in screen and at the top of the left sidebar.',
},
});
type Props = { type Props = {
handleNameChanges: (name: string) => void; handleNameChanges: (name: string) => void;
name: Team['display_name']; name: Team['display_name'];
@@ -48,8 +37,14 @@ const TeamNameSection = ({clientError, handleNameChanges, name}: Props) => {
return ( return (
<BaseSettingItem <BaseSettingItem
title={translations.TeamInfo} title={formatMessage({
description={translations.TeamNameInfo} id: 'general_tab.teamInfo',
defaultMessage: 'Team info',
})}
description={formatMessage({
id: 'general_tab.teamNameInfo',
defaultMessage: 'This name will appear on your sign-in screen and at the top of the left sidebar.',
})}
content={nameSectionInput} content={nameSectionInput}
error={clientError} error={clientError}
/> />

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React, {type ChangeEvent, useRef, useState, useEffect, useCallback} from 'react'; import React, {type ChangeEvent, useRef, useState, useEffect, useCallback} from 'react';
import {defineMessages, useIntl} from 'react-intl'; import {useIntl} from 'react-intl';
import {TrashCanOutlineIcon} from '@mattermost/compass-icons/components'; import {TrashCanOutlineIcon} from '@mattermost/compass-icons/components';
import type {Team} from '@mattermost/types/teams'; import type {Team} from '@mattermost/types/teams';
@@ -14,19 +14,8 @@ import type {BaseSettingItemProps} from 'components/widgets/modals/components/ba
import Constants from 'utils/constants'; import Constants from 'utils/constants';
import * as FileUtils from 'utils/file_utils'; import * as FileUtils from 'utils/file_utils';
import {imageURLForTeam} from 'utils/utils'; import {imageURLForTeam} from 'utils/utils';
import './team_picture_section.scss';
const translations = defineMessages({ import './team_picture_section.scss';
Title: {
id: 'setting_picture.title',
defaultMessage: 'Team Icon',
},
Profile: {
id: 'setting_picture.help.profile',
defaultMessage: 'Upload a picture in BMP, JPG, JPEG, or PNG format. Maximum file size: {max}',
values: {max: '50MB'},
},
});
type Props = { type Props = {
team: Team; team: Team;
@@ -168,8 +157,19 @@ const TeamPictureSection = ({team, file, teamName, disabled, onFileChange, onRem
return ( return (
<BaseSettingItem <BaseSettingItem
title={translations.Title} title={formatMessage({
description={teamImageSource ? undefined : translations.Profile} id: 'setting_picture.title',
defaultMessage: 'Team Icon',
})}
description={teamImageSource ? undefined : formatMessage(
{
id: 'setting_picture.help.profile',
defaultMessage: 'Upload a picture in BMP, JPG, JPEG, or PNG format. Maximum file size: {max}',
},
{
max: '50MB',
},
)}
content={teamPictureSection} content={teamPictureSection}
className='picture-setting-item' className='picture-setting-item'
error={clientError} error={clientError}

Просмотреть файл

@@ -9,11 +9,11 @@
padding: 0; padding: 0;
margin: 0 0 8px; margin: 0 0 8px;
color: var(--center-channel-color); color: var(--center-channel-color);
font-family: 'Metropolis', sans-serif; font-family: "Open Sans", sans-serif;
font-size: 16px; font-size: 14px;
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
line-height: 24px; line-height: 20px;
} }
&__description { &__description {
@@ -82,6 +82,7 @@
width: fit-content; width: fit-content;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin-bottom: 0;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;

Просмотреть файл

@@ -16,8 +16,8 @@ type ExtendedMessageDescriptor = MessageDescriptor & {
}; };
export type BaseSettingItemProps = { export type BaseSettingItemProps = {
title?: ExtendedMessageDescriptor; title?: string;
description?: ExtendedMessageDescriptor; description?: string;
error?: ExtendedMessageDescriptor; error?: ExtendedMessageDescriptor;
}; };
@@ -29,21 +29,22 @@ type Props = BaseSettingItemProps & {
function BaseSettingItem({title, description, content, className, error, descriptionAboveContent = false}: Props): JSX.Element { function BaseSettingItem({title, description, content, className, error, descriptionAboveContent = false}: Props): JSX.Element {
const {formatMessage} = useIntl(); const {formatMessage} = useIntl();
const Title = title && (
const titleComponent = title && (
<h4 <h4
data-testid='mm-modal-generic-section-item__title' data-testid='mm-modal-generic-section-item__title'
className='mm-modal-generic-section-item__title' className='mm-modal-generic-section-item__title'
> >
{formatMessage({id: title.id, defaultMessage: title.defaultMessage}, title.values)} {title}
</h4> </h4>
); );
const Description = description && ( const descriptionComponent = description && (
<p <p
data-testid='mm-modal-generic-section-item__description' data-testid='mm-modal-generic-section-item__description'
className='mm-modal-generic-section-item__description' className='mm-modal-generic-section-item__description'
> >
{formatMessage({id: description.id, defaultMessage: description.defaultMessage}, description.values)} {description}
</p> </p>
); );
@@ -57,19 +58,17 @@ function BaseSettingItem({title, description, content, className, error, descrip
</div> </div>
); );
const getClassName = classNames('mm-modal-generic-section-item', className);
return ( return (
<div className={getClassName}> <div className={classNames('mm-modal-generic-section-item', className)}>
{Title} {titleComponent}
{descriptionAboveContent ? Description : undefined} {descriptionAboveContent ? descriptionComponent : undefined}
<div <div
data-testid='mm-modal-generic-section-item__content' data-testid='mm-modal-generic-section-item__content'
className='mm-modal-generic-section-item__content' className='mm-modal-generic-section-item__content'
> >
{content} {content}
</div> </div>
{descriptionAboveContent ? undefined : Description} {descriptionAboveContent ? undefined : descriptionComponent}
{Error} {Error}
</div> </div>
); );

Просмотреть файл

@@ -1,35 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react'; import React from 'react';
import {FormattedMessage} from 'react-intl';
import type {MessageDescriptor} from 'react-intl';
import type {BaseSettingItemProps} from './base_setting_item'; import type {BaseSettingItemProps} from './base_setting_item';
import BaseSettingItem from './base_setting_item'; import BaseSettingItem from './base_setting_item';
export type FieldsetCheckbox = { export type FieldsetCheckbox = {
dataTestId?: string; dataTestId?: string;
title: MessageDescriptor;
name: string; name: string;
} }
type Props = BaseSettingItemProps & { type Props = BaseSettingItemProps & {
inputFieldData: FieldsetCheckbox; inputFieldData: FieldsetCheckbox;
inputFieldValue: boolean; inputFieldValue: boolean;
/**
* The title of the checkbox input field, pass in FormattedMessage component for styling compatibility
*/
inputFieldTitle: ReactNode;
handleChange: (e: boolean) => void; handleChange: (e: boolean) => void;
className?: string; className?: string;
descriptionAboveContent?: boolean; descriptionAboveContent?: boolean;
} }
function CheckboxSettingItem({
export default function CheckboxSettingItem({
title, title,
description, description,
inputFieldData, inputFieldData,
inputFieldValue, inputFieldValue,
inputFieldTitle,
handleChange, handleChange,
className, className,
descriptionAboveContent = false, descriptionAboveContent = false,
}: Props): JSX.Element { }: Props) {
const content = ( const content = (
<fieldset <fieldset
key={inputFieldData.name} key={inputFieldData.name}
@@ -44,14 +49,11 @@ function CheckboxSettingItem({
checked={inputFieldValue} checked={inputFieldValue}
onChange={(e) => handleChange(e.target.checked)} onChange={(e) => handleChange(e.target.checked)}
/> />
<FormattedMessage {inputFieldTitle}
id={inputFieldData.title.id}
defaultMessage={inputFieldData.title.defaultMessage}
/>
</label> </label>
<br/>
</fieldset> </fieldset>
); );
return ( return (
<BaseSettingItem <BaseSettingItem
content={content} content={content}
@@ -62,5 +64,3 @@ function CheckboxSettingItem({
/> />
); );
} }
export default CheckboxSettingItem;

Просмотреть файл

@@ -15,6 +15,7 @@
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
line-height: 28px; line-height: 28px;
white-space: nowrap;
} }
&__vertical-divider { &__vertical-divider {
@@ -42,28 +43,6 @@
flex: 1 1 auto; flex: 1 1 auto;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
color: rgba(var(--center-channel-color-rgb), 0.56);
gap: 24px;
}
&__close-btn {
display: flex;
overflow: hidden;
width: 40px;
height: 40px;
align-items: center;
justify-content: center;
border: unset;
border-radius: 4px;
margin-left: 8px;
background: transparent;
color: rgba(var(--center-channel-color-rgb), 0.56);
&:hover,
&.active {
background: rgba(var(--center-channel-color-rgb), 0.08);
color: rgba(var(--center-channel-color-rgb), 0.72);
}
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {

Просмотреть файл

@@ -3,7 +3,6 @@
import React from 'react'; import React from 'react';
import {CloseIcon} from '@mattermost/compass-icons/components';
import './modal_header.scss'; import './modal_header.scss';
type Props = { type Props = {
@@ -29,11 +28,8 @@ function ModalHeader({id, title, subtitle, handleClose}: Props) {
className='mm-modal-header__ctr' className='mm-modal-header__ctr'
onClick={handleClose} onClick={handleClose}
> >
<button className='style--none mm-modal-header__close-btn'> <button className='btn btn-icon'>
<CloseIcon <i className='icon icon-close'/>
size={24}
color={'currentcolor'}
/>
</button> </button>
</div> </div>
</header> </header>

Просмотреть файл

@@ -2,46 +2,41 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
&__info-ctr { .modalSectionHeader {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: 8px;
gap: 8px; gap: 8px;
margin-block-end: 24px;
.modalSectionTitle {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: var(--center-channel-color);
font-family: "Metropolis", sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 600;
gap: 8px;
line-height: 24px;
}
.modalSectionDescription {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: rgba(var(--center-channel-color-rgb), 0.64);
font-family: "Open Sans", sans-serif;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
} }
&__title { .modalSectionContent {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: var(--center-channel-color);
font-family: 'Metropolis', sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 24px;
}
&__row {
display: flex;
align-items: center;
gap: 8px;
}
&__description {
display: flex;
align-items: center;
padding: 0;
margin: 0;
color: rgba(var(--center-channel-color-rgb), 0.64);
font-family: 'Open Sans', sans-serif;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
&__content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24px;

Просмотреть файл

@@ -2,14 +2,13 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React from 'react';
import type {MessageDescriptor} from 'react-intl'; import type {ReactNode} from 'react';
import {useIntl} from 'react-intl';
import './modal_section.scss'; import './modal_section.scss';
type Props = { type Props = {
title?: MessageDescriptor; title?: ReactNode;
description?: MessageDescriptor; description?: ReactNode;
content: JSX.Element; content: JSX.Element;
titleSuffix?: JSX.Element; titleSuffix?: JSX.Element;
}; };
@@ -19,46 +18,29 @@ function ModalSection({
description, description,
content, content,
titleSuffix, titleSuffix,
}: Props): JSX.Element { }: Props) {
const {formatMessage} = useIntl(); const titleComponent = title && (
const titleContent = title ? ( <h4 className='modalSectionTitle'>
<h4 className='mm-modal-generic-section__title'> {title}
{formatMessage({id: title.id, defaultMessage: title.defaultMessage})} {titleSuffix}
</h4> </h4>
) : undefined; );
const descriptionContent = description && ( const descriptionComponent = description && (
<p className='mm-modal-generic-section__description'> <p className='modalSectionDescription'>
{formatMessage({id: description.id, defaultMessage: description.defaultMessage})} {description}
</p> </p>
); );
function titleRow() {
if (titleSuffix) {
return (<div className='mm-modal-generic-section__row'>
{titleContent}
{titleSuffix}
</div>);
}
return titleContent;
}
const titleDescriptionSection = () => {
if (title || description) {
return (
<div className='mm-modal-generic-section__title-description-ctr'>
{titleRow()}
{descriptionContent}
</div>
);
}
return null;
};
return ( return (
<section className='mm-modal-generic-section'> <section className='mm-modal-generic-section'>
{titleDescriptionSection()} {(title || description) && (
<div className='mm-modal-generic-section__content'> <div className='modalSectionHeader'>
{titleComponent}
{descriptionComponent}
</div>
)}
<div className='modalSectionContent'>
{content} {content}
</div> </div>
</section> </section>

Просмотреть файл

@@ -1,9 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {ReactNode} from 'react';
import React from 'react'; import React from 'react';
import type {MessageDescriptor} from 'react-intl';
import {FormattedMessage} from 'react-intl';
import type {BaseSettingItemProps} from './base_setting_item'; import type {BaseSettingItemProps} from './base_setting_item';
import BaseSettingItem from './base_setting_item'; import BaseSettingItem from './base_setting_item';
@@ -11,7 +10,7 @@ import BaseSettingItem from './base_setting_item';
export type FieldsetRadio = { export type FieldsetRadio = {
options: Array<{ options: Array<{
dataTestId?: string; dataTestId?: string;
title: MessageDescriptor; title: ReactNode;
name: string; name: string;
key: string; key: string;
value: string; value: string;
@@ -20,13 +19,16 @@ export type FieldsetRadio = {
} }
type Props = BaseSettingItemProps & { type Props = BaseSettingItemProps & {
className?: string;
inputFieldData: FieldsetRadio; inputFieldData: FieldsetRadio;
inputFieldValue: string; inputFieldValue: string;
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void; handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
} }
function RadioSettingItem({ function RadioSettingItem({
title, title,
description, description,
className,
inputFieldData, inputFieldData,
inputFieldValue, inputFieldValue,
handleChange, handleChange,
@@ -46,10 +48,7 @@ function RadioSettingItem({
value={option.value} value={option.value}
onChange={handleChange} onChange={handleChange}
/> />
<FormattedMessage {option.title}
id={option.title.id}
defaultMessage={option.title.defaultMessage}
/>
{option.suffix} {option.suffix}
</label> </label>
); );
@@ -62,6 +61,7 @@ function RadioSettingItem({
); );
return ( return (
<BaseSettingItem <BaseSettingItem
className={className}
content={content} content={content}
title={title} title={title}
description={description} description={description}