mirror of
https://github.com/owncast/owncast.git
synced 2024-11-26 23:24:29 +03:00
Merge branch 'gek/ios-browser-notifications' into develop
This commit is contained in:
commit
d7ed23e153
4 changed files with 88 additions and 9 deletions
|
@ -1,5 +1,7 @@
|
||||||
import { Row, Spin, Typography, Button } from 'antd';
|
import { Row, Spin, Typography, Button } from 'antd';
|
||||||
import React, { FC, useState } from 'react';
|
import React, { FC, useState } from 'react';
|
||||||
|
import UploadOutlined from '@ant-design/icons/lib/icons/UploadOutlined';
|
||||||
|
import PlusSquareOutlined from '@ant-design/icons/lib/icons/PlusSquareOutlined';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { ErrorBoundary } from 'react-error-boundary';
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { accessTokenAtom, clientConfigStateAtom } from '../../stores/ClientConfigStore';
|
import { accessTokenAtom, clientConfigStateAtom } from '../../stores/ClientConfigStore';
|
||||||
|
@ -8,9 +10,11 @@ import {
|
||||||
saveNotificationRegistration,
|
saveNotificationRegistration,
|
||||||
} from '../../../services/notifications-service';
|
} from '../../../services/notifications-service';
|
||||||
import styles from './BrowserNotifyModal.module.scss';
|
import styles from './BrowserNotifyModal.module.scss';
|
||||||
import isPushNotificationSupported from '../../../utils/browserPushNotifications';
|
|
||||||
import { ComponentError } from '../../ui/ComponentError/ComponentError';
|
import { ComponentError } from '../../ui/ComponentError/ComponentError';
|
||||||
|
|
||||||
|
import { isMobileSafariHomeScreenApp, isMobileSafariIos } from '../../../utils/helpers';
|
||||||
|
import { arePushNotificationSupported } from '../../../utils/browserPushNotifications';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
const NotificationsNotSupported = () => (
|
const NotificationsNotSupported = () => (
|
||||||
|
@ -21,6 +25,31 @@ const NotificationsNotSupportedLocal = () => (
|
||||||
<div>Browser notifications are not supported for local servers.</div>
|
<div>Browser notifications are not supported for local servers.</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MobileSafariInstructions = () => (
|
||||||
|
<div>
|
||||||
|
<Title level={3}>Get notified on iOS</Title>
|
||||||
|
It takes a couple extra steps to make sure you get notified when your favorite streams go live.
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
Tap the <strong>share</strong> button <UploadOutlined /> in Safari.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Scroll down and tap <strong>“Add to Home Screen”</strong> <PlusSquareOutlined />
|
||||||
|
.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Tap <strong>“Add”</strong>.
|
||||||
|
</li>
|
||||||
|
<li>Give this link a name and tap the new icon on your home screen</li>
|
||||||
|
|
||||||
|
<li>Come back to this screen and enable notifications.</li>
|
||||||
|
<li>
|
||||||
|
Tap <strong>“Allow”</strong> when prompted.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
export type PermissionPopupPreviewProps = {
|
export type PermissionPopupPreviewProps = {
|
||||||
start: () => void;
|
start: () => void;
|
||||||
};
|
};
|
||||||
|
@ -78,22 +107,27 @@ export const BrowserNotifyModal = () => {
|
||||||
const [browserPushPermissionsPending, setBrowserPushPermissionsPending] =
|
const [browserPushPermissionsPending, setBrowserPushPermissionsPending] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const notificationsPermitted =
|
const notificationsPermitted =
|
||||||
isPushNotificationSupported() && Notification.permission !== 'default';
|
arePushNotificationSupported() && Notification.permission !== 'default';
|
||||||
|
|
||||||
const { notifications } = config;
|
const { notifications } = config;
|
||||||
const { browser } = notifications;
|
const { browser } = notifications;
|
||||||
const { publicKey } = browser;
|
const { publicKey } = browser;
|
||||||
|
|
||||||
const browserPushSupported = browser.enabled && isPushNotificationSupported();
|
const browserPushSupported =
|
||||||
|
browser.enabled && (arePushNotificationSupported() || isMobileSafariHomeScreenApp());
|
||||||
|
|
||||||
// If notification permissions are granted, show user info how to disable them
|
// If notification permissions are granted, show user info how to disable them
|
||||||
if (notificationsPermitted) {
|
if (notificationsPermitted) {
|
||||||
return <NotificationsEnabled />;
|
return <NotificationsEnabled />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isMobileSafariIos() && !isMobileSafariHomeScreenApp()) {
|
||||||
|
return <MobileSafariInstructions />;
|
||||||
|
}
|
||||||
|
|
||||||
const startBrowserPushRegistration = async () => {
|
const startBrowserPushRegistration = async () => {
|
||||||
// If notification permissions are already denied or granted, don't do anything.
|
// If notification permissions are already denied or granted, don't do anything.
|
||||||
if (isPushNotificationSupported() && Notification.permission !== 'default') {
|
if (arePushNotificationSupported() && Notification.permission !== 'default') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import dynamic from 'next/dynamic';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import ActionButtons from './ActionButtons';
|
import ActionButtons from './ActionButtons';
|
||||||
import { LOCAL_STORAGE_KEYS, getLocalStorage, setLocalStorage } from '../../../utils/localStorage';
|
import { LOCAL_STORAGE_KEYS, getLocalStorage, setLocalStorage } from '../../../utils/localStorage';
|
||||||
import isPushNotificationSupported from '../../../utils/browserPushNotifications';
|
import { canPushNotificationsBeSupported } from '../../../utils/browserPushNotifications';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
clientConfigStateAtom,
|
clientConfigStateAtom,
|
||||||
|
@ -179,7 +179,9 @@ export const Content: FC = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// isPushNotificationSupported relies on `navigator` so that needs to be
|
// isPushNotificationSupported relies on `navigator` so that needs to be
|
||||||
// fired from this useEffect.
|
// fired from this useEffect.
|
||||||
setSupportsBrowserNotifications(isPushNotificationSupported() && browserNotificationsEnabled);
|
setSupportsBrowserNotifications(
|
||||||
|
canPushNotificationsBeSupported() && browserNotificationsEnabled,
|
||||||
|
);
|
||||||
}, [browserNotificationsEnabled]);
|
}, [browserNotificationsEnabled]);
|
||||||
|
|
||||||
const showChat = isChatAvailable && !chatDisabled && isChatVisible;
|
const showChat = isChatAvailable && !chatDisabled && isChatVisible;
|
||||||
|
@ -238,7 +240,7 @@ export const Content: FC = () => {
|
||||||
<Col span={24} style={{ paddingRight: dynamicPadding }}>
|
<Col span={24} style={{ paddingRight: dynamicPadding }}>
|
||||||
<ActionButtons
|
<ActionButtons
|
||||||
supportFediverseFeatures={supportFediverseFeatures}
|
supportFediverseFeatures={supportFediverseFeatures}
|
||||||
supportsBrowserNotifications={supportsBrowserNotifications}
|
supportsBrowserNotifications
|
||||||
showNotifyReminder={showNotifyReminder}
|
showNotifyReminder={showNotifyReminder}
|
||||||
setShowNotifyModal={setShowNotifyModal}
|
setShowNotifyModal={setShowNotifyModal}
|
||||||
disableNotifyReminderPopup={disableNotifyReminderPopup}
|
disableNotifyReminderPopup={disableNotifyReminderPopup}
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
export default function isPushNotificationSupported() {
|
import { isMobileSafariIos } from './helpers';
|
||||||
return 'serviceWorker' in navigator && 'PushManager' in window;
|
|
||||||
|
export function arePushNotificationSupported() {
|
||||||
|
return 'Notification' in window && 'serviceWorker' in navigator && 'PushManager' in window;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canPushNotificationsBeSupported() {
|
||||||
|
// Mobile safari will return false for supporting push notifications, but
|
||||||
|
// it does support them. So we need to check for mobile safari and return
|
||||||
|
// true if it is.
|
||||||
|
if (isMobileSafariIos()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return 'Notification' in window && 'serviceWorker' in navigator && 'PushManager' in window;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import UAParser from 'ua-parser-js';
|
||||||
|
|
||||||
export function pluralize(string, count) {
|
export function pluralize(string, count) {
|
||||||
if (count === 1) {
|
if (count === 1) {
|
||||||
return string;
|
return string;
|
||||||
|
@ -20,3 +22,32 @@ export function mergeMeta(meta) {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isMobileSafariIos = () => {
|
||||||
|
try {
|
||||||
|
const ua = navigator.userAgent;
|
||||||
|
const uaParser = new UAParser(ua);
|
||||||
|
const browser = uaParser.getBrowser();
|
||||||
|
const device = uaParser.getDevice();
|
||||||
|
|
||||||
|
if (device.vendor !== 'Apple') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device.type !== 'mobile' && device.type !== 'tablet') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return browser.name === 'Mobile Safari' || browser.name === 'Safari';
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isMobileSafariHomeScreenApp = () => {
|
||||||
|
if (!isMobileSafariIos()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'standalone' in window.navigator && window.navigator.standalone;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue