[MM-61066] Add support for disabled notifications in desktop (#28739)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
48c14af280
Коммит
fe5756225c
@@ -8,6 +8,7 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
|||||||
|
|
||||||
import NotificationPermissionNeverGrantedBar from 'components/announcement_bar/notification_permission_bar/notification_permission_never_granted_bar';
|
import NotificationPermissionNeverGrantedBar from 'components/announcement_bar/notification_permission_bar/notification_permission_never_granted_bar';
|
||||||
import NotificationPermissionUnsupportedBar from 'components/announcement_bar/notification_permission_bar/notification_permission_unsupported_bar';
|
import NotificationPermissionUnsupportedBar from 'components/announcement_bar/notification_permission_bar/notification_permission_unsupported_bar';
|
||||||
|
import {useDesktopAppNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isNotificationAPISupported,
|
isNotificationAPISupported,
|
||||||
@@ -19,6 +20,8 @@ import {
|
|||||||
export default function NotificationPermissionBar() {
|
export default function NotificationPermissionBar() {
|
||||||
const isLoggedIn = Boolean(useSelector(getCurrentUserId));
|
const isLoggedIn = Boolean(useSelector(getCurrentUserId));
|
||||||
|
|
||||||
|
useDesktopAppNotificationPermission();
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {useCallback, useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
import type {NotificationPermissionNeverGranted} from 'utils/notifications';
|
||||||
|
import {isNotificationAPISupported} from 'utils/notifications';
|
||||||
|
import {isDesktopApp} from 'utils/user_agent';
|
||||||
|
|
||||||
|
export type DesktopNotificationPermission = Exclude<NotificationPermission, typeof NotificationPermissionNeverGranted> | undefined;
|
||||||
|
|
||||||
|
// We store the permission state globally here to avoid calling requestPermission() multiple times
|
||||||
|
let desktopNotificationPermissionGlobalState: DesktopNotificationPermission | undefined;
|
||||||
|
|
||||||
|
// This is used to request notification permission for desktop app
|
||||||
|
// it also returns the permission state. We use this as a workaround for bug with Electron - https://github.com/electron/electron/issues/11221
|
||||||
|
// tl;dr Electron always show 'granted' when queries for Notification.permission, hence this workaround
|
||||||
|
export function useDesktopAppNotificationPermission(): [DesktopNotificationPermission, () => Promise<NotificationPermission>] {
|
||||||
|
const [desktopNotificationPermission, setDesktopNotificationPermission] = useState<DesktopNotificationPermission>(undefined);
|
||||||
|
|
||||||
|
const isDesktop = isDesktopApp();
|
||||||
|
const isSupported = isNotificationAPISupported();
|
||||||
|
|
||||||
|
const requestDesktopNotificationPermission = useCallback(async () => {
|
||||||
|
// Based on Electron's notification permission it will have following states
|
||||||
|
// - allowed - No further action needed
|
||||||
|
// - denied permanently - No further action needed
|
||||||
|
// - denied (temporary) - In this case, electron notification permission dialog is shown with requestPermission()
|
||||||
|
const permission = await Notification.requestPermission();
|
||||||
|
|
||||||
|
// Update the global state
|
||||||
|
desktopNotificationPermissionGlobalState = permission as DesktopNotificationPermission;
|
||||||
|
|
||||||
|
// Update the local state
|
||||||
|
setDesktopNotificationPermission(permission as DesktopNotificationPermission);
|
||||||
|
|
||||||
|
return permission;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDesktop || !isSupported) {
|
||||||
|
setDesktopNotificationPermission(undefined);
|
||||||
|
} else if (desktopNotificationPermissionGlobalState === undefined) {
|
||||||
|
// We are in initial state, we need to request permission now
|
||||||
|
requestDesktopNotificationPermission();
|
||||||
|
} else if (desktopNotificationPermissionGlobalState !== undefined) {
|
||||||
|
setDesktopNotificationPermission(desktopNotificationPermissionGlobalState);
|
||||||
|
}
|
||||||
|
}, [isDesktop, isSupported, requestDesktopNotificationPermission]);
|
||||||
|
|
||||||
|
return [desktopNotificationPermission, requestDesktopNotificationPermission];
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ const SectionNoticeButton = ({
|
|||||||
<button
|
<button
|
||||||
onClick={button.onClick}
|
onClick={button.onClick}
|
||||||
className={classNames('btn btn-sm sectionNoticeButton', buttonClass)}
|
className={classNames('btn btn-sm sectionNoticeButton', buttonClass)}
|
||||||
|
disabled={button.disabled}
|
||||||
>
|
>
|
||||||
{button.loading && (<i className='icon fa fa-pulse fa-spinner'/>)}
|
{button.loading && (<i className='icon fa fa-pulse fa-spinner'/>)}
|
||||||
{leading}
|
{leading}
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ export type SectionNoticeButtonProp = {
|
|||||||
trailingIcon?: string;
|
trailingIcon?: string;
|
||||||
leadingIcon?: string;
|
leadingIcon?: string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import * as useDesktopAppNotificationPermission from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
|
import type {DesktopNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
|
|
||||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import * as utilsNotifications from 'utils/notifications';
|
import * as utilsNotifications from 'utils/notifications';
|
||||||
|
|
||||||
@@ -47,4 +50,22 @@ describe('NotificationPermissionSectionNotice', () => {
|
|||||||
|
|
||||||
expect(container).toBeEmptyDOMElement();
|
expect(container).toBeEmptyDOMElement();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should render "Desktop denied" notice when desktop permission is denied', () => {
|
||||||
|
jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
|
||||||
|
jest.spyOn(useDesktopAppNotificationPermission, 'useDesktopAppNotificationPermission').mockReturnValue([utilsNotifications.NotificationPermissionDenied as DesktopNotificationPermission, jest.fn()]);
|
||||||
|
|
||||||
|
renderWithContext(<NotificationPermissionSectionNotice/>);
|
||||||
|
|
||||||
|
expect(screen.getByText('Desktop notifications permission required')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render nothing when desktop permission is granted', () => {
|
||||||
|
jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
|
||||||
|
jest.spyOn(useDesktopAppNotificationPermission, 'useDesktopAppNotificationPermission').mockReturnValue([utilsNotifications.NotificationPermissionGranted as DesktopNotificationPermission, jest.fn()]);
|
||||||
|
|
||||||
|
const {container} = renderWithContext(<NotificationPermissionSectionNotice/>);
|
||||||
|
|
||||||
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,17 +3,22 @@
|
|||||||
|
|
||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
|
import {useDesktopAppNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
import NotificationPermissionDeniedNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_denied_section_notice';
|
import NotificationPermissionDeniedNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_denied_section_notice';
|
||||||
import NotificationPermissionNeverGrantedNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_never_granted_section_notice';
|
import NotificationPermissionNeverGrantedNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_never_granted_section_notice';
|
||||||
import NotificationPermissionUnsupportedSectionNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_unsupported_section_notice';
|
import NotificationPermissionUnsupportedSectionNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_unsupported_section_notice';
|
||||||
|
|
||||||
import {getNotificationPermission, isNotificationAPISupported, NotificationPermissionDenied, NotificationPermissionNeverGranted} from 'utils/notifications';
|
import {getNotificationPermission, isNotificationAPISupported, NotificationPermissionDenied, NotificationPermissionNeverGranted} from 'utils/notifications';
|
||||||
|
|
||||||
|
import NotificationPermissionDesktopDeniedSectionNotice from './notification_permission_desktop_denied_section_notice';
|
||||||
|
|
||||||
export default function NotificationPermissionSectionNotice() {
|
export default function NotificationPermissionSectionNotice() {
|
||||||
const isNotificationSupported = isNotificationAPISupported();
|
const isNotificationSupported = isNotificationAPISupported();
|
||||||
|
|
||||||
const [notificationPermission, setNotificationPermission] = useState(getNotificationPermission());
|
const [notificationPermission, setNotificationPermission] = useState(getNotificationPermission());
|
||||||
|
|
||||||
|
const [desktopNotificationPermission, requestDesktopNotificationPermission] = useDesktopAppNotificationPermission();
|
||||||
|
|
||||||
function handleRequestNotificationClicked(permission: NotificationPermission) {
|
function handleRequestNotificationClicked(permission: NotificationPermission) {
|
||||||
setNotificationPermission(permission);
|
setNotificationPermission(permission);
|
||||||
}
|
}
|
||||||
@@ -22,6 +27,10 @@ export default function NotificationPermissionSectionNotice() {
|
|||||||
return <NotificationPermissionUnsupportedSectionNotice/>;
|
return <NotificationPermissionUnsupportedSectionNotice/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (desktopNotificationPermission === NotificationPermissionDenied) {
|
||||||
|
return <NotificationPermissionDesktopDeniedSectionNotice requestDesktopNotificationPermission={requestDesktopNotificationPermission}/>;
|
||||||
|
}
|
||||||
|
|
||||||
if (isNotificationSupported && notificationPermission === NotificationPermissionNeverGranted) {
|
if (isNotificationSupported && notificationPermission === NotificationPermissionNeverGranted) {
|
||||||
return <NotificationPermissionNeverGrantedNotice onCtaButtonClick={handleRequestNotificationClicked}/>;
|
return <NotificationPermissionNeverGrantedNotice onCtaButtonClick={handleRequestNotificationClicked}/>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React, {useCallback, useState} from 'react';
|
||||||
|
import {useIntl} from 'react-intl';
|
||||||
|
|
||||||
|
import SectionNotice from 'components/section_notice';
|
||||||
|
|
||||||
|
import {NotificationPermissionDenied} from 'utils/notifications';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
requestDesktopNotificationPermission: () => Promise<NotificationPermission>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NotificationPermissionDesktopDeniedSectionNotice(props: Props) {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const [checkedPermissionDenied, setCheckedPermissionDenied] = useState(false);
|
||||||
|
|
||||||
|
async function handleCheckPermissionButtonClick() {
|
||||||
|
const permission = await props.requestDesktopNotificationPermission();
|
||||||
|
if (permission === NotificationPermissionDenied) {
|
||||||
|
setCheckedPermissionDenied(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleInstructionButtonClick = useCallback(() => {
|
||||||
|
window.open('https://mattermost.com/pl/manage-notifications', '_blank', 'noopener,noreferrer');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const title = checkedPermissionDenied ? intl.formatMessage({
|
||||||
|
id: 'user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.titleDenied',
|
||||||
|
defaultMessage: 'Desktop notifications permission was denied',
|
||||||
|
}) : intl.formatMessage({
|
||||||
|
id: 'user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.title',
|
||||||
|
defaultMessage: 'Desktop notifications permission required',
|
||||||
|
});
|
||||||
|
|
||||||
|
const text = checkedPermissionDenied ? intl.formatMessage({
|
||||||
|
id: 'user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.messageDenied',
|
||||||
|
defaultMessage: 'Notifications for this Mattermost server are blocked. To receive notifications, please enable them manually.',
|
||||||
|
}) : intl.formatMessage({
|
||||||
|
id: 'user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.message',
|
||||||
|
defaultMessage: "You're missing important message and call notifications from Mattermost. To start receiving them, please enable them manually.",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='extraContentBeforeSettingList'>
|
||||||
|
<SectionNotice
|
||||||
|
type='danger'
|
||||||
|
title={title}
|
||||||
|
text={text}
|
||||||
|
primaryButton={{
|
||||||
|
text: intl.formatMessage({
|
||||||
|
id: 'user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.checkPermissionButton',
|
||||||
|
defaultMessage: 'Check permission',
|
||||||
|
}),
|
||||||
|
onClick: handleCheckPermissionButtonClick,
|
||||||
|
disabled: checkedPermissionDenied,
|
||||||
|
}}
|
||||||
|
tertiaryButton={{
|
||||||
|
text: intl.formatMessage({
|
||||||
|
id: 'user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.instructionButton',
|
||||||
|
defaultMessage: 'How to enable notifications',
|
||||||
|
}),
|
||||||
|
onClick: handleInstructionButtonClick,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import * as useDesktopAppNotificationPermission from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
|
import type {DesktopNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
|
|
||||||
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
import {renderWithContext, screen} from 'tests/react_testing_utils';
|
||||||
import * as utilsNotifications from 'utils/notifications';
|
import * as utilsNotifications from 'utils/notifications';
|
||||||
|
|
||||||
@@ -47,4 +50,22 @@ describe('NotificationPermissionTitleTag', () => {
|
|||||||
|
|
||||||
expect(container).toBeEmptyDOMElement();
|
expect(container).toBeEmptyDOMElement();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should render "Permission required" tag when desktop permission is denied', () => {
|
||||||
|
jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
|
||||||
|
jest.spyOn(useDesktopAppNotificationPermission, 'useDesktopAppNotificationPermission').mockReturnValue([utilsNotifications.NotificationPermissionDenied as DesktopNotificationPermission, jest.fn()]);
|
||||||
|
|
||||||
|
renderWithContext(<NotificationPermissionTitleTag/>);
|
||||||
|
|
||||||
|
expect(screen.queryByText('Permission required')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render nothing when desktop permission is granted', () => {
|
||||||
|
jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
|
||||||
|
jest.spyOn(useDesktopAppNotificationPermission, 'useDesktopAppNotificationPermission').mockReturnValue([utilsNotifications.NotificationPermissionGranted as DesktopNotificationPermission, jest.fn()]);
|
||||||
|
|
||||||
|
const {container} = renderWithContext(<NotificationPermissionTitleTag/>);
|
||||||
|
|
||||||
|
expect(container).toBeEmptyDOMElement();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
|
|
||||||
|
import {useDesktopAppNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||||
import Tag from 'components/widgets/tag/tag';
|
import Tag from 'components/widgets/tag/tag';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -16,6 +17,8 @@ import {
|
|||||||
export default function NotificationPermissionTitleTag() {
|
export default function NotificationPermissionTitleTag() {
|
||||||
const {formatMessage} = useIntl();
|
const {formatMessage} = useIntl();
|
||||||
|
|
||||||
|
const [desktopNotificationPermission] = useDesktopAppNotificationPermission();
|
||||||
|
|
||||||
if (!isNotificationAPISupported()) {
|
if (!isNotificationAPISupported()) {
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
@@ -32,7 +35,8 @@ export default function NotificationPermissionTitleTag() {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
getNotificationPermission() === NotificationPermissionNeverGranted ||
|
getNotificationPermission() === NotificationPermissionNeverGranted ||
|
||||||
getNotificationPermission() === NotificationPermissionDenied
|
getNotificationPermission() === NotificationPermissionDenied ||
|
||||||
|
desktopNotificationPermission === NotificationPermissionDenied
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
|
|||||||
@@ -5723,8 +5723,14 @@
|
|||||||
"user.settings.notifications.desktopAndMobile.nothing": "Nothing",
|
"user.settings.notifications.desktopAndMobile.nothing": "Nothing",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.noPermissionIssueTag": "Not supported",
|
"user.settings.notifications.desktopAndMobile.notificationSection.noPermissionIssueTag": "Not supported",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.button": "How to enable notifications",
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.button": "How to enable notifications",
|
||||||
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.instructionButton": "How to enable notifications",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.message": "You're missing important message and call notifications from Mattermost. To start receiving notifications, please enable notifications for Mattermost in your browser settings.",
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.message": "You're missing important message and call notifications from Mattermost. To start receiving notifications, please enable notifications for Mattermost in your browser settings.",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.title": "Browser notification permission was denied",
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDenied.title": "Browser notification permission was denied",
|
||||||
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.checkPermissionButton": "Check permission",
|
||||||
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.message": "You're missing important message and call notifications from Mattermost. To start receiving them, please enable them manually.",
|
||||||
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.messageDenied": "Notifications for this Mattermost server are blocked. To receive notifications, please enable them manually.",
|
||||||
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.title": "Desktop notifications permission required",
|
||||||
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionDeniedDesktop.titleDenied": "Desktop notifications permission was denied",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.permissionIssueTag": "Permission required",
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionIssueTag": "Permission required",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.permissionNeverGranted.button": "Enable notifications",
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionNeverGranted.button": "Enable notifications",
|
||||||
"user.settings.notifications.desktopAndMobile.notificationSection.permissionNeverGranted.message": "You're missing important message and call notifications from Mattermost. Mattermost notifications are disabled by this browser.",
|
"user.settings.notifications.desktopAndMobile.notificationSection.permissionNeverGranted.message": "You're missing important message and call notifications from Mattermost. Mattermost notifications are disabled by this browser.",
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user