Skip to content
Open
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
24 changes: 22 additions & 2 deletions lib/src/sdk/frappe_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,25 @@ class FrappeSDK {
_modeNotifier?.value = next;
}

FrappeSDK({required this.baseUrl, this.databaseAppName});
FrappeSDK({
required this.baseUrl,
this.databaseAppName,
this.isOnlineOverride,
});

/// Optional override for the SDK's connectivity probe (see
/// [SyncService.isOnline]). When set, the platform's
/// `connectivity_plus` check is replaced — the override decides whether
/// `pullSync`/`pullSyncMany`/`pushSync` see the device as online.
///
/// Production wiring should leave this null. The legitimate dev use is
/// the emulator+`adb reverse` workflow, where the platform classifies
/// the network as `none` despite HTTP working fine. Setting
/// `isOnlineOverride: () async => true` in dev keeps sync running.
///
/// If the override throws, [SyncService.isOnline] falls back to the
/// platform probe.
final Future<bool> Function()? isOnlineOverride;

/// Test-only constructor: accepts a pre-built [AppDatabase] (e.g. in-memory).
/// Wires all services directly without calling [initialize()].
Expand All @@ -157,7 +175,8 @@ class FrappeSDK {
enabled: true,
isPersisted: true,
),
}) : databaseAppName = null {
}) : databaseAppName = null,
isOnlineOverride = null {
_database = database;
_modeNotifier = OfflineModeNotifier(offlineMode);
_syncCompleteController = StreamController<void>.broadcast();
Expand Down Expand Up @@ -341,6 +360,7 @@ class FrappeSDK {
getMobileUuid: _resolveMobileUuid,
offlineModeNotifier: _modeNotifier!,
pushRunner: () => _pushEngine!.runOnce(),
isOnlineOverride: isOnlineOverride,
);
// Build UnifiedResolver — single read path for all offline queries.
// Probe connectivity once here AND subscribe to the platform
Expand Down
32 changes: 31 additions & 1 deletion lib/src/services/sync_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,45 @@ class SyncService {
),
OfflineModeNotifier? offlineModeNotifier,
Future<void> Function()? pushRunner,
Future<bool> Function()? isOnlineOverride,
}) : _getMobileUuid = getMobileUuid,
_modeNotifier = offlineModeNotifier ?? OfflineModeNotifier(offlineMode),
_pushRunner = pushRunner;
_pushRunner = pushRunner,
_isOnlineOverride = isOnlineOverride;

/// Optional override for the connectivity probe.
///
/// When supplied, [isOnline] calls this instead of `connectivity_plus`.
/// The override returning `true` lets [pullSync] / [pullSyncMany] /
/// [pushSync] proceed past their connectivity guard even on transports
/// the platform plugin doesn't classify as `mobile | wifi | ethernet`
/// (notably `adb reverse` tunnels during emulator development, where
/// the link reads as `none` despite HTTP being fully functional).
///
/// Production callers should leave this null and rely on the platform
/// probe. Dev/test wiring is the legitimate use case.
///
/// If the override throws, [isOnline] logs the failure and falls
/// through to the platform probe — a buggy override won't brick sync.
final Future<bool> Function()? _isOnlineOverride;

/// Check if device is online.
/// Returns false when offline mode is disabled, regardless of connectivity,
/// so callers can use this method without a separate mode guard.
Future<bool> isOnline() async {
if (!offlineMode.enabled) return false;
final override = _isOnlineOverride;
if (override != null) {
try {
return await override();
} catch (e, st) {
// ignore: avoid_print
print(
'SyncService.isOnline: isOnlineOverride threw, '
'falling back to platform probe — $e\n$st',
);
}
}
final connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult.contains(ConnectivityResult.mobile) ||
connectivityResult.contains(ConnectivityResult.wifi) ||
Expand Down
Loading