[MM-59296] Can't open web client on iOS Safari (#27607)

Этот коммит содержится в:
M-ZubairAhmed
2024-07-18 10:39:16 +00:00
коммит произвёл GitHub
родитель db138fd23a
Коммит 1ff54a31bc
5 изменённых файлов: 173 добавлений и 22 удалений

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

@@ -0,0 +1,78 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {screen, waitFor} from '@testing-library/react';
import React from 'react';
import {renderWithContext, userEvent} from 'tests/react_testing_utils';
import {requestNotificationPermission, isNotificationAPISupported} from 'utils/notifications';
import NotificationPermissionBar from './index';
jest.mock('utils/notifications', () => ({
requestNotificationPermission: jest.fn(),
isNotificationAPISupported: jest.fn(),
}));
describe('NotificationPermissionBar', () => {
const initialState = {
entities: {
users: {
currentUserId: 'user-id',
},
general: {
config: {},
license: {},
},
},
};
beforeEach(() => {
(isNotificationAPISupported as jest.Mock).mockReturnValue(true);
(window as any).Notification = {permission: 'default'};
});
afterEach(() => {
jest.clearAllMocks();
delete (window as any).Notification;
});
test('should render the notification bar when conditions are met', () => {
renderWithContext(<NotificationPermissionBar/>, initialState);
expect(screen.getByText('We need your permission to show desktop notifications.')).toBeInTheDocument();
expect(screen.getByText('Enable notifications')).toBeInTheDocument();
});
test('should not render the notification bar if user is not logged in', () => {
renderWithContext(<NotificationPermissionBar/>);
expect(screen.queryByText('We need your permission to show desktop notifications.')).not.toBeInTheDocument();
expect(screen.queryByText('Enable notifications')).not.toBeInTheDocument();
});
test('should not render the notification bar if Notifications are not supported', () => {
delete (window as any).Notification;
(isNotificationAPISupported as jest.Mock).mockReturnValue(false);
renderWithContext(<NotificationPermissionBar/>, initialState);
expect(screen.queryByText('We need your permission to show desktop notifications.')).not.toBeInTheDocument();
expect(screen.queryByText('Enable notifications')).not.toBeInTheDocument();
});
test('should call requestNotificationPermission and hide the bar when the button is clicked', async () => {
(requestNotificationPermission as jest.Mock).mockResolvedValue('granted');
renderWithContext(<NotificationPermissionBar/>, initialState);
expect(screen.getByText('We need your permission to show desktop notifications.')).toBeInTheDocument();
await waitFor(async () => {
userEvent.click(screen.getByText('Enable notifications'));
});
expect(requestNotificationPermission).toHaveBeenCalled();
expect(screen.queryByText('We need your permission to show desktop notifications.')).not.toBeInTheDocument();
});
});

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

