feat(app): add offline mode composable and enhanced PWA caching
Create useOffline.ts with online/offline detection, sync queue for pending actions, and auto-process on reconnect. Enhance vite PWA config with CacheFirst for TMDB and Wikipedia images, StaleWhileRevalidate for TMDB API responses, with size and TTL limits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a442fbbe62
commit
ea5e82bfe3
@@ -0,0 +1,75 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export interface SyncAction {
|
||||
type: string
|
||||
payload: unknown
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
const isOnline = ref(navigator.onLine)
|
||||
const pendingSync = ref(0)
|
||||
const syncQueue: SyncAction[] = []
|
||||
|
||||
function handleOnline() {
|
||||
isOnline.value = true
|
||||
processSyncQueue()
|
||||
}
|
||||
|
||||
function handleOffline() {
|
||||
isOnline.value = false
|
||||
}
|
||||
|
||||
/** Queue an action to be synced when back online */
|
||||
function queueForSync(action: SyncAction) {
|
||||
syncQueue.push(action)
|
||||
pendingSync.value = syncQueue.length
|
||||
}
|
||||
|
||||
/** Process all pending sync actions */
|
||||
async function processSyncQueue(): Promise<void> {
|
||||
if (!isOnline.value || syncQueue.length === 0) return
|
||||
|
||||
while (syncQueue.length > 0) {
|
||||
const action = syncQueue.shift()
|
||||
if (!action) break
|
||||
|
||||
try {
|
||||
// Route sync actions based on type
|
||||
if (action.type === 'conversation') {
|
||||
// Conversation sync handled by idb-storage auto-save
|
||||
continue
|
||||
}
|
||||
if (action.type === 'favorite') {
|
||||
// Favorites are local-only, no sync needed
|
||||
continue
|
||||
}
|
||||
// Add more sync handlers as needed
|
||||
} catch {
|
||||
// Re-queue failed actions
|
||||
syncQueue.unshift(action)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pendingSync.value = syncQueue.length
|
||||
}
|
||||
|
||||
export function useOffline() {
|
||||
onMounted(() => {
|
||||
window.addEventListener('online', handleOnline)
|
||||
window.addEventListener('offline', handleOffline)
|
||||
isOnline.value = navigator.onLine
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('online', handleOnline)
|
||||
window.removeEventListener('offline', handleOffline)
|
||||
})
|
||||
|
||||
return {
|
||||
isOnline,
|
||||
pendingSync,
|
||||
queueForSync,
|
||||
processSyncQueue,
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,30 @@ export default defineConfig({
|
||||
urlPattern: /\/api\/rss-articles\?.*/i,
|
||||
handler: 'NetworkOnly',
|
||||
},
|
||||
{
|
||||
urlPattern: /\/api\/tmdb\/.*/i,
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'tmdb-cache',
|
||||
expiration: { maxEntries: 200, maxAgeSeconds: 86400 },
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /^https:\/\/image\.tmdb\.org\/.*/i,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'tmdb-images',
|
||||
expiration: { maxEntries: 500, maxAgeSeconds: 604800 },
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /^https:\/\/upload\.wikimedia\.org\/.*/i,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'wiki-images',
|
||||
expiration: { maxEntries: 200, maxAgeSeconds: 604800 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
devOptions: {
|
||||
|
||||
Reference in New Issue
Block a user