diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.test.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.test.tsx
index f0ce194938..88aff7cc7c 100644
--- a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.test.tsx
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.test.tsx
@@ -17,6 +17,9 @@ import DesktopNotificationSettings, {
const validNotificationLevels = Object.values(NotificationLevels);
+jest.mock('components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice', () => () =>
);
+jest.mock('components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag', () => () =>
);
+
describe('DesktopNotificationSettings', () => {
const baseProps: Props = {
active: true,
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.tsx
index 6001f59d4b..edea0bf5b0 100644
--- a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.tsx
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/index.tsx
@@ -12,6 +12,8 @@ import type {UserNotifyProps} from '@mattermost/types/users';
import SettingItemMax from 'components/setting_item_max';
import SettingItemMin from 'components/setting_item_min';
import type SettingItemMinComponent from 'components/setting_item_min';
+import NotificationPermissionSectionNotice from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice';
+import NotificationPermissionTitleTag from 'components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag';
import Constants, {NotificationLevels, UserSettingsNotificationSections} from 'utils/constants';
@@ -322,6 +324,7 @@ function DesktopAndMobileNotificationSettings({
saving={saving}
serverError={error}
updateSection={handleChangeForMaxSection}
+ extraContentBeforeSettingList={
}
/>
);
}
@@ -330,10 +333,13 @@ function DesktopAndMobileNotificationSettings({
+ <>
+
+
+ >
}
describe={getCollapsedText(desktopActivity, pushActivity)}
section={UserSettingsNotificationSections.DESKTOP_AND_MOBILE}
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/index.test.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/index.test.tsx
new file mode 100644
index 0000000000..d5c1622e3d
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/index.test.tsx
@@ -0,0 +1,50 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import {renderWithContext, screen} from 'tests/react_testing_utils';
+import * as utilsNotifications from 'utils/notifications';
+
+import NotificationPermissionSectionNotice from './index';
+
+describe('NotificationPermissionSectionNotice', () => {
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ test('should render "Unsupported" notice when notifications are not supported', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(false);
+
+ renderWithContext(
);
+
+ expect(screen.getByText('Browser notifications unsupported')).toBeInTheDocument();
+ });
+
+ test('should render "Never granted" notice when notifications are never granted', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
+ jest.spyOn(utilsNotifications, 'getNotificationPermission').mockReturnValue('default');
+
+ renderWithContext(
);
+
+ expect(screen.getByText('Browser notifications are disabled')).toBeInTheDocument();
+ });
+
+ test('should render "Denied" notice when notifications are denied', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
+ jest.spyOn(utilsNotifications, 'getNotificationPermission').mockReturnValue('denied');
+
+ renderWithContext(
);
+
+ expect(screen.getByText('Browser notification permission was denied')).toBeInTheDocument();
+ });
+
+ test('should render nothing when notifications are granted', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
+ jest.spyOn(utilsNotifications, 'getNotificationPermission').mockReturnValue('granted');
+
+ const {container} = renderWithContext(
);
+
+ expect(container).toBeEmptyDOMElement();
+ });
+});
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/index.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/index.tsx
new file mode 100644
index 0000000000..bb7883000f
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/index.tsx
@@ -0,0 +1,35 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useState} from 'react';
+
+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';
+
+export default function NotificationPermissionSectionNotice() {
+ const isNotificationSupported = isNotificationAPISupported();
+
+ const [notificationPermission, setNotificationPermission] = useState(getNotificationPermission());
+
+ function handleRequestNotificationClicked(permission: NotificationPermission) {
+ setNotificationPermission(permission);
+ }
+
+ if (!isNotificationSupported) {
+ return
;
+ }
+
+ if (isNotificationSupported && notificationPermission === NotificationPermissionNeverGranted) {
+ return
;
+ }
+
+ if (isNotificationSupported && notificationPermission === NotificationPermissionDenied) {
+ return ;
+ }
+
+ return null;
+}
+
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_denied_section_notice.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_denied_section_notice.tsx
new file mode 100644
index 0000000000..654dd8ab58
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_denied_section_notice.tsx
@@ -0,0 +1,38 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useCallback} from 'react';
+import {useIntl} from 'react-intl';
+
+import SectionNotice from 'components/section_notice';
+
+export default function NotificationPermissionDeniedSectionNotice() {
+ const intl = useIntl();
+
+ const handleClick = useCallback(() => {
+ window.open('https://mattermost.com/pl/manage-notifications', '_blank', 'noopener,noreferrer');
+ }, []);
+
+ return (
+
+
+
+ );
+}
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_never_granted_section_notice.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_never_granted_section_notice.tsx
new file mode 100644
index 0000000000..e3e5a43bd4
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_never_granted_section_notice.tsx
@@ -0,0 +1,48 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useCallback} from 'react';
+import {useIntl} from 'react-intl';
+
+import SectionNotice from 'components/section_notice';
+
+import {requestNotificationPermission} from 'utils/notifications';
+
+type Props = {
+ onCtaButtonClick: (permission: NotificationPermission) => void;
+}
+
+export default function NotificationPermissionNeverGrantedSectionNotice(props: Props) {
+ const intl = useIntl();
+
+ const handleClick = useCallback(async () => {
+ const permission = await requestNotificationPermission();
+ if (permission) {
+ props.onCtaButtonClick(permission);
+ }
+ }, [props.onCtaButtonClick]);
+
+ return (
+
+
+
+ );
+}
+
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_unsupported_section_notice.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_unsupported_section_notice.tsx
new file mode 100644
index 0000000000..b645b18b80
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice/notification_permission_unsupported_section_notice.tsx
@@ -0,0 +1,39 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React, {useCallback} from 'react';
+import {useIntl} from 'react-intl';
+
+import SectionNotice from 'components/section_notice';
+
+export default function NotificationPermissionUnsupportedSectionNotice() {
+ const intl = useIntl();
+
+ const handleClick = useCallback(async () => {
+ window.open('https://mattermost.com/pl/pc-web-requirements', '_blank', 'noopener,noreferrer');
+ }, []);
+
+ return (
+
+
+
+ );
+}
+
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag/index.test.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag/index.test.tsx
new file mode 100644
index 0000000000..183919cde0
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag/index.test.tsx
@@ -0,0 +1,50 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+
+import {renderWithContext, screen} from 'tests/react_testing_utils';
+import * as utilsNotifications from 'utils/notifications';
+
+import NotificationPermissionTitleTag from './index';
+
+describe('NotificationPermissionTitleTag', () => {
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ test('should render "Not supported" tag when notifications are not supported', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(false);
+
+ renderWithContext();
+
+ expect(screen.queryByText('Not supported')).toBeInTheDocument();
+ });
+
+ test('should render "Permission required" tag when permission is never granted yet', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
+ jest.spyOn(utilsNotifications, 'getNotificationPermission').mockReturnValue(utilsNotifications.NotificationPermissionNeverGranted);
+
+ renderWithContext();
+
+ expect(screen.queryByText('Permission required')).toBeInTheDocument();
+ });
+
+ test('should render "Permission required" tag when permission is denied', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
+ jest.spyOn(utilsNotifications, 'getNotificationPermission').mockReturnValue(utilsNotifications.NotificationPermissionDenied);
+
+ renderWithContext();
+
+ expect(screen.queryByText('Permission required')).toBeInTheDocument();
+ });
+
+ test('should render nothing when permission is granted', () => {
+ jest.spyOn(utilsNotifications, 'isNotificationAPISupported').mockReturnValue(true);
+ jest.spyOn(utilsNotifications, 'getNotificationPermission').mockReturnValue(utilsNotifications.NotificationPermissionGranted);
+
+ const {container} = renderWithContext();
+
+ expect(container).toBeEmptyDOMElement();
+ });
+});
diff --git a/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag/index.tsx b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag/index.tsx
new file mode 100644
index 0000000000..dcc9083023
--- /dev/null
+++ b/webapp/channels/src/components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag/index.tsx
@@ -0,0 +1,51 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {useIntl} from 'react-intl';
+
+import Tag from 'components/widgets/tag/tag';
+
+import {
+ getNotificationPermission,
+ isNotificationAPISupported,
+ NotificationPermissionDenied,
+ NotificationPermissionNeverGranted,
+} from 'utils/notifications';
+
+export default function NotificationPermissionTitleTag() {
+ const {formatMessage} = useIntl();
+
+ if (!isNotificationAPISupported()) {
+ return (
+
+ );
+ }
+
+ if (
+ getNotificationPermission() === NotificationPermissionNeverGranted ||
+ getNotificationPermission() === NotificationPermissionDenied
+ ) {
+ return (
+
+ );
+ }
+
+ return null;
+}
diff --git a/webapp/channels/src/components/user_settings/notifications/user_settings_notifications.test.tsx b/webapp/channels/src/components/user_settings/notifications/user_settings_notifications.test.tsx
index d779f530b5..5acca09eea 100644
--- a/webapp/channels/src/components/user_settings/notifications/user_settings_notifications.test.tsx
+++ b/webapp/channels/src/components/user_settings/notifications/user_settings_notifications.test.tsx
@@ -10,6 +10,9 @@ import {TestHelper} from 'utils/test_helper';
import UserSettingsNotifications, {areDesktopAndMobileSettingsDifferent} from './user_settings_notifications';
+jest.mock('components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_section_notice', () => () => );
+jest.mock('components/user_settings/notifications/desktop_and_mobile_notification_setting/notification_permission_title_tag', () => () => );
+
describe('components/user_settings/display/UserSettingsDisplay', () => {
const defaultProps = {
user: TestHelper.getUserMock({id: 'user_id'}),
diff --git a/webapp/channels/src/components/widgets/tag/tag.tsx b/webapp/channels/src/components/widgets/tag/tag.tsx
index d55d18cea7..dd9faecee4 100644
--- a/webapp/channels/src/components/widgets/tag/tag.tsx
+++ b/webapp/channels/src/components/widgets/tag/tag.tsx
@@ -9,7 +9,7 @@ import styled, {css} from 'styled-components';
import glyphMap from '@mattermost/compass-icons/components';
import type {IconGlyphTypes} from '@mattermost/compass-icons/IconGlyphs';
-export type TagVariant = 'info' | 'success' | 'warning' | 'danger';
+export type TagVariant = 'info' | 'success' | 'warning' | 'danger' | 'dangerDim';
export type TagSize = 'xs' | 'sm' | 'md' | 'lg'
@@ -26,10 +26,6 @@ type Props = {
type TagWrapperProps = Required>;
const TagWrapper = styled.div`
- --tag-bg: var(--semantic-color-general);
- --tag-bg-opacity: 0.08;
- --tag-color: var(--semantic-color-general);
-
appearance: none;
display: inline-flex;
@@ -84,38 +80,39 @@ const TagWrapper = styled.div`
padding: 2px 5px;
}
- &.Tag--info,
- &.Tag--success,
- &.Tag--warning,
- &.Tag--danger {
- --tag-bg-opacity: 1;
- --tag-color: 255, 255, 255;
- }
+ background: rgba(var(--semantic-color-general), 0.08);
+ color: rgb(var(--semantic-color-general));
&.Tag--info {
- --tag-bg: var(--semantic-color-info);
+ background: rgba(var(--semantic-color-info), 1);
+ color: rgb(255, 255, 255);
}
&.Tag--success {
- --tag-bg: var(--semantic-color-success);
+ background: rgba(var(--semantic-color-success), 1);
+ color: rgb(255, 255, 255);
}
&.Tag--warning {
- --tag-bg: var(--semantic-color-warning);
+ background: rgba(var(--semantic-color-warning), 1);
+ color: rgb(255, 255, 255);
}
&.Tag--danger {
- --tag-bg: var(--semantic-color-danger);
+ background: rgba(var(--semantic-color-danger), 1);
+ color: rgb(255, 255, 255);
}
- background: rgba(var(--tag-bg), var(--tag-bg-opacity));
- color: rgb(var(--tag-color));
+ &.Tag--dangerDim {
+ background: rgba(var(--semantic-color-danger), 0.08);
+ color: rgb(var(--semantic-color-danger));
+ }
${({onClick}) => typeof onClick === 'function' && (
css`
&:hover,
&:focus {
- background: rgba(var(--tag-bg), 0.08);
+ background: rgba(var(--semantic-color-general), 0.08);
cursor: pointer;
}
`
diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json
index 5a940baf62..9766fc3c04 100644
--- a/webapp/channels/src/i18n/en.json
+++ b/webapp/channels/src/i18n/en.json
@@ -2892,12 +2892,14 @@
"announcement_bar.error.trial_license_expiring_last_day": "This is the last day of your free trial. Purchase a license now to continue using Mattermost Professional and Enterprise features.",
"announcement_bar.error.trial_license_expiring_last_day.short": "This is the last day of your free trial.",
"announcement_bar.notification.email_verified": "Email verified",
- "announcement_bar.notification.enable_notifications": "Enable notifications",
- "announcement_bar.notification.needs_permission": "We need your permission to show desktop notifications.",
"announcement_bar.warn.contact_support_email": "Contact support.",
"announcement_bar.warn.contact_support_text": "To renew your license, contact support at support@mattermost.com.",
"announcement_bar.warn.no_internet_connection": "Looks like you do not have access to the internet.",
"announcement_bar.warn.renew_license_contact_sales": "Contact sales",
+ "announcementBar.notification.permissionNeverGrantedBar.cta": "Enable notifications",
+ "announcementBar.notification.permissionNeverGrantedBar.message": "We need your permission to show notifications in the browser.",
+ "announcementBar.notification.unsupportedBar.cta": "Update your browser",
+ "announcementBar.notification.unsupportedBar.message": "Your browser does not support browser notifications.",
"api.channel.add_guest.added": "{addedUsername} added to the channel as a guest by {username}.",
"api.channel.add_member.added": "{addedUsername} added to the channel by {username}.",
"api.channel.delete_channel.archived": "{username} archived the channel.",
@@ -5661,6 +5663,17 @@
"user.settings.notifications.desktopAndMobile.noneDesktopButMobileMentions": "Never on desktop; mentions, direct messages, and group messages on mobile",
"user.settings.notifications.desktopAndMobile.noneForDesktopAndMobile": "Never",
"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.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.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.",
+ "user.settings.notifications.desktopAndMobile.notificationSection.permissionNeverGranted.title": "Browser notifications are disabled",
+ "user.settings.notifications.desktopAndMobile.notificationSection.permissionUnsupported.button": "Update your browser",
+ "user.settings.notifications.desktopAndMobile.notificationSection.permissionUnsupported.message": "You're missing important message and call notifications from Mattermost. To start receiving notifications, please update to a supported browser.",
+ "user.settings.notifications.desktopAndMobile.notificationSection.permissionUnsupported.title": "Browser notifications unsupported",
"user.settings.notifications.desktopAndMobile.notifyForDesktopthreads": "Notify me about replies to threads I'm following",
"user.settings.notifications.desktopAndMobile.notifyForMobilethreads": "Notify me on mobile about replies to threads I'm following",
"user.settings.notifications.desktopAndMobile.noValidSettings": "Configure desktop and mobile settings",
diff --git a/webapp/channels/src/sass/components/_announcement-bar.scss b/webapp/channels/src/sass/components/_announcement-bar.scss
index 2eabfc4fac..85befaffdc 100644
--- a/webapp/channels/src/sass/components/_announcement-bar.scss
+++ b/webapp/channels/src/sass/components/_announcement-bar.scss
@@ -107,7 +107,7 @@
font-size: 12px;
font-style: normal;
font-weight: 600;
- line-height: 16px;
+ line-height: 14px;
}
}
diff --git a/webapp/channels/src/sass/routes/_settings.scss b/webapp/channels/src/sass/routes/_settings.scss
index 157444a717..1dcd40471c 100644
--- a/webapp/channels/src/sass/routes/_settings.scss
+++ b/webapp/channels/src/sass/routes/_settings.scss
@@ -255,6 +255,8 @@
.section-max {
@include pie-clearfix;
+ display: flex;
+ flex-direction: column;
padding: 12px;
margin-bottom: 0;
background: rgba(var(--center-channel-color-rgb), 0.04);
@@ -267,6 +269,11 @@
line-height: 20px;
}
+ .extraContentBeforeSettingList {
+ width: 100%;
+ padding-block-start: 20px;
+ }
+
.sectionContent {
padding: 20px 0 0 0;
@@ -774,10 +781,13 @@
}
.section-min__title {
+ display: flex;
+ flex-direction: row;
padding-right: 50px;
margin: 0 0 5px;
font-size: 14px;
font-weight: 600;
+ gap: 8px;
line-height: 20px;
&.isDisabled {
diff --git a/webapp/channels/src/utils/notifications.ts b/webapp/channels/src/utils/notifications.ts
index 15095e1a96..a4b922bc27 100644
--- a/webapp/channels/src/utils/notifications.ts
+++ b/webapp/channels/src/utils/notifications.ts
@@ -105,7 +105,15 @@ export function isNotificationAPISupported() {
return ('Notification' in window) && (typeof Notification.requestPermission === 'function');
}
-export async function requestNotificationPermission() {
+export function getNotificationPermission(): NotificationPermission | null {
+ if (!isNotificationAPISupported()) {
+ return null;
+ }
+
+ return Notification.permission;
+}
+
+export async function requestNotificationPermission(): Promise {
if (!isNotificationAPISupported()) {
return null;
}
@@ -117,3 +125,7 @@ export async function requestNotificationPermission() {
return null;
}
}
+
+export const NotificationPermissionNeverGranted = 'default';
+export const NotificationPermissionGranted = 'granted';
+export const NotificationPermissionDenied = 'denied';