From 8a21ffddb569b8a44bfc5680d7ccb8016af8ba6e Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Fri, 27 Mar 2026 16:34:09 -0400 Subject: [PATCH] iOS bug fixes --- frontend/index.html | 3 +++ frontend/src/App.jsx | 46 +++++++++++++++++++++++++++++++++++++ frontend/src/pages/Chat.jsx | 45 ++++-------------------------------- 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 4da8faf..04cc53b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,6 +6,9 @@ + + + RosterChirp diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 19a6276..2ffd661 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import { AuthProvider, useAuth } from './contexts/AuthContext.jsx'; import { SocketProvider } from './contexts/SocketContext.jsx'; @@ -6,6 +7,50 @@ import Login from './pages/Login.jsx'; import Chat from './pages/Chat.jsx'; import ChangePassword from './pages/ChangePassword.jsx'; +// ── iOS "Add to Home Screen" banner ─────────────────────────────────────────── +// iOS Safari does not fire beforeinstallprompt. Push notifications require the +// app to be installed as a PWA. This banner is shown to any iOS Safari user who +// has not yet added the app to their Home Screen. +const IOS_BANNER_KEY = 'rc_ios_install_dismissed'; + +function IOSInstallBanner() { + const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent); + const isStandalone = window.navigator.standalone === true; + const [dismissed, setDismissed] = useState(() => localStorage.getItem(IOS_BANNER_KEY) === '1'); + + if (!isIOS || isStandalone || dismissed) return null; + + const dismiss = () => { + localStorage.setItem(IOS_BANNER_KEY, '1'); + setDismissed(true); + }; + + return ( +
+
+
Add to Home Screen
+
+ To receive push notifications, tap the{' '} + + + + + + {' '}Share button, then select "Add to Home Screen". +
+
+ +
+ ); +} + function ProtectedRoute({ children }) { const { user, loading, mustChangePassword } = useAuth(); if (loading) return ( @@ -36,6 +81,7 @@ export default function App() { return ( + {/* All routes go through jama auth */} -
-
Add to Home Screen
-
- To receive push notifications, tap the{' '} - {/* iOS share icon */} - - - - - - {' '}Share button, then select "Add to Home Screen". -
-
- - - ); -} - export default function Chat() { const { socket } = useSocket(); const { user } = useAuth(); @@ -79,9 +47,6 @@ export default function Chat() { const [helpDismissed, setHelpDismissed] = useState(true); // true until status loaded const [isMobile, setIsMobile] = useState(window.innerWidth < 768); const [showSidebar, setShowSidebar] = useState(true); - const isIOSSafari = /iphone|ipad/i.test(navigator.userAgent) && !window.navigator.standalone; - const [iosBannerDismissed, setIosBannerDismissed] = useState(() => localStorage.getItem(IOS_BANNER_KEY) === '1'); - const showIOSBanner = isIOSSafari && !iosBannerDismissed; // Check if help should be shown on login useEffect(() => { @@ -585,7 +550,7 @@ export default function Chat() { {modal === 'branding' && setModal(null)} />} {modal === 'help' && setModal(null)} dismissed={helpDismissed} />} {modal === 'about' && setModal(null)} />} - {showIOSBanner && { localStorage.setItem(IOS_BANNER_KEY, '1'); setIosBannerDismissed(true); }} />} + ); } @@ -614,7 +579,7 @@ export default function Chat() { {modal === 'branding' && setModal(null)} />} {modal === 'help' && setModal(null)} dismissed={helpDismissed} />} {modal === 'about' && setModal(null)} />} - {showIOSBanner && { localStorage.setItem(IOS_BANNER_KEY, '1'); setIosBannerDismissed(true); }} />} + ); } @@ -674,7 +639,7 @@ export default function Chat() { {modal === 'help' && setModal(null)} dismissed={helpDismissed} />} {modal === 'about' && setModal(null)} />} {modal === 'newchat' && setModal(null)} onCreated={(g) => { loadGroups(); setModal(null); setActiveGroupId(g.id); setPage('chat'); }} />} - {showIOSBanner && { localStorage.setItem(IOS_BANNER_KEY, '1'); setIosBannerDismissed(true); }} />} + ); } @@ -707,7 +672,7 @@ export default function Chat() { {modal === 'branding' && setModal(null)} />} {modal === 'help' && setModal(null)} dismissed={helpDismissed} />} {modal === 'about' && setModal(null)} />} - {showIOSBanner && { localStorage.setItem(IOS_BANNER_KEY, '1'); setIosBannerDismissed(true); }} />} + ); } @@ -752,7 +717,7 @@ export default function Chat() { )} {modal === 'about' && setModal(null)} />} {modal === 'help' && setModal(null)} dismissed={helpDismissed} />} - {showIOSBanner && { localStorage.setItem(IOS_BANNER_KEY, '1'); setIosBannerDismissed(true); }} />} + ); }