Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@capacitor/haptics": "^8.0.0",
"@capacitor/ios": "^8.0.2",
"@capacitor/keyboard": "^8.0.0",
"@capacitor/network": "^8.0.0",
"@capacitor/preferences": "^8.0.0",
"@capacitor/push-notifications": "^8.0.0",
"@capacitor/share": "^8.0.0",
Expand Down
63 changes: 9 additions & 54 deletions src/hooks/useOffline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import { useState, useEffect, useCallback, useRef } from "react";
import { useConvex } from "convex/react";
import { Capacitor } from "@capacitor/core";
import { syncManager, type SyncStatus } from "../lib/sync";
import { getQueuedMutations } from "../lib/offline";
import { getNetworkStatus, onNetworkChange } from "../lib/network";

/**
* Hook return type for useOffline
Expand Down Expand Up @@ -56,9 +56,7 @@ export interface UseOfflineResult {
* ```
*/
export function useOffline(): UseOfflineResult {
const [isOnline, setIsOnline] = useState(
typeof navigator !== "undefined" ? navigator.onLine : true
);
const [isOnline, setIsOnline] = useState(getNetworkStatus());
const [syncStatus, setSyncStatus] = useState<SyncStatus>({ status: "idle" });
const [pendingCount, setPendingCount] = useState(0);
const convex = useConvex();
Expand All @@ -72,59 +70,16 @@ export function useOffline(): UseOfflineResult {
};
}, []);

// Track online/offline events and trigger sync on reconnect
// Use Capacitor Network plugin on native for reliable detection
// Track online/offline events using Capacitor network service and trigger sync on reconnect
useEffect(() => {
let cleanup: (() => void) | undefined;

const handleOnline = () => {
setIsOnline(true);
syncManager.sync(convex);
};

const handleOffline = () => {
setIsOnline(false);
const handleNetworkChange = (connected: boolean) => {
setIsOnline(connected);
if (connected) {
syncManager.sync(convex);
}
};

if (Capacitor.isNativePlatform()) {
// Use Capacitor Network plugin for reliable native network detection
import("@capacitor/network").then(({ Network }) => {
// Get initial status
Network.getStatus().then((status) => {
if (isMounted.current) {
setIsOnline(status.connected);
}
});

// Listen for changes
const listener = Network.addListener("networkStatusChange", (status) => {
if (isMounted.current) {
if (status.connected) {
handleOnline();
} else {
handleOffline();
}
}
});

cleanup = () => {
listener.then((l) => l.remove());
};
});
} else {
// Fallback to browser events for web
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);

cleanup = () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}

return () => {
cleanup?.();
};
return onNetworkChange(handleNetworkChange);
}, [convex]);

// Subscribe to sync status updates from SyncManager
Expand Down
34 changes: 34 additions & 0 deletions src/lib/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Capacitor } from '@capacitor/core';

type NetworkStatusListener = (connected: boolean) => void;
const listeners: NetworkStatusListener[] = [];
let isConnected = true;

export function getNetworkStatus() { return isConnected; }

export function onNetworkChange(listener: NetworkStatusListener) {
listeners.push(listener);
return () => {
const idx = listeners.indexOf(listener);
if (idx >= 0) listeners.splice(idx, 1);
};
}

export async function initNetworkMonitoring() {
if (!Capacitor.isNativePlatform()) {
// Web fallback
isConnected = navigator.onLine;
window.addEventListener('online', () => { isConnected = true; listeners.forEach(l => l(true)); });
window.addEventListener('offline', () => { isConnected = false; listeners.forEach(l => l(false)); });
return;
}

const { Network } = await import('@capacitor/network');
const status = await Network.getStatus();
isConnected = status.connected;

Network.addListener('networkStatusChange', (status: { connected: boolean }) => {
isConnected = status.connected;
listeners.forEach(l => l(status.connected));
});
}
4 changes: 4 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SettingsProvider } from './hooks/useSettings'
import { registerServiceWorker } from './lib/sw-registration'
import { initDarkMode } from './lib/storage'
import { initNativePlatform, initKeyboardHandling } from './lib/native'
import { initNetworkMonitoring } from './lib/network'
import './index.css'
import App from './App.tsx'

Expand All @@ -21,6 +22,9 @@ initDarkMode();
initNativePlatform();
initKeyboardHandling();

// Initialize network monitoring for offline support
initNetworkMonitoring();

// Register service worker for offline support (web only - disabled in native apps)
if (!Capacitor.isNativePlatform()) {
registerServiceWorker({
Expand Down
Loading