@umituz/react-native-design-system 2.6.117 → 2.6.118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "2.6.117",
4
- "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image and timezone utilities",
3
+ "version": "2.6.118",
4
+ "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone and offline utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "exports": {
@@ -21,6 +21,7 @@
21
21
  "./device": "./src/device/index.ts",
22
22
  "./image": "./src/image/index.ts",
23
23
  "./timezone": "./src/timezone/index.ts",
24
+ "./offline": "./src/offline/index.ts",
24
25
  "./package.json": "./package.json"
25
26
  },
26
27
  "scripts": {
@@ -44,7 +45,8 @@
44
45
  "responsive",
45
46
  "safe-area",
46
47
  "image",
47
- "timezone"
48
+ "timezone",
49
+ "offline"
48
50
  ],
49
51
  "author": "Ümit UZ <umit@umituz.com>",
50
52
  "license": "MIT",
@@ -69,6 +71,7 @@
69
71
  "expo-device": ">=5.0.0",
70
72
  "expo-font": ">=12.0.0",
71
73
  "expo-image": ">=3.0.0",
74
+ "expo-network": ">=8.0.0",
72
75
  "expo-sharing": ">=12.0.0",
73
76
  "react": ">=19.0.0",
74
77
  "react-native": ">=0.81.0",
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Offline Exports
3
+ *
4
+ * Network connectivity state management for React Native apps
5
+ */
6
+
7
+ export * from '../offline';
package/src/index.ts CHANGED
@@ -77,6 +77,11 @@ export * from './exports/uuid';
77
77
  // =============================================================================
78
78
  export * from './exports/timezone';
79
79
 
80
+ // =============================================================================
81
+ // OFFLINE EXPORTS
82
+ // =============================================================================
83
+ export * from './exports/offline';
84
+
80
85
  // =============================================================================
81
86
  // VARIANT UTILITIES
