diff --git a/lib/providers/app_state_provider.dart b/lib/providers/app_state_provider.dart index 059dda5..c903f00 100644 --- a/lib/providers/app_state_provider.dart +++ b/lib/providers/app_state_provider.dart @@ -5100,6 +5100,7 @@ class AppStateProvider extends ChangeNotifier with WidgetsBindingObserver { // Extract session_id into local variable — never stored in shared state final offlineSessionId = effectiveAuth!['session_id'] as String?; + final uploadPublicKey = publicKey!; if (offlineSessionId == null) { debugError('[OFFLINE] Auth succeeded but no session_id in response'); return OfflineUploadResult.authFailed; @@ -5179,6 +5180,12 @@ class AppStateProvider extends ChangeNotifier with WidgetsBindingObserver { if (result == UploadResult.success) { uploadedCount += batch.length; debugLog('[OFFLINE] Uploaded batch $batchNum: ${batch.length} pings'); + _forwardOfflineBatchToCustomApi( + batch, + batchNum: batchNum, + publicKey: uploadPublicKey, + auth: effectiveAuth, + ); continue; } @@ -5234,6 +5241,32 @@ class AppStateProvider extends ChangeNotifier with WidgetsBindingObserver { } } + /// Forward a successfully uploaded offline batch to the Custom API endpoint. + void _forwardOfflineBatchToCustomApi( + List> batch, { + required int batchNum, + required String publicKey, + required Map auth, + }) { + final contact = publicKey.length >= 8 + ? publicKey.substring(0, 8).toUpperCase() + : null; + var iata = zoneCode ?? _preferences.iataCode; + final zone = auth['zone']; + if (zone is Map && zone['code'] != null) { + iata = zone['code'].toString(); + } + debugLog( + '[OFFLINE] Custom API forward for batch $batchNum ' + '(${batch.length} pings, contact=$contact, iata=$iata)'); + _customApiService.forwardPings( + batch, + contactOverride: contact, + iataOverride: iata, + source: 'offline', + ); + } + /// Delete an offline session without uploading Future deleteOfflineSession(String filename) async { await _offlineSessionService.deleteSession(filename); diff --git a/lib/services/custom_api_service.dart b/lib/services/custom_api_service.dart index f5ba0c7..4615921 100644 --- a/lib/services/custom_api_service.dart +++ b/lib/services/custom_api_service.dart @@ -41,17 +41,42 @@ class CustomApiService { _client = client ?? http.Client(); /// Fire-and-forget: forward pings to the custom endpoint. - /// Called after successful MeshMapper upload. Never throws. - void forwardPings(List> pings) { + /// Called after successful MeshMapper upload (online queue or offline session). + /// Never throws. + void forwardPings( + List> pings, { + String? contactOverride, + String? iataOverride, + String source = 'online', + }) { + if (pings.isEmpty) return; + final prefs = _prefsGetter(); - if (!prefs.customApiEnabled) return; - if (prefs.customApiUrl == null || prefs.customApiUrl!.isEmpty) return; - if (prefs.customApiKey == null || prefs.customApiKey!.isEmpty) return; + debugLog( + '[CUSTOM API] Forward requested ($source): ${pings.length} item(s)'); + + if (!prefs.customApiEnabled) { + debugLog('[CUSTOM API] Forward skipped ($source): Custom API disabled'); + return; + } + if (prefs.customApiUrl == null || prefs.customApiUrl!.isEmpty) { + debugLog('[CUSTOM API] Forward skipped ($source): URL not configured'); + return; + } + if (prefs.customApiKey == null || prefs.customApiKey!.isEmpty) { + debugLog('[CUSTOM API] Forward skipped ($source): API key not configured'); + return; + } // Enrich with contact and iata (custom API only — never sent to MeshMapper) - final contact = - prefs.customApiIncludeContact ? contactGetter?.call() : null; - final iata = iataGetter?.call(); + final contact = prefs.customApiIncludeContact + ? (contactOverride ?? contactGetter?.call()) + : null; + final iata = iataOverride ?? iataGetter?.call(); + + debugLog( + '[CUSTOM API] Forwarding ($source) → ${prefs.customApiUrl} ' + '(contact=${contact ?? "off"}, iata=${iata ?? "none"})'); final enriched = pings.map((ping) { final enrichedPing = Map.from(ping);