feat: add PWA install prompt UI (#35)
Some checks failed
Deploy to Coolify / Code Quality (pull_request) Has been cancelled
Deploy to Coolify / Run Tests (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Development (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Production (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Test (pull_request) Has been cancelled
Pull Request Checks / Validate PR (pull_request) Has been cancelled
Some checks failed
Deploy to Coolify / Code Quality (pull_request) Has been cancelled
Deploy to Coolify / Run Tests (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Development (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Production (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Test (pull_request) Has been cancelled
Pull Request Checks / Validate PR (pull_request) Has been cancelled
- Create usePWAInstall composable for install management - Add InstallPrompt banner component with auto-show after 3s - Add App Settings tab in settings page - Show install button with loading state - Display installation status and instructions - Handle dismissal with 7-day cooldown - Add iOS/Android installation guides - Show PWA features list - Display storage usage with visual progress - Auto-hide prompt after successful install Features: - Automatic install prompt after 3 seconds - Manual install from settings - Platform-specific instructions - Smart dismissal tracking - Storage info visualization Closes #35
This commit is contained in:
93
app/composables/usePWAInstall.ts
Normal file
93
app/composables/usePWAInstall.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Composable to handle PWA installation
|
||||
*
|
||||
* Usage:
|
||||
* const { canInstall, isInstalled, promptInstall, dismissInstall } = usePWAInstall()
|
||||
*/
|
||||
export function usePWAInstall() {
|
||||
const canInstall = ref(false)
|
||||
const isInstalled = ref(false)
|
||||
const deferredPrompt = ref<any>(null)
|
||||
|
||||
if (process.client) {
|
||||
// Check if already installed
|
||||
if (window.matchMedia('(display-mode: standalone)').matches) {
|
||||
isInstalled.value = true
|
||||
}
|
||||
|
||||
// Listen for beforeinstallprompt event
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
// Prevent the mini-infobar from appearing on mobile
|
||||
e.preventDefault()
|
||||
|
||||
// Stash the event so it can be triggered later
|
||||
deferredPrompt.value = e
|
||||
canInstall.value = true
|
||||
})
|
||||
|
||||
// Listen for appinstalled event
|
||||
window.addEventListener('appinstalled', () => {
|
||||
isInstalled.value = true
|
||||
canInstall.value = false
|
||||
deferredPrompt.value = null
|
||||
})
|
||||
}
|
||||
|
||||
async function promptInstall() {
|
||||
if (!deferredPrompt.value) {
|
||||
return { outcome: 'not-available' }
|
||||
}
|
||||
|
||||
// Show the install prompt
|
||||
deferredPrompt.value.prompt()
|
||||
|
||||
// Wait for the user to respond to the prompt
|
||||
const { outcome } = await deferredPrompt.value.userChoice
|
||||
|
||||
// Clear the deferredPrompt
|
||||
deferredPrompt.value = null
|
||||
|
||||
if (outcome === 'accepted') {
|
||||
canInstall.value = false
|
||||
}
|
||||
|
||||
return { outcome }
|
||||
}
|
||||
|
||||
function dismissInstall() {
|
||||
canInstall.value = false
|
||||
deferredPrompt.value = null
|
||||
|
||||
// Remember dismissal for 7 days
|
||||
if (process.client) {
|
||||
localStorage.setItem('pwa-install-dismissed', Date.now().toString())
|
||||
}
|
||||
}
|
||||
|
||||
function shouldShowPrompt() {
|
||||
if (!canInstall.value || isInstalled.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (process.client) {
|
||||
const dismissed = localStorage.getItem('pwa-install-dismissed')
|
||||
if (dismissed) {
|
||||
const dismissedTime = parseInt(dismissed)
|
||||
const sevenDays = 7 * 24 * 60 * 60 * 1000
|
||||
if (Date.now() - dismissedTime < sevenDays) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
canInstall: readonly(canInstall),
|
||||
isInstalled: readonly(isInstalled),
|
||||
promptInstall,
|
||||
dismissInstall,
|
||||
shouldShowPrompt
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user