Merge pull request #71 from johndoe6345789/codex/centralize-localstorage-handling

Centralize PWA install prompt state handling
This commit is contained in:
2026-01-18 00:58:34 +00:00
committed by GitHub
2 changed files with 42 additions and 26 deletions

View File

@@ -1,4 +1,3 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Download, X, DeviceMobile, Desktop } from '@phosphor-icons/react'
@@ -6,41 +5,26 @@ import { motion, AnimatePresence } from 'framer-motion'
import { usePWA } from '@/hooks/use-pwa'
export function PWAInstallPrompt() {
const { isInstallable, installApp } = usePWA()
const [dismissed, setDismissed] = useState(false)
const [showPrompt, setShowPrompt] = useState(false)
useEffect(() => {
const hasBeenDismissed = localStorage.getItem('pwa-install-dismissed')
if (hasBeenDismissed) {
setDismissed(true)
}
const timer = setTimeout(() => {
if (isInstallable && !hasBeenDismissed) {
setShowPrompt(true)
}
}, 3000)
return () => clearTimeout(timer)
}, [isInstallable])
const {
isInstallable,
isInstallPromptDismissed,
isInstallPromptVisible,
installApp,
dismissInstallPrompt,
} = usePWA()
const handleInstall = async () => {
const success = await installApp()
if (success) {
setShowPrompt(false)
}
if (success) return
}
const handleDismiss = () => {
setShowPrompt(false)
setDismissed(true)
localStorage.setItem('pwa-install-dismissed', 'true')
dismissInstallPrompt()
}
return (
<AnimatePresence>
{isInstallable && !dismissed && showPrompt && (
{isInstallable && !isInstallPromptDismissed && isInstallPromptVisible && (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}

View File

@@ -14,6 +14,8 @@ interface PWAState {
}
export function usePWA() {
const [isInstallPromptDismissed, setInstallPromptDismissed] = useState(false)
const [isInstallPromptVisible, setInstallPromptVisible] = useState(false)
const [state, setState] = useState<PWAState>({
isInstallable: false,
isInstalled: false,
@@ -25,6 +27,10 @@ export function usePWA() {
useEffect(() => {
if (typeof window === 'undefined') return
const storedDismissed = window.localStorage.getItem('pwa-install-dismissed')
if (storedDismissed) {
setInstallPromptDismissed(true)
}
const checkInstalled = () => {
try {
@@ -52,6 +58,7 @@ export function usePWA() {
const handleAppInstalled = () => {
setState(prev => ({ ...prev, isInstalled: true, isInstallable: false }))
setDeferredPrompt(null)
setInstallPromptVisible(false)
}
const handleOnline = () => {
@@ -104,10 +111,24 @@ export function usePWA() {
}
}, [])
useEffect(() => {
if (!state.isInstallable || state.isInstalled || isInstallPromptDismissed) {
setInstallPromptVisible(false)
return
}
const timer = window.setTimeout(() => {
setInstallPromptVisible(true)
}, 3000)
return () => window.clearTimeout(timer)
}, [state.isInstallable, state.isInstalled, isInstallPromptDismissed])
const installApp = async () => {
if (!deferredPrompt) return false
try {
setInstallPromptVisible(false)
await deferredPrompt.prompt()
const choiceResult = await deferredPrompt.userChoice
@@ -123,6 +144,14 @@ export function usePWA() {
}
}
const dismissInstallPrompt = () => {
setInstallPromptDismissed(true)
setInstallPromptVisible(false)
if (typeof window !== 'undefined') {
window.localStorage.setItem('pwa-install-dismissed', 'true')
}
}
const updateApp = () => {
if (state.registration) {
state.registration.waiting?.postMessage({ type: 'SKIP_WAITING' })
@@ -165,7 +194,10 @@ export function usePWA() {
return {
...state,
isInstallPromptDismissed,
isInstallPromptVisible,
installApp,
dismissInstallPrompt,
updateApp,
clearCache,
requestNotificationPermission,