Files
maternal-app/maternal-web/hooks/useBackgroundSync.ts
Andrei 898a76c83a
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
feat: Complete PWA implementation with offline support and install prompts
PWA Features Implemented:
 Offline Fallback Page (/offline)
  - User-friendly offline page with connection status
  - Auto-redirect when back online
  - Lists available offline features
  - Retry and home navigation buttons

 Install Prompt UI (InstallPrompt component)
  - beforeinstallprompt event handler for Android/Desktop
  - iOS-specific install instructions with Share icon
  - Smart dismissal with 7-day cooldown
  - Already-installed detection

 Background Sync for Pending Actions
  - useBackgroundSync hook with multiple sync triggers
  - Periodic sync every 5 minutes when online
  - Sync on tab visibility change
  - Service Worker sync registration
  - BackgroundSyncProvider integration

 next-pwa Configuration Updates
  - Offline fallback to /offline page
  - Network timeout (10s) for better offline detection
  - skipWaiting and clientsClaim enabled
  - Runtime caching with NetworkFirst strategy

Files Created:
- app/offline/page.tsx (131 lines)
- components/pwa/InstallPrompt.tsx (164 lines)
- hooks/useBackgroundSync.ts (71 lines)
- components/providers/BackgroundSyncProvider.tsx (10 lines)

Files Modified:
- app/layout.tsx (added InstallPrompt and BackgroundSyncProvider)
- next.config.mjs (offline fallback + workbox options)

Total: 376 new lines across 4 new files + 2 modified files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 07:38:47 +00:00

72 lines
2.4 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { setOnlineStatus } from '@/store/slices/networkSlice';
export function useBackgroundSync() {
const dispatch = useAppDispatch();
const { isOnline } = useAppSelector((state) => state.network);
const { pendingActions } = useAppSelector((state) => state.offline);
const syncIntervalRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
// Register background sync if supported
if ('serviceWorker' in navigator && 'sync' in ServiceWorkerRegistration.prototype) {
navigator.serviceWorker.ready.then((registration) => {
// Register a sync event
return (registration as any).sync.register('sync-pending-actions');
}).catch((error) => {
console.error('Background sync registration failed:', error);
});
}
// Listen for sync events
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data && event.data.type === 'BACKGROUND_SYNC') {
// Trigger sync from service worker by simulating online status
dispatch(setOnlineStatus(true));
}
});
}
}, [dispatch]);
useEffect(() => {
// Periodic sync every 5 minutes when online and have pending actions
if (isOnline && pendingActions.length > 0) {
syncIntervalRef.current = setInterval(() => {
console.log('Periodic sync check...');
// Trigger sync by re-dispatching online status
dispatch(setOnlineStatus(true));
}, 5 * 60 * 1000); // 5 minutes
}
return () => {
if (syncIntervalRef.current) {
clearInterval(syncIntervalRef.current);
}
};
}, [isOnline, pendingActions.length, dispatch]);
// Sync on visibility change (when user returns to tab)
useEffect(() => {
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible' && isOnline && pendingActions.length > 0) {
console.log('Tab visible, syncing pending actions...');
// Trigger sync by re-dispatching online status
dispatch(setOnlineStatus(true));
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, [isOnline, pendingActions.length, dispatch]);
return {
pendingCount: pendingActions.length,
isOnline,
};
}