summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabe Kangas <gabek@real-ity.com>2023-05-21 14:12:14 -0700
committerGabe Kangas <gabek@real-ity.com>2023-06-05 21:01:51 -0700
commit447ab10738fe09ac77410ab36a84e8312143ae09 (patch)
tree1a6f2932d6c92418a1b5eab1a008b1d2c85138f5
parent9d5482adf677926918725b30208e4aa3b8374ed1 (diff)
feat: add ios specific push notification instructionsgek/ios-browser-notifications
Closes #2992
-rw-r--r--web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx42
-rw-r--r--web/components/ui/Content/Content.tsx8
-rw-r--r--web/utils/browserPushNotifications.ts16
-rw-r--r--web/utils/helpers.js31
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>&ldquo;Add to Home Screen&rdquo;</strong> <PlusSquareOutlined />
+ .
+ </li>
+ <li>
+ Tap <strong>&ldquo;Add&rdquo;</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>&ldquo;Allow&rdquo;</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;
+};