UNPKG

@cardql/react-native

Version:

CardQL SDK for React Native applications with mobile-optimized features

162 lines (136 loc) 4.24 kB
import { useState, useEffect, useCallback } from "react"; import { useIsOnline } from "./useNetworkStatus"; import { useCardQLClient } from "../context"; import { storage } from "../storage"; export interface QueuedOperation { id: string; mutation: string; variables: any; timestamp: number; retryCount: number; } export interface UseOfflineQueueOptions { maxRetries?: number; retryDelay?: number; storageKey?: string; } export interface UseOfflineQueueResult { queue: QueuedOperation[]; addToQueue: (mutation: string, variables: any) => Promise<void>; processQueue: () => Promise<void>; clearQueue: () => Promise<void>; removeFromQueue: (id: string) => Promise<void>; isProcessing: boolean; } const STORAGE_KEY = "cardql_offline_queue"; /** * Hook for managing offline operations queue */ export function useOfflineQueue( options: UseOfflineQueueOptions = {} ): UseOfflineQueueResult { const { maxRetries = 3, retryDelay = 1000, storageKey = STORAGE_KEY, } = options; const cardql = useCardQLClient(); const isOnline = useIsOnline(); const [queue, setQueue] = useState<QueuedOperation[]>([]); const [isProcessing, setIsProcessing] = useState(false); // Load queue from storage on mount useEffect(() => { const loadQueue = async () => { try { const storedQueue = await storage.getItem(storageKey); if (storedQueue) { setQueue(JSON.parse(storedQueue)); } } catch (error) { console.warn("Failed to load offline queue:", error); } }; loadQueue(); }, [storageKey]); // Save queue to storage whenever it changes useEffect(() => { const saveQueue = async () => { try { await storage.setItem(storageKey, JSON.stringify(queue)); } catch (error) { console.warn("Failed to save offline queue:", error); } }; saveQueue(); }, [queue, storageKey]); // Process queue when coming back online useEffect(() => { if (isOnline && queue.length > 0 && !isProcessing) { processQueue(); } }, [isOnline, queue.length, isProcessing]); const addToQueue = useCallback(async (mutation: string, variables: any) => { const operation: QueuedOperation = { id: `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, mutation, variables, timestamp: Date.now(), retryCount: 0, }; setQueue((prev) => [...prev, operation]); }, []); const removeFromQueue = useCallback(async (id: string) => { setQueue((prev) => prev.filter((op) => op.id !== id)); }, []); const processQueue = useCallback(async () => { if (!isOnline || isProcessing || queue.length === 0) { return; } setIsProcessing(true); const processedIds: string[] = []; for (const operation of queue) { try { await cardql.client.request(operation.mutation, operation.variables); processedIds.push(operation.id); } catch (error) { console.warn("Failed to process queued operation:", error); // Increment retry count setQueue((prev) => prev.map((op) => op.id === operation.id ? { ...op, retryCount: op.retryCount + 1 } : op ) ); // Remove if max retries exceeded if (operation.retryCount >= maxRetries) { processedIds.push(operation.id); console.warn("Max retries exceeded for operation:", operation.id); } // Wait before processing next operation await new Promise((resolve) => setTimeout(resolve, retryDelay)); } } // Remove processed operations if (processedIds.length > 0) { setQueue((prev) => prev.filter((op) => !processedIds.includes(op.id))); } setIsProcessing(false); }, [isOnline, isProcessing, queue, cardql, maxRetries, retryDelay]); const clearQueue = useCallback(async () => { setQueue([]); try { await storage.removeItem(storageKey); } catch (error) { console.warn("Failed to clear queue storage:", error); } }, [storageKey]); return { queue, addToQueue, processQueue, clearQueue, removeFromQueue, isProcessing, }; }