@@ -10,25 +10,25 @@ import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import AnnouncementBar from 'components/announcement_bar/default_announcement_bar'; import AnnouncementBar from 'components/announcement_bar/default_announcement_bar';
import {AnnouncementBarTypes} from 'utils/constants'; import {AnnouncementBarTypes} from 'utils/constants';
import {requestNotificationPermission, isNotificationAPISupported} from 'utils/notifications';
export default function NotificationPermissionBar() { export default function NotificationPermissionBar() {
const isLoggedIn = Boolean(useSelector(getCurrentUserId)); const isLoggedIn = Boolean(useSelector(getCurrentUserId));
const [show, setShow] = useState(Notification.permission === 'default'); const [show, setShow] = useState(isNotificationAPISupported() ? Notification.permission === 'default' : false);
const handleClick = useCallback(async () => {
await requestNotificationPermission();
setShow(false);
}, []);
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
// If the user closes the bar, don't show the notification bar any more for the rest of the session, but // If the user closes the bar, don't show the notification bar any more for the rest of the session, but
// show it again after refresh. // show it again on app refresh.
setShow(false); setShow(false);
}, []); }, []);
const handleClick = useCallback(() => { if (!show || !isLoggedIn || !isNotificationAPISupported()) {
Notification.requestPermission().then(() => {
setShow(false);
});
}, []);
if (!show || !isLoggedIn) {
return null; return null;
} }
@@ -39,7 +39,7 @@ export default function NotificationPermissionBar() {
type={AnnouncementBarTypes.ANNOUNCEMENT} type={AnnouncementBarTypes.ANNOUNCEMENT}
message={ message={
<FormattedMessage <FormattedMessage
id='announcement_bar.notification.needs_permisson' id='announcement_bar.notification.needs_permission'
defaultMessage='We need your permission to show desktop notifications.' defaultMessage='We need your permission to show desktop notifications.'
/> />
} }

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

@@ -2814,7 +2814,7 @@
"announcement_bar.error.trial_license_expiring_last_day.short": "This is the last day of your free trial.", "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.email_verified": "Email verified",
"announcement_bar.notification.enable_notifications": "Enable notifications", "announcement_bar.notification.enable_notifications": "Enable notifications",
"announcement_bar.notification.needs_permisson": "We need your permission to show desktop notifications.", "announcement_bar.notification.needs_permission": "We need your permission to show desktop notifications.",
"announcement_bar.warn.contact_support_text": "To renew your license, contact support at support@mattermost.com.", "announcement_bar.warn.contact_support_text": "To renew your license, contact support at support@mattermost.com.",
"announcement_bar.warn.email_support": "[Contact support](!{email}).", "announcement_bar.warn.email_support": "[Contact support](!{email}).",
"announcement_bar.warn.no_internet_connection": "Looks like you do not have access to the internet.", "announcement_bar.warn.no_internet_connection": "Looks like you do not have access to the internet.",

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

@@ -3,11 +3,10 @@
/* eslint-disable global-require */ /* eslint-disable global-require */
// to enable being a typescript file
export const a = '';
import configureStore from 'tests/test_store'; import configureStore from 'tests/test_store';
import type {showNotification} from './notifications'; import type {showNotification} from './notifications';
import {isNotificationAPISupported, requestNotificationPermission} from './notifications';
declare global { declare global {
interface Window { interface Window {
@@ -39,20 +38,20 @@ describe('Notifications.showNotification', () => {
it('should throw an exception if Notification is not defined on window', async () => { it('should throw an exception if Notification is not defined on window', async () => {
delete window.Notification; delete window.Notification;
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification not supported'); await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification API is not supported');
}); });
it('should throw an exception if Notification.requestPermission is not defined', async () => { it('should throw an exception if Notification.requestPermission is not defined', async () => {
window.Notification = jest.fn(); window.Notification = jest.fn();
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification.requestPermission not supported'); await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification API is not supported');
}); });
it('should throw an exception if Notification.requestPermission is not a function', async () => { it('should throw an exception if Notification.requestPermission is not a function', async () => {
window.Notification = jest.fn(); window.Notification = jest.fn();
window.Notification.requestPermission = true; window.Notification.requestPermission = true;
await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification.requestPermission not supported'); await expect(store.dispatch(Notifications.showNotification())).rejects.toThrow('Notification API is not supported');
expect(window.Notification).not.toHaveBeenCalled(); expect(window.Notification).not.toHaveBeenCalled();
}); });
@@ -241,3 +240,64 @@ describe('Notifications.showNotification', () => {
expect(window.Notification.requestPermission).toHaveBeenCalledTimes(0); expect(window.Notification.requestPermission).toHaveBeenCalledTimes(0);
}); });
}); });
describe('Notifications.isNotificationAPISupported', () => {
beforeEach(() => {
window.Notification = {
requestPermission: jest.fn(),
};
});
afterEach(() => {
delete (window as any).Notification;
});
it('should return true if Notification is supported', () => {
expect(isNotificationAPISupported()).toBe(true);
});
it('should return false if Notification is not supported', () => {
delete (window as any).Notification;
expect(isNotificationAPISupported()).toBe(false);
});
it('should return false if requestPermission is not a function', () => {
(window as any).Notification = {};
expect(isNotificationAPISupported()).toBe(false);
});
});
describe('Notifications.requestNotificationPermission', () => {
beforeEach(() => {
(window as any).Notification = {
requestPermission: jest.fn(),
};
});
afterEach(() => {
delete (window as any).Notification;
});
it('should return the permission if Notification.requestPermission resolves', async () => {
(window as any).Notification.requestPermission = jest.fn().mockResolvedValue('granted');
const permission = await requestNotificationPermission();
expect(permission).toBe('granted');
});
it('should return null if Notification is not supported', async () => {
delete (window as any).Notification;
const permission = await requestNotificationPermission();
expect(permission).toBeNull();
});
it('should return null if requestPermission throws an error', async () => {
(window as any).Notification.requestPermission = jest.fn().mockRejectedValue('some error');
const permission = await requestNotificationPermission();
expect(permission).toBeNull();
});
});

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

@@ -50,12 +50,8 @@ export function showNotification(
icon = iconWS; icon = iconWS;
} }
if (!('Notification' in window)) { if (!isNotificationAPISupported()) {
throw new Error('Notification not supported'); throw new Error('Notification API is not supported');
}
if (typeof Notification.requestPermission !== 'function') {
throw new Error('Notification.requestPermission not supported');
} }
if (Notification.permission !== 'granted') { if (Notification.permission !== 'granted') {
@@ -104,3 +100,20 @@ export function showNotification(
}; };
}; };
} }
export function isNotificationAPISupported() {
return ('Notification' in window) && (typeof Notification.requestPermission === 'function');
}
export async function requestNotificationPermission() {
if (!isNotificationAPISupported()) {
return null;
}
try {
const notificationPermission = await Notification.requestPermission();
return notificationPermission;
} catch (error) {
return null;
}
}