@@ -51,6 +51,29 @@ export type NotificationItem = {
5151 } ;
5252} ;
5353
54+ type BrowserNotificationSupport =
55+ | "checking"
56+ | "available"
57+ | "ios-browser"
58+ | "unsupported" ;
59+
60+ function isAppleMobileBrowser ( ) {
61+ if ( typeof navigator === "undefined" ) return false ;
62+ return (
63+ / i P a d | i P h o n e | i P o d / i. test ( navigator . userAgent ) ||
64+ ( navigator . platform === "MacIntel" && navigator . maxTouchPoints > 1 )
65+ ) ;
66+ }
67+
68+ function isStandaloneWebApp ( ) {
69+ if ( typeof window === "undefined" ) return false ;
70+ const standaloneNavigator = navigator as Navigator & { standalone ?: boolean } ;
71+ return (
72+ window . matchMedia ( "(display-mode: standalone)" ) . matches ||
73+ standaloneNavigator . standalone === true
74+ ) ;
75+ }
76+
5477function formatTimeAgo ( timestamp ?: Timestamp ) : string {
5578 if ( ! timestamp ) return "Just now" ;
5679 const seconds = Math . floor ( ( Date . now ( ) - timestamp . toMillis ( ) ) / 1000 ) ;
@@ -80,12 +103,19 @@ export default function NotificationBell({
80103 const [ filter , setFilter ] = useState < "all" | "unread" > ( "all" ) ;
81104 const popoverRef = useRef < HTMLDivElement > ( null ) ;
82105 const [ browserPermission , setBrowserPermission ] = useState < NotificationPermission > ( "default" ) ;
106+ const [ browserSupport , setBrowserSupport ] =
107+ useState < BrowserNotificationSupport > ( "checking" ) ;
83108 const isInitialSnapshotRef = useRef ( true ) ;
84109
85110 // Check browser notification permission
86111 useEffect ( ( ) => {
87112 if ( typeof window !== "undefined" && "Notification" in window ) {
88113 setBrowserPermission ( Notification . permission ) ;
114+ setBrowserSupport ( "available" ) ;
115+ } else if ( isAppleMobileBrowser ( ) && ! isStandaloneWebApp ( ) ) {
116+ setBrowserSupport ( "ios-browser" ) ;
117+ } else {
118+ setBrowserSupport ( "unsupported" ) ;
89119 }
90120 } , [ ] ) ;
91121
@@ -141,18 +171,37 @@ export default function NotificationBell({
141171 Notification . permission === "granted"
142172 ) {
143173 try {
144- const popup = new Notification ( data . title || "New Notification" , {
174+ const options : NotificationOptions = {
145175 body : data . message || "You received a new notification on L.A.P Tutorials." ,
146- icon : "/favicon.ico " ,
176+ icon : "/icons/android-chrome-192x192.png " ,
147177 tag : change . doc . id ,
148- } ) ;
149- popup . onclick = ( ) => {
150- window . focus ( ) ;
151- if ( data . link ) {
152- router . push ( data . link ) ;
153- }
154- popup . close ( ) ;
178+ data : { url : data . link || "/" } ,
155179 } ;
180+
181+ if ( "serviceWorker" in navigator ) {
182+ void navigator . serviceWorker . ready
183+ . then ( ( registration ) =>
184+ registration . showNotification (
185+ data . title || "New Notification" ,
186+ options ,
187+ ) ,
188+ )
189+ . catch ( ( popupErr ) => {
190+ console . error ( "Error displaying service worker notification:" , popupErr ) ;
191+ } ) ;
192+ } else {
193+ const popup = new Notification (
194+ data . title || "New Notification" ,
195+ options ,
196+ ) ;
197+ popup . onclick = ( ) => {
198+ window . focus ( ) ;
199+ if ( data . link ) {
200+ router . push ( data . link ) ;
201+ }
202+ popup . close ( ) ;
203+ } ;
204+ }
156205 } catch ( popupErr ) {
157206 console . error ( "Error displaying native notification:" , popupErr ) ;
158207 }
@@ -375,20 +424,25 @@ export default function NotificationBell({
375424 </ div >
376425
377426 { /* Browser notification prompt banner */ }
378- { browserPermission === "default" && (
427+ { browserSupport === "available" && browserPermission === "default" && (
379428 < button
380429 type = "button"
381430 onClick = { requestBrowserPermission }
382431 className = "flex w-full items-center justify-between border-b border-white/10 bg-[#8a2ae3]/10 px-4 py-2 text-left text-xs text-[#8a2ae3] transition-colors hover:bg-[#8a2ae3]/20"
383432 >
384433 < span className = "flex items-center gap-1.5 font-medium" >
385- < Bell className = "h-3.5 w-3.5" /> Enable browser pop-ups
434+ < Bell className = "h-3.5 w-3.5" /> Enable notifications
386435 </ span >
387436 < span className = "font-mono text-[10px] font-bold uppercase underline" >
388437 Enable
389438 </ span >
390439 </ button >
391440 ) }
441+ { browserSupport === "ios-browser" && (
442+ < div className = "border-b border-white/10 bg-[#8a2ae3]/10 px-4 py-2 text-xs leading-relaxed text-[#c997ff]" >
443+ Add L.A.P Docs to your iPad Home Screen, open it there, then enable notifications.
444+ </ div >
445+ ) }
392446
393447 { /* Filter Tabs */ }
394448 < div className = "flex border-b border-white/10 text-xs font-mono" >
0 commit comments