@tanstack/offline-transactions
Version:
Offline-first transaction capabilities for TanStack DB
126 lines (103 loc) • 3.12 kB
text/typescript
import NetInfo from '@react-native-community/netinfo'
import { AppState } from 'react-native'
import type { AppStateStatus, NativeEventSubscription } from 'react-native'
import type { OnlineDetector } from '../types'
/**
* React Native online detector that uses RN APIs.
* Listens for:
* - Network connectivity changes via `@react-native-community/netinfo`
* - App state changes (foreground/background) via `AppState`
*/
export class ReactNativeOnlineDetector implements OnlineDetector {
private listeners: Set<() => void> = new Set()
private netInfoUnsubscribe: (() => void) | null = null
private appStateSubscription: NativeEventSubscription | null = null
private isListening = false
private wasConnected = true
constructor() {
this.startListening()
}
private startListening(): void {
if (this.isListening) {
return
}
this.isListening = true
if (typeof NetInfo.fetch === `function`) {
void NetInfo.fetch()
.then((state) => {
this.wasConnected = this.toConnectivityState(state)
})
.catch(() => {
// Ignore initial fetch failures and rely on subscription updates.
})
}
// Subscribe to network state changes
this.netInfoUnsubscribe = NetInfo.addEventListener((state) => {
const isConnected = this.toConnectivityState(state)
// Only notify when transitioning to online
if (isConnected && !this.wasConnected) {
this.notifyListeners()
}
this.wasConnected = isConnected
})
// Subscribe to app state changes (foreground/background)
this.appStateSubscription = AppState.addEventListener(
`change`,
this.handleAppStateChange,
)
}
private handleAppStateChange = (nextState: AppStateStatus): void => {
// Notify when app becomes active (foreground)
if (nextState === `active`) {
this.notifyListeners()
}
}
private stopListening(): void {
if (!this.isListening) {
return
}
this.isListening = false
if (this.netInfoUnsubscribe) {
this.netInfoUnsubscribe()
this.netInfoUnsubscribe = null
}
if (this.appStateSubscription) {
this.appStateSubscription.remove()
this.appStateSubscription = null
}
}
private notifyListeners(): void {
for (const listener of this.listeners) {
try {
listener()
} catch (error) {
console.warn(`ReactNativeOnlineDetector listener error:`, error)
}
}
}
subscribe(callback: () => void): () => void {
this.listeners.add(callback)
return () => {
this.listeners.delete(callback)
if (this.listeners.size === 0) {
this.stopListening()
}
}
}
notifyOnline(): void {
this.notifyListeners()
}
isOnline(): boolean {
return this.wasConnected
}
dispose(): void {
this.stopListening()
this.listeners.clear()
}
private toConnectivityState(state: {
isConnected: boolean | null
isInternetReachable: boolean | null
}): boolean {
return !!state.isConnected && state.isInternetReachable !== false
}
}