diff options
author | Gabe Kangas <gabek@real-ity.com> | 2023-05-21 14:12:14 -0700 |
---|---|---|
committer | Gabe Kangas <gabek@real-ity.com> | 2023-06-05 21:01:51 -0700 |
commit | 447ab10738fe09ac77410ab36a84e8312143ae09 (patch) | |
tree | 1a6f2932d6c92418a1b5eab1a008b1d2c85138f5 | |
parent | 9d5482adf677926918725b30208e4aa3b8374ed1 (diff) |
feat: add ios specific push notification instructionsgek/ios-browser-notifications
Closes #2992
-rw-r--r-- | web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx | 42 | ||||
-rw-r--r-- | web/components/ui/Content/Content.tsx | 8 | ||||
-rw-r--r-- | web/utils/browserPushNotifications.ts | 16 | ||||
-rw-r--r-- | web/utils/helpers.js | 31 |
4 files changed, 88 insertions, 9 deletions
diff --git a/web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx b/web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx index 90d50f113..4c7eef07c 100644 --- a/web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx +++ b/web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx @@ -1,5 +1,7 @@ import { Row, Spin, Typography, Button } from 'antd'; 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 { ErrorBoundary } from 'react-error-boundary'; import { accessTokenAtom, clientConfigStateAtom } from '../../stores/ClientConfigStore'; @@ -8,9 +10,11 @@ import { saveNotificationRegistration, } from '../../../services/notifications-service'; import styles from './BrowserNotifyModal.module.scss'; -import isPushNotificationSupported from '../../../utils/browserPushNotifications'; import { ComponentError } from '../../ui/ComponentError/ComponentError'; +import { isMobileSafariHomeScreenApp, isMobileSafariIos } from '../../../utils/helpers'; +import { arePushNotificationSupported } from '../../../utils/browserPushNotifications'; + const { Title } = Typography; const NotificationsNotSupported = () => ( @@ -21,6 +25,31 @@ const NotificationsNotSupportedLocal = () => ( <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 = { start: () => void; }; @@ -78,22 +107,27 @@ export const BrowserNotifyModal = () => { const [browserPushPermissionsPending, setBrowserPushPermissionsPending] = useState<boolean>(false); const notificationsPermitted = - isPushNotificationSupported() && Notification.permission !== 'default'; + arePushNotificationSupported() && Notification.permission !== 'default'; const { notifications } = config; const { browser } = notifications; 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 (notificationsPermitted) { return <NotificationsEnabled />; } + if (isMobileSafariIos() && !isMobileSafariHomeScreenApp()) { + return <MobileSafariInstructions />; + } + const startBrowserPushRegistration = async () => { // If notification permissions are already denied or granted, don't do anything. - if (isPushNotificationSupported() && Notification.permission !== 'default') { + if (arePushNotificationSupported() && Notification.permission !== 'default') { return; } diff --git a/web/components/ui/Content/Content.tsx b/web/components/ui/Content/Content.tsx index 5bb6e5eaa..e26df480b 100644 --- a/web/components/ui/Content/Content.tsx +++ b/web/components/ui/Content/Content.tsx @@ -6,7 +6,7 @@ import dynamic from 'next/dynamic'; import classnames from 'classnames'; import ActionButtons from './ActionButtons'; import { LOCAL_STORAGE_KEYS, getLocalStorage, setLocalStorage } from '../../../utils/localStorage'; -import isPushNotificationSupported from '../../../utils/browserPushNotifications'; +import { canPushNotificationsBeSupported } from '../../../utils/browserPushNotifications'; import { clientConfigStateAtom, @@ -179,7 +179,9 @@ export const Content: FC = () => { useEffect(() => { // isPushNotificationSupported relies on `navigator` so that needs to be // fired from this useEffect. - setSupportsBrowserNotifications(isPushNotificationSupported() && browserNotificationsEnabled); + setSupportsBrowserNotifications( + canPushNotificationsBeSupported() && browserNotificationsEnabled, + ); }, [browserNotificationsEnabled]); const showChat = isChatAvailable && !chatDisabled && isChatVisible; @@ -238,7 +240,7 @@ export const Content: FC = () => { <Col span={24} style={{ paddingRight: dynamicPadding }}> <ActionButtons supportFediverseFeatures={supportFediverseFeatures} - supportsBrowserNotifications={supportsBrowserNotifications} + supportsBrowserNotifications showNotifyReminder={showNotifyReminder} setShowNotifyModal={setShowNotifyModal} disableNotifyReminderPopup={disableNotifyReminderPopup} diff --git a/web/utils/browserPushNotifications.ts b/web/utils/browserPushNotifications.ts index 20f22804a..f13c54d91 100644 --- a/web/utils/browserPushNotifications.ts +++ b/web/utils/browserPushNotifications.ts @@ -1,3 +1,15 @@ -export default function isPushNotificationSupported() { - return 'serviceWorker' in navigator && 'PushManager' in window; +import { isMobileSafariIos } from './helpers'; + +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; } diff --git a/web/utils/helpers.js b/web/utils/helpers.js index bc89c25be..c7e504d5b 100644 --- a/web/utils/helpers.js +++ b/web/utils/helpers.js @@ -1,3 +1,5 @@ +import UAParser from 'ua-parser-js'; + export function pluralize(string, count) { if (count === 1) { return string; @@ -20,3 +22,32 @@ export function mergeMeta(meta) { 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; +}; |