import { useEffect, useState, useCallback, useRef } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; import NetInfo from '@react-native-community/netinfo'; const DRAFT_KEY = 'proposal_draft'; const OFFLINE_QUEUE_KEY = 'proposal_offline_queue'; export function useOfflineDraft>( initialData: T, ) { const [draft, setDraft] = useState(initialData); const [isLoaded, setIsLoaded] = useState(false); const saveTimeout = useRef>(null); useEffect(() => { AsyncStorage.getItem(DRAFT_KEY).then((stored) => { if (stored) { try { setDraft(JSON.parse(stored)); } catch { // Corrupted draft, use initial data } } setIsLoaded(true); }); }, []); const updateDraft = useCallback( (updates: Partial) => { setDraft((prev) => { const next = { ...prev, ...updates }; if (saveTimeout.current) clearTimeout(saveTimeout.current); saveTimeout.current = setTimeout(() => { AsyncStorage.setItem(DRAFT_KEY, JSON.stringify(next)); }, 1000); return next; }); }, [], ); const clearDraft = useCallback(async () => { if (saveTimeout.current) clearTimeout(saveTimeout.current); await AsyncStorage.removeItem(DRAFT_KEY); setDraft(initialData); }, [initialData]); return { draft, updateDraft, clearDraft, isLoaded }; } interface QueuedSubmission { data: unknown; timestamp: string; } export async function queueOfflineSubmission(data: unknown): Promise { const stored = await AsyncStorage.getItem(OFFLINE_QUEUE_KEY); const queue: QueuedSubmission[] = stored ? JSON.parse(stored) : []; queue.push({ data, timestamp: new Date().toISOString() }); await AsyncStorage.setItem(OFFLINE_QUEUE_KEY, JSON.stringify(queue)); } let _isProcessing = false; export async function processOfflineQueue( submitFn: (data: unknown) => Promise, ): Promise { if (_isProcessing) return 0; _isProcessing = true; try { const state = await NetInfo.fetch(); if (!state.isConnected) return 0; const stored = await AsyncStorage.getItem(OFFLINE_QUEUE_KEY); if (!stored) return 0; const queue: QueuedSubmission[] = JSON.parse(stored); let processed = 0; for (const item of queue) { try { await submitFn(item.data); processed++; } catch (error) { const status = (error as { response?: { status?: number } })?.response?.status; if (status && status >= 400 && status < 500) { console.error('Permanent failure for queued submission, skipping:', error); processed++; continue; } break; } } if (processed === queue.length) { await AsyncStorage.removeItem(OFFLINE_QUEUE_KEY); } else if (processed > 0) { await AsyncStorage.setItem( OFFLINE_QUEUE_KEY, JSON.stringify(queue.slice(processed)), ); } return processed; } finally { _isProcessing = false; } } export async function getOfflineQueueCount(): Promise { const stored = await AsyncStorage.getItem(OFFLINE_QUEUE_KEY); if (!stored) return 0; return JSON.parse(stored).length; }