[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 NotificationPermissionUnsupportedBar from 'components/announcement_bar/notification_permission_bar/notification_permission_unsupported_bar';
|
||||
import {useDesktopAppNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||
|
||||
import {
|
||||
isNotificationAPISupported,
|
||||
@@ -19,6 +20,8 @@ import {
|
||||
export default function NotificationPermissionBar() {
|
||||
const isLoggedIn = Boolean(useSelector(getCurrentUserId));
|
||||
|
||||
useDesktopAppNotificationPermission();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
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
|
||||
onClick={button.onClick}
|
||||
className={classNames('btn btn-sm sectionNoticeButton', buttonClass)}
|
||||
disabled={button.disabled}
|
||||
>
|
||||
{button.loading && (<i className='icon fa fa-pulse fa-spinner'/>)}
|
||||
{leading}
|
||||
|
||||
@@ -7,4 +7,5 @@ export type SectionNoticeButtonProp = {
|
||||
trailingIcon?: string;
|
||||
leadingIcon?: string;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
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 * as utilsNotifications from 'utils/notifications';
|
||||
|
||||
@@ -47,4 +50,22 @@ describe('NotificationPermissionSectionNotice', () => {
|
||||
|
||||
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 {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 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 {getNotificationPermission, isNotificationAPISupported, NotificationPermissionDenied, NotificationPermissionNeverGranted} from 'utils/notifications';
|
||||
|
||||
import NotificationPermissionDesktopDeniedSectionNotice from './notification_permission_desktop_denied_section_notice';
|
||||
|
||||
export default function NotificationPermissionSectionNotice() {
|
||||
const isNotificationSupported = isNotificationAPISupported();
|
||||
|
||||
const [notificationPermission, setNotificationPermission] = useState(getNotificationPermission());
|
||||
|
||||
const [desktopNotificationPermission, requestDesktopNotificationPermission] = useDesktopAppNotificationPermission();
|
||||
|
||||
function handleRequestNotificationClicked(permission: NotificationPermission) {
|
||||
setNotificationPermission(permission);
|
||||
}
|
||||
@@ -22,6 +27,10 @@ export default function NotificationPermissionSectionNotice() {
|
||||
return <NotificationPermissionUnsupportedSectionNotice/>;
|
||||
}
|
||||
|
||||
if (desktopNotificationPermission === NotificationPermissionDenied) {
|
||||
return <NotificationPermissionDesktopDeniedSectionNotice requestDesktopNotificationPermission={requestDesktopNotificationPermission}/>;
|
||||
}
|
||||
|
||||
if (isNotificationSupported && notificationPermission === NotificationPermissionNeverGranted) {
|
||||
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 * 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 * as utilsNotifications from 'utils/notifications';
|
||||
|
||||
@@ -47,4 +50,22 @@ describe('NotificationPermissionTitleTag', () => {
|
||||
|
||||
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 {useIntl} from 'react-intl';
|
||||
|
||||
import {useDesktopAppNotificationPermission} from 'components/common/hooks/use_desktop_notification_permission';
|
||||
import Tag from 'components/widgets/tag/tag';
|
||||
|
||||
import {
|
||||
@@ -16,6 +17,8 @@ import {
|
||||
export default function NotificationPermissionTitleTag() {
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
const [desktopNotificationPermission] = useDesktopAppNotificationPermission();
|
||||
|
||||
if (!isNotificationAPISupported()) {
|
||||
return (
|
||||
<Tag
|
||||
@@ -32,7 +35,8 @@ export default function NotificationPermissionTitleTag() {
|
||||
|
||||
if (
|
||||
getNotificationPermission() === NotificationPermissionNeverGranted ||
|
||||
getNotificationPermission() === NotificationPermissionDenied
|
||||
getNotificationPermission() === NotificationPermissionDenied ||
|
||||
desktopNotificationPermission === NotificationPermissionDenied
|
||||
) {
|
||||
return (
|
||||
<Tag
|
||||
|
||||
@@ -5723,8 +5723,14 @@
|
||||
"user.settings.notifications.desktopAndMobile.nothing": "Nothing",
|
||||
"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.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.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.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.",
|
||||
|
||||
Ссылка в новой задаче
Block a user