1 import { useState, useEffect } from 'react';
2 import { useQuery, useQueryClient } from '@tanstack/react-query';
3 import {
4 isOfflineSupported,
5 isOnline,
6 loadOfflineData,
7 saveOfflineData,
8 syncWhenOnline,
9 getStorageUsage,
10 type OfflineData
11 } from '@/lib/offline-storage';
12 import type { RepairGuide, TroubleshootingFlow, DeviceComponent } from '@shared/schema';
13
14 export function useOfflineMode() {
15 const [isOfflineModeEnabled, setIsOfflineModeEnabled] = useState(false);
16 const [isCurrentlyOnline, setIsCurrentlyOnline] = useState(isOnline());
17 const [lastSyncTime, setLastSyncTime] = useState<Date | null>(null);
18 const queryClient = useQueryClient();
19
20 // Monitor online/offline status
21 useEffect(() => {
22 const handleOnline = () => {
23 setIsCurrentlyOnline(true);
24 if (isOfflineModeEnabled) {
25 syncWhenOnline();
26 }
27 };
28
29 const handleOffline = () => {
30 setIsCurrentlyOnline(false);
31 };
32
33 window.addEventListener('online', handleOnline);
34 window.addEventListener('offline', handleOffline);
35
36 return () => {
37 window.removeEventListener('online', handleOnline);
38 window.removeEventListener('offline', handleOffline);
39 };
40 }, [isOfflineModeEnabled]);
41
42 // Load offline data on mount
43 useEffect(() => {
44 if (isOfflineSupported()) {
45 const offlineData = loadOfflineData();
46 if (offlineData.lastSync) {
47 setLastSyncTime(new Date(offlineData.lastSync));
48 }
49 }
50 }, []);
51
52 // Enable offline mode and cache current data
53 const enableOfflineMode = async (): Promise<boolean> => {
54 if (!isOfflineSupported()) return false;
55
56 try {
57 // Get current data from React Query cache or fetch fresh
58 const guides = queryClient.getQueryData<RepairGuide[]>(['/api/repair-guides']) || [];
59 const flows = queryClient.getQueryData<TroubleshootingFlow[]>(['/api/troubleshooting-flows']) || [];
60 const components = queryClient.getQueryData<DeviceComponent[]>(['/api/device-components']) || [];
61
62 // If cache is empty, fetch fresh data
63 let freshGuides = guides;
64 let freshFlows = flows;
65 let freshComponents = components;
66
67 if (isCurrentlyOnline && (guides.length === 0 || flows.length === 0 || components.length === 0)) {
68 try {
69 const [guidesRes, flowsRes, componentsRes] = await Promise.all([
70 fetch('/api/repair-guides').then(res => res.json()),
71 fetch('/api/troubleshooting-flows').then(res => res.json()),
72 fetch('/api/device-components').then(res => res.json()),
73 ]);
74
75 freshGuides = guidesRes || guides;
76 freshFlows = flowsRes || flows;
77 freshComponents = componentsRes || components;
78 } catch (error) {
79 console.error('Failed to fetch fresh data for offline mode:', error);
80 }
81 }
82
83 await saveOfflineData({
84 guides: freshGuides,
85 flows: freshFlows,
86 components: freshComponents,
87 });
88
89 setIsOfflineModeEnabled(true);
90 setLastSyncTime(new Date());
91 return true;
92 } catch (error) {
93 console.error('Failed to enable offline mode:', error);
94 return false;
95 }
96 };
97
98 // Disable offline mode
99 const disableOfflineMode = () => {
100 setIsOfflineModeEnabled(false);
101 };
102
103 // Get offline data
104 const getOfflineData = (): OfflineData => {
105 return loadOfflineData();
106 };
107
108 // Force sync data
109 const forcSync = async (): Promise<boolean> => {
110 if (!isCurrentlyOnline) return false;
111
112 try {
113 await syncWhenOnline();
114 setLastSyncTime(new Date());
115
116 // Invalidate React Query cache to refresh with new data
117 queryClient.invalidateQueries({ queryKey: ['/api/repair-guides'] });
118 queryClient.invalidateQueries({ queryKey: ['/api/troubleshooting-flows'] });
119 queryClient.invalidateQueries({ queryKey: ['/api/device-components'] });
120
121 return true;
122 } catch (error) {
123 console.error('Sync failed:', error);
124 return false;
125 }
126 };
127
128 // Get storage usage information
129 const storageUsage = getStorageUsage();
130 const storageInfo = {
131 used: storageUsage.used,
132 available: storageUsage.total - storageUsage.used,
133 percentage: storageUsage.percentage
134 };
135
136 // Query for offline guides when offline mode is enabled and online
137 const { data: offlineGuides = [] } = useQuery<RepairGuide[]>({
138 queryKey: ['offline-guides'],
139 queryFn: () => {
140 const offlineData = getOfflineData();
141 return offlineData.guides;
142 },
143 enabled: isOfflineModeEnabled && !isCurrentlyOnline,
144 staleTime: Infinity,
145 });
146
147 const { data: offlineFlows = [] } = useQuery<TroubleshootingFlow[]>({
148 queryKey: ['offline-flows'],
149 queryFn: () => {
150 const offlineData = getOfflineData();
151 return offlineData.flows;
152 },
153 enabled: isOfflineModeEnabled && !isCurrentlyOnline,
154 staleTime: Infinity,
155 });
156
157 const { data: offlineComponents = [] } = useQuery<DeviceComponent[]>({
158 queryKey: ['offline-components'],
159 queryFn: () => {
160 const offlineData = getOfflineData();
161 return offlineData.components;
162 },
163 enabled: isOfflineModeEnabled && !isCurrentlyOnline,
164 staleTime: Infinity,
165 });
166
167 return {
168 // Status
169 isOfflineSupported: isOfflineSupported(),
170 isOfflineModeEnabled,
171 isCurrentlyOnline,
172 lastSyncTime,
173 storageInfo,
174
175 // Actions
176 enableOfflineMode,
177 disableOfflineMode,
178 forceSync: forcSync,
179 getOfflineData,
180
181 // Offline data
182 offlineGuides,
183 offlineFlows,
184 offlineComponents,
185 };
186 }