diff --git a/mobile/app/index.tsx b/mobile/app/index.tsx
index 2279aec..75a55e8 100644
--- a/mobile/app/index.tsx
+++ b/mobile/app/index.tsx
@@ -15,6 +15,7 @@ import { SettingsModal } from '../components/SettingsModal';
import { ConfirmModal, type ConfirmOption } from '../components/ConfirmModal';
import { UploadModal } from '../components/UploadModal';
import { SyncStatusIcon } from '../components/SyncStatusIcon';
+import { NetworkStatusBar } from '../components/NetworkStatusBar';
import { useSyncQueue } from '../hooks/useSyncQueue';
import { useUploadQueue } from '../hooks/useUploadQueue';
import { useAutoSync } from '../hooks/useAutoSync';
@@ -161,6 +162,7 @@ export function HomeScreen() {
navigation.setOptions({
headerRight: () => (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ Offline
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ marginRight: 4,
+ padding: 4,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ dotOnline: {
+ width: 8,
+ height: 8,
+ borderRadius: 4,
+ backgroundColor: '#4CAF50',
+ },
+ offlineBadge: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: '#E53935',
+ borderRadius: 12,
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ gap: 4,
+ },
+ offlineText: {
+ fontSize: 11,
+ fontWeight: '700',
+ color: '#fff',
+ },
+});
diff --git a/mobile/hooks/useNetworkStatus.ts b/mobile/hooks/useNetworkStatus.ts
new file mode 100644
index 0000000..9a23b65
--- /dev/null
+++ b/mobile/hooks/useNetworkStatus.ts
@@ -0,0 +1,55 @@
+import { useSyncExternalStore, useRef } from 'react';
+import NetInfo, { NetInfoState, NetInfoSubscription } from '@react-native-community/netinfo';
+
+type Listener = () => void;
+
+let state: NetInfoState | null = null;
+const listeners = new Set();
+let subscription: NetInfoSubscription | null = null;
+
+function subscribe(listener: Listener): () => void {
+ listeners.add(listener);
+ return () => { listeners.delete(listener); };
+}
+
+function getSnapshot(): boolean {
+ return state?.isConnected ?? true;
+}
+
+function initIfNeeded() {
+ if (subscription) return;
+ NetInfo.fetch().then((info) => {
+ state = info;
+ listeners.forEach((l) => l());
+ });
+ subscription = NetInfo.addEventListener((info) => {
+ state = info;
+ listeners.forEach((l) => l());
+ });
+}
+
+export interface NetworkStatus {
+ isOnline: boolean;
+ isWifi: boolean;
+ isCellular: boolean;
+ connectionType: string;
+ isInternetReachable: boolean | null;
+}
+
+export function useNetworkStatus(): NetworkStatus {
+ const mountRef = useRef(false);
+ if (!mountRef.current) {
+ initIfNeeded();
+ mountRef.current = true;
+ }
+
+ const isConnected = useSyncExternalStore(subscribe, getSnapshot);
+
+ return {
+ isOnline: isConnected,
+ isWifi: state?.type === 'wifi',
+ isCellular: state?.type === 'cellular',
+ connectionType: state?.type ?? 'unknown',
+ isInternetReachable: state?.isInternetReachable ?? null,
+ };
+}