82
87
  // =============================================================================
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @umituz/react-native-offline
3
+ * Network connectivity state management for React Native apps
4
+ */
5
+
6
+ // Types
7
+ export type {
8
+ NetworkState,
9
+ OfflineState,
10
+ OfflineStore,
11
+ OfflineConfig,
12
+ ConnectionQuality,
13
+ } from './types';
14
+
15
+ // Store
16
+ export { useOfflineStore } from './infrastructure/storage/OfflineStore';
17
+
18
+ // Hooks
19
+ export { useOffline, configureOffline } from './presentation/hooks/useOffline';
20
+ export { useOfflineState } from './presentation/hooks/useOfflineState';
21
+ export { useOfflineWithMutations } from './presentation/hooks/useOfflineWithMutations';
22
+
23
+ // Components
24
+ export { OfflineBanner } from './presentation/components/OfflineBanner';
25
+ export type { OfflineBannerProps } from './presentation/components/OfflineBanner';
26
+
27
+ // Providers
28
+ export { NetworkProvider } from './presentation/providers/NetworkProvider';
29
+
30
+ // Events
31
+ export { networkEvents } from './infrastructure/events/NetworkEvents';
32
+ export type { NetworkEventListener, NetworkEvents } from './infrastructure/events/NetworkEvents';
33
+
34
+ // Utils
35
+ export { HealthCheck } from './infrastructure/utils/healthCheck';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Network Events
3
+ * Event emitter for network state changes
4
+ * Allows subscribing to network events throughout the app
5
+ */
6
+
7
+ import type { NetworkState } from '../../types';
8
+
9
+ export type NetworkEventListener = (state: NetworkState) => void;
10
+
11
+ export interface NetworkEvents {
12
+ on(event: 'online', listener: NetworkEventListener): void;
13
+ on(event: 'offline', listener: NetworkEventListener): void;
14
+ on(event: 'change', listener: NetworkEventListener): void;
15
+ off(event: 'online' | 'offline' | 'change', listener: NetworkEventListener): void;
16
+ emit(event: 'online' | 'offline' | 'change', state: NetworkState): void;
17
+ removeAllListeners(): void;
18
+ }
19
+
20
+ class NetworkEventEmitter implements NetworkEvents {
21
+ private listeners: Map<string, Set<NetworkEventListener>> = new Map();
22
+
23
+ on(event: 'online' | 'offline' | 'change', listener: NetworkEventListener): void {
24
+ if (!this.listeners.has(event)) {
25
+ this.listeners.set(event, new Set());
26
+ }
27
+ this.listeners.get(event)?.add(listener);
28
+ }
29
+
30
+ off(event: 'online' | 'offline' | 'change', listener: NetworkEventListener): void {
31
+ this.listeners.get(event)?.delete(listener);
32
+ }
33
+
34
+ emit(event: 'online' | 'offline' | 'change', state: NetworkState): void {
35
+ this.listeners.get(event)?.forEach((listener) => {
36
+ try {
37
+ listener(state);
38
+ } catch (error) {
39
+ if (__DEV__) {
40
+ // eslint-disable-next-line no-console
41
+ console.error(`[NetworkEventEmitter] Error in ${event} listener:`, error);
42
+ }
43
+ }
44
+ });
45
+ }
46
+
47
+ removeAllListeners(): void {
48
+ this.listeners.clear();
49
+ }
50
+ }
51
+
52
+ export const networkEvents = new NetworkEventEmitter();
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Offline Store
3
+ * Manages network connectivity state across the application
4
+ * Uses expo-network for universal network detection
5
+ */
6
+
7
+ import { createStore } from '@umituz/react-native-storage';
8
+ import type { NetworkState, OfflineState, OfflineActions } from '../../types';
9
+
10
+ const initialState: OfflineState = {
11
+ isOnline: true,
12
+ isOffline: false,
13
+ connectionType: null,
14
+ isInternetReachable: null,
15
+ lastOnlineAt: new Date(),
16
+ lastOfflineAt: null,
17
+ connectionQuality: {
18
+ latency: null,
19
+ effectiveType: null,
20
+ isSlow: false,
21
+ },
22
+ };
23
+
24
+ export const useOfflineStore = createStore<OfflineState, OfflineActions>({
25
+ name: 'offline-store',
26
+ initialState,
27
+ persist: false,
28
+ actions: (set, get) => ({
29
+ updateNetworkState: (state: NetworkState) => {
30
+ const isConnected = state.isConnected ?? false;
31
+ const isReachable = state.isInternetReachable ?? null;
32
+ const isOnline = isConnected && (isReachable !== false);
33
+ const currentState = get();
34
+
35
+ const hasChanged =
36
+ currentState.isOnline !== isOnline ||
37
+ currentState.connectionType !== state.type ||
38
+ currentState.isInternetReachable !== isReachable;
39
+
40
+ if (hasChanged) {
41
+ set({
42
+ isOnline,
43
+ isOffline: !isConnected || (isReachable === false),
44
+ connectionType: state.type,
45
+ isInternetReachable: isReachable,
46
+ lastOnlineAt: (isConnected && isReachable !== false) ? new Date() : currentState.lastOnlineAt,
47
+ lastOfflineAt: (!isConnected || isReachable === false) ? new Date() : currentState.lastOfflineAt,
48
+ connectionQuality: currentState.connectionQuality,
49
+ });
50
+ }
51
+ },
52
+
53
+ setOnline: () => {
54
+ const currentState = get();
55
+ if (!currentState.isOnline) {
56
+ set({
57
+ isOnline: true,
58
+ isOffline: false,
59
+ lastOnlineAt: new Date(),
60
+ connectionQuality: currentState.connectionQuality,
61
+ });
62
+ }
63
+ },
64
+
65
+ setOffline: () => {
66
+ const currentState = get();
67
+ if (currentState.isOnline) {
68
+ set({
69
+ isOnline: false,
70
+ isOffline: true,
71
+ lastOfflineAt: new Date(),
72
+ connectionQuality: currentState.connectionQuality,
73
+ });
74
+ }
75
+ },
76
+
77
+ reset: () => set(initialState),
78
+ }),
79
+ });
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Connection Health Check
3
+ * Periodically checks real internet connectivity by pinging a reliable endpoint
4
+ */
5
+
6
+ import type { OfflineConfig } from '../../types';
7
+
8
+ const DEFAULT_HEALTH_CHECK_URL = 'https://www.google.com/favicon.ico';
9
+ const DEFAULT_TIMEOUT = 5000;
10
+
11
+ export class HealthCheck {
12
+ private intervalId: ReturnType<typeof setInterval> | null = null;
13
+ private isChecking = false;
14
+ private config: Required<OfflineConfig>;
15
+
16
+ constructor(config: OfflineConfig = {}) {
17
+ this.config = {
18
+ persist: config.persist ?? false,
19
+ debug: config.debug ?? false,
20
+ healthCheckInterval: config.healthCheckInterval ?? 0,
21
+ healthCheckTimeout: config.healthCheckTimeout ?? DEFAULT_TIMEOUT,
22
+ healthCheckUrl: config.healthCheckUrl ?? DEFAULT_HEALTH_CHECK_URL,
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Perform a single health check
28
+ */
29
+ async check(): Promise<boolean> {
30
+ if (this.isChecking) {
31
+ return false;
32
+ }
33
+
34
+ this.isChecking = true;
35
+
36
+ try {
37
+ const controller = new AbortController();
38
+ const timeoutId = setTimeout(() => controller.abort(), this.config.healthCheckTimeout);
39
+
40
+ const response = await fetch(this.config.healthCheckUrl, {
41
+ method: 'HEAD',
42
+ signal: controller.signal,
43
+ cache: 'no-cache',
44
+ });
45
+
46
+ clearTimeout(timeoutId);
47
+
48
+ const isHealthy = response.ok;
49
+
50
+ if (this.config.debug) {
51
+ // eslint-disable-next-line no-console
52
+ console.log('[HealthCheck] Result:', isHealthy ? 'HEALTHY' : 'UNHEALTHY');
53
+ }
54
+
55
+ return isHealthy;
56
+ } catch (error) {
57
+ if (this.config.debug) {
58
+ // eslint-disable-next-line no-console
59
+ console.warn('[HealthCheck] Failed:', error);
60
+ }
61
+
62
+ return false;
63
+ } finally {
64
+ this.isChecking = false;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Start periodic health checks
70
+ */
71
+ start(callback: (isHealthy: boolean) => void): void {
72
+ if (this.config.healthCheckInterval === 0) {
73
+ if (this.config.debug) {
74
+ // eslint-disable-next-line no-console
75
+ console.log('[HealthCheck] Disabled (interval = 0)');
76
+ }
77
+ return;
78
+ }
79
+
80
+ if (this.intervalId) {
81
+ this.stop();
82
+ }
83
+
84
+ if (this.config.debug) {
85
+ // eslint-disable-next-line no-console
86
+ console.log('[HealthCheck] Starting (interval:', this.config.healthCheckInterval + 'ms)');
87
+ }
88
+
89
+ this.intervalId = setInterval(async () => {
90
+ const isHealthy = await this.check();
91
+ callback(isHealthy);
92
+ }, this.config.healthCheckInterval);
93
+ }
94
+
95
+ /**
96
+ * Stop health checks
97
+ */
98
+ stop(): void {
99
+ if (this.intervalId) {
100
+ clearInterval(this.intervalId);
101
+ this.intervalId = null;
102
+
103
+ if (this.config.debug) {
104
+ // eslint-disable-next-line no-console
105
+ console.log('[HealthCheck] Stopped');
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Clean up resources
112
+ */
113
+ destroy(): void {
114
+ this.stop();
115
+ }
116
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * OfflineBanner Component
3
+ *
4
+ * Displays a banner when the device is offline.
5
+ * Fully customizable for use across 100+ apps.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { OfflineBanner, useOffline } from '@umituz/react-native-offline';
10
+ *
11
+ * const App = () => {
12
+ * const { isOffline } = useOffline();
13
+ *
14
+ * return (
15
+ * <>
16
+ * <OfflineBanner
17
+ * visible={isOffline}
18
+ * message="No internet connection"
19
+ * backgroundColor="#FF6B6B"
20
+ * />
21
+ * <YourContent />
22
+ * </>
23
+ * );
24
+ * };
25
+ * ```
26
+ */
27
+
28
+ import React, { useEffect, useRef, memo } from 'react';
29
+ import {
30
+ View,
31
+ Text,
32
+ Animated,
33
+ StyleSheet,
34
+ type ViewStyle,
35
+ type TextStyle,
36
+ } from 'react-native';
37
+
38
+ export interface OfflineBannerProps {
39
+ /** Whether the banner is visible */
40
+ visible: boolean;
41
+ /** Message to display */
42
+ message?: string;
43
+ /** Background color of the banner */
44
+ backgroundColor?: string;
45
+ /** Text color */
46
+ textColor?: string;
47
+ /** Icon to display (emoji or custom element) */
48
+ icon?: string | React.ReactNode;
49
+ /** Position of the banner */
50
+ position?: 'top' | 'bottom';
51
+ /** Custom container style */
52
+ style?: ViewStyle;
53
+ /** Custom text style */
54
+ textStyle?: TextStyle;
55
+ /** Animation duration in ms */
56
+ animationDuration?: number;
57
+ /** Height of the banner */
58
+ height?: number;
59
+ }
60
+
61
+ const DEFAULT_HEIGHT = 44;
62
+ const DEFAULT_ANIMATION_DURATION = 300;
63
+
64
+ export const OfflineBanner: React.FC<OfflineBannerProps> = memo(({
65
+ visible,
66
+ message = 'No internet connection',
67
+ backgroundColor = '#FF6B6B',
68
+ textColor = '#FFFFFF',
69
+ icon = '📡',
70
+ position = 'top',
71
+ style,
72
+ textStyle,
73
+ animationDuration = DEFAULT_ANIMATION_DURATION,
74
+ height = DEFAULT_HEIGHT,
75
+ }) => {
76
+ const animatedValue = useRef(new Animated.Value(visible ? 1 : 0)).current;
77
+ const isFirstRender = useRef(true);
78
+
79
+ useEffect(() => {
80
+ if (isFirstRender.current) {
81
+ isFirstRender.current = false;
82
+ animatedValue.setValue(visible ? 1 : 0);
83
+ return;
84
+ }
85
+
86
+ Animated.timing(animatedValue, {
87
+ toValue: visible ? 1 : 0,
88
+ duration: animationDuration,
89
+ useNativeDriver: false,
90
+ }).start();
91
+ }, [visible, animatedValue, animationDuration]);
92
+
93
+ const translateY = animatedValue.interpolate({
94
+ inputRange: [0, 1],
95
+ outputRange: [position === 'top' ? -height : height, 0],
96
+ });
97
+
98
+ const animatedHeight = animatedValue.interpolate({
99
+ inputRange: [0, 1],
100
+ outputRange: [0, height],
101
+ });
102
+
103
+ const opacity = animatedValue.interpolate({
104
+ inputRange: [0, 0.5, 1],
105
+ outputRange: [0, 0.8, 1],
106
+ });
107
+
108
+ const positionStyle: ViewStyle = position === 'top'
109
+ ? { top: 0 }
110
+ : { bottom: 0 };
111
+
112
+ return (
113
+ <Animated.View
114
+ style={[
115
+ styles.container,
116
+ positionStyle,
117
+ {
118
+ backgroundColor,
119
+ height: animatedHeight,
120
+ transform: [{ translateY }],
121
+ opacity,
122
+ },
123
+ style,
124
+ ]}
125
+ pointerEvents={visible ? 'auto' : 'none'}
126
+ >
127
+ <View style={styles.content}>
128
+ {typeof icon === 'string' ? (
129
+ <Text style={styles.icon}>{icon}</Text>
130
+ ) : (
131
+ icon
132
+ )}
133
+ <Text
134
+ style={[
135
+ styles.message,
136
+ { color: textColor },
137
+ textStyle,
138
+ ]}
139
+ numberOfLines={1}
140
+ >
141
+ {message}
142
+ </Text>
143
+ </View>
144
+ </Animated.View>
145
+ );
146
+ });
147
+
148
+ OfflineBanner.displayName = 'OfflineBanner';
149
+
150
+ const styles = StyleSheet.create({
151
+ container: {
152
+ position: 'absolute',
153
+ left: 0,
154
+ right: 0,
155
+ zIndex: 9999,
156
+ overflow: 'hidden',
157
+ },
158
+ content: {
159
+ flex: 1,
160
+ flexDirection: 'row',
161
+ alignItems: 'center',
162
+ justifyContent: 'center',
163
+ paddingHorizontal: 16,
164
+ gap: 8,
165
+ },
166
+ icon: {
167
+ fontSize: 16,
168
+ },
169
+ message: {
170
+ fontSize: 14,
171
+ fontWeight: '500',
172
+ },
173
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * useOffline Hook
3
+ * Primary hook for accessing offline state in components
4
+ * Automatically subscribes to network changes via expo-network
5
+ */
6
+
7
+ import { useEffect, useCallback, useRef } from 'react';
8
+ import * as Network from 'expo-network';
9
+ import type { NetworkState as ExpoNetworkState } from 'expo-network';
10
+ import type { NetworkState, OfflineConfig } from '../../types';
11
+ import { useOfflineStore } from '../../infrastructure/storage/OfflineStore';
12
+ import { networkEvents } from '../../infrastructure/events/NetworkEvents';
13
+
14
+ /**
15
+ * Convert expo-network state to our internal format
16
+ */
17
+ const toNetworkState = (state: ExpoNetworkState): NetworkState => ({
18
+ type: state.type?.toString() ?? 'unknown',
19
+ isConnected: state.isConnected ?? false,
20
+ isInternetReachable: state.isInternetReachable ?? null,
21
+ details: null,
22
+ });
23
+
24
+ let globalConfig: OfflineConfig = {};
25
+
26
+ export const configureOffline = (config: OfflineConfig): void => {
27
+ globalConfig = config;
28
+ };
29
+
30
+ export const useOffline = (config?: OfflineConfig) => {
31
+ const store = useOfflineStore();
32
+ const isInitialized = useRef(false);
33
+ const previousStateRef = useRef<NetworkState | null>(null);
34
+ const mergedConfig = { ...globalConfig, ...config };
35
+
36
+ const handleNetworkStateChange = useCallback((state: ExpoNetworkState) => {
37
+ const networkState = toNetworkState(state);
38
+ const wasOnline = previousStateRef.current?.isConnected ?? false;
39
+ const isNowOnline = networkState.isConnected ?? false;
40
+
41
+ store.updateNetworkState(networkState);
42
+
43
+ if (wasOnline !== isNowOnline) {
44
+ if (isNowOnline) {
45
+ networkEvents.emit('online', networkState);
46
+ } else {
47
+ networkEvents.emit('offline', networkState);
48
+ }
49
+ }
50
+
51
+ networkEvents.emit('change', networkState);
52
+ previousStateRef.current = networkState;
53
+ }, [store]);
54
+
55
+ useEffect(() => {
56
+ if (isInitialized.current) return;
57
+
58
+ Network.getNetworkStateAsync()
59
+ .then((state: ExpoNetworkState) => {
60
+ handleNetworkStateChange(state);
61
+ isInitialized.current = true;
62
+ })
63
+ .catch((error: Error) => {
64
+ if (__DEV__ || mergedConfig.debug) {
65
+ // eslint-disable-next-line no-console
66
+ console.error('[react-native-offline] Failed to fetch network state:', error);
67
+ }
68
+ });
69
+
70
+ const subscription = Network.addNetworkStateListener(handleNetworkStateChange);
71
+
72
+ return () => {
73
+ subscription.remove();
74
+ isInitialized.current = false;
75
+ };
76
+ }, [handleNetworkStateChange, mergedConfig.debug]);
77
+
78
+ return {
79
+ isOnline: store.isOnline,
80
+ isOffline: store.isOffline,
81
+ connectionType: store.connectionType,
82
+ isInternetReachable: store.isInternetReachable,
83
+ lastOnlineAt: store.lastOnlineAt,
84
+ lastOfflineAt: store.lastOfflineAt,
85
+ connectionQuality: store.connectionQuality,
86
+ hasConnection: store.isOnline,
87
+ hasInternet: store.isOnline && store.isInternetReachable !== false,
88
+ };
89
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * useOfflineState Hook
3
+ * Raw store access without NetInfo subscription
4
+ * Use this for selectors and performance optimization
5
+ */
6
+
7
+ import type { OfflineStore } from '../../types';
8
+ import { useOfflineStore } from '../../infrastructure/storage/OfflineStore';
9
+
10
+ export const useOfflineState = <T = OfflineStore>(
11
+ selector?: (state: OfflineStore) => T
12
+ ): T => {
13
+ const store = useOfflineStore();
14
+ return selector ? selector(store) : (store as T);
15
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * useOfflineWithMutations Hook
3
+ * Enhanced version that calls a callback when coming back online
4
+ * Useful for syncing data or resuming operations
5
+ */
6
+
7
+ import { useEffect, useRef, useCallback } from 'react';
8
+ import { useOffline } from './useOffline';
9
+
10
+ export const useOfflineWithMutations = (onOnline: () => Promise<void>) => {
11
+ const offlineState = useOffline();
12
+ const previousOnlineRef = useRef(offlineState.isOnline);
13
+ const isProcessingRef = useRef(false);
14
+
15
+ const handleOnlineCallback = useCallback(async () => {
16
+ if (isProcessingRef.current) return;
17
+
18
+ isProcessingRef.current = true;
19
+ try {
20
+ if (__DEV__) {
21
+ // eslint-disable-next-line no-console
22
+ console.log('[react-native-offline] Executing online callback');
23
+ }
24
+ await onOnline();
25
+ } catch (error) {
26
+ if (__DEV__) {
27
+ // eslint-disable-next-line no-console
28
+ console.error('[react-native-offline] Online callback failed:', error);
29
+ }
30
+ } finally {
31
+ isProcessingRef.current = false;
32
+ }
33
+ }, [onOnline]);
34
+
35
+ useEffect(() => {
36
+ const wasOffline = !previousOnlineRef.current;
37
+ const isNowOnline = offlineState.isOnline;
38
+
39
+ if (wasOffline && isNowOnline && !isProcessingRef.current) {
40
+ handleOnlineCallback();
41
+ }
42
+
43
+ previousOnlineRef.current = offlineState.isOnline;
44
+ }, [offlineState.isOnline, handleOnlineCallback]);
45
+
46
+ return offlineState;
47
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Network Provider
3
+ *
4
+ * Initializes network connectivity listener at app startup.
5
+ * Wrap your app with this provider to enable offline detection.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { NetworkProvider } from '@umituz/react-native-offline';
10
+ *
11
+ * const App = () => (
12
+ * <NetworkProvider>
13
+ * <YourApp />
14
+ * </NetworkProvider>
15
+ * );
16
+ * ```
17
+ */
18
+
19
+ import React, { ReactNode, memo } from 'react';
20
+ import { useOffline } from '../hooks/useOffline';
21
+
22
+ interface NetworkProviderProps {
23
+ children: ReactNode;
24
+ }
25
+
26
+ export const NetworkProvider: React.FC<NetworkProviderProps> = memo(({ children }) => {
27
+ // Initialize NetInfo listener - updates Zustand store
28
+ useOffline();
29
+
30
+ return <>{children}</>;
31
+ });
32
+
33
+ NetworkProvider.displayName = 'NetworkProvider';
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Type Definitions
3
+ * Network connectivity state types for React Native apps
4
+ */
5
+
6
+ /**
7
+ * Network state from expo-network
8
+ */
9
+ export interface NetworkState {
10
+ readonly type: string;
11
+ readonly isConnected: boolean | null;
12
+ readonly isInternetReachable: boolean | null;
13
+ readonly details: unknown;
14
+ }
15
+
16
+ /**
17
+ * Configuration for offline behavior
18
+ */
19
+ export interface OfflineConfig {
20
+ /** Enable persistent storage of network state */
21
+ persist?: boolean;
22
+ /** Enable debug logging */
23
+ debug?: boolean;
24
+ /** Health check interval in ms (0 = disabled) */
25
+ healthCheckInterval?: number;
26
+ /** Health check timeout in ms */
27
+ healthCheckTimeout?: number;
28
+ /** Health check URL to ping */
29
+ healthCheckUrl?: string;
30
+ }
31
+
32
+ /**
33
+ * Connection quality metrics
34
+ */
35
+ export interface ConnectionQuality {
36
+ readonly latency: number | null;
37
+ readonly effectiveType: '2g' | '3g' | '4g' | '5g' | 'unknown' | null;
38
+ readonly isSlow: boolean;
39
+ }
40
+
41
+ /**
42
+ * Offline state representation
43
+ */
44
+ export interface OfflineState {
45
+ readonly isOnline: boolean;
46
+ readonly isOffline: boolean;
47
+ readonly connectionType: string | null;
48
+ readonly isInternetReachable: boolean | null;
49
+ readonly lastOnlineAt: Date | null;
50
+ readonly lastOfflineAt: Date | null;
51
+ readonly connectionQuality: ConnectionQuality;
52
+ }
53
+
54
+ /**
55
+ * Offline store actions
56
+ */
57
+ export interface OfflineActions {
58
+ readonly updateNetworkState: (state: NetworkState) => void;
59
+ readonly setOnline: () => void;
60
+ readonly setOffline: () => void;
61
+ readonly reset: () => void;
62
+ }
63
+
64
+ /**
65
+ * Offline store with state and actions
66
+ */
67
+ export interface OfflineStore extends OfflineState, OfflineActions {}