diff --git a/lib/services/map_data_provider.dart b/lib/services/map_data_provider.dart index 6a7f620..8f76a91 100644 --- a/lib/services/map_data_provider.dart +++ b/lib/services/map_data_provider.dart @@ -136,15 +136,11 @@ class MapDataProvider { // AUTO (default): try local first, then remote if not offline try { - debugPrint('[MapDataProvider] Trying local tile $z/$x/$y (offline: $offline)'); return await fetchLocalTile(z: z, x: x, y: y); - } catch (e) { - debugPrint('[MapDataProvider] Local tile $z/$x/$y failed: $e'); + } catch (_) { if (!offline) { - debugPrint('[MapDataProvider] Falling back to remote for $z/$x/$y'); return _fetchRemoteTileFromCurrentProvider(z, x, y); } else { - debugPrint('[MapDataProvider] Offline mode - no fallback for $z/$x/$y'); throw OfflineModeException("Tile $z/$x/$y not found in offline areas and offline mode is enabled."); } } diff --git a/lib/services/network_status.dart b/lib/services/network_status.dart index 2f5ea23..65e7dec 100644 --- a/lib/services/network_status.dart +++ b/lib/services/network_status.dart @@ -4,7 +4,7 @@ import 'dart:async'; import '../app_state.dart'; enum NetworkIssueType { osmTiles, overpassApi, both } -enum NetworkStatusType { waiting, issues, timedOut, noData, ready } +enum NetworkStatusType { waiting, issues, timedOut, noData, ready, success } class NetworkStatus extends ChangeNotifier { static final NetworkStatus instance = NetworkStatus._(); @@ -15,11 +15,13 @@ class NetworkStatus extends ChangeNotifier { bool _isWaitingForData = false; bool _isTimedOut = false; bool _hasNoData = false; + bool _hasSuccess = false; int _recentOfflineMisses = 0; Timer? _osmRecoveryTimer; Timer? _overpassRecoveryTimer; Timer? _waitingTimer; Timer? _noDataResetTimer; + Timer? _successResetTimer; // Getters bool get hasAnyIssues => _osmTilesHaveIssues || _overpassHasIssues; @@ -28,12 +30,14 @@ class NetworkStatus extends ChangeNotifier { bool get isWaitingForData => _isWaitingForData; bool get isTimedOut => _isTimedOut; bool get hasNoData => _hasNoData; + bool get hasSuccess => _hasSuccess; NetworkStatusType get currentStatus { if (hasAnyIssues) return NetworkStatusType.issues; if (_isWaitingForData) return NetworkStatusType.waiting; if (_isTimedOut) return NetworkStatusType.timedOut; if (_hasNoData) return NetworkStatusType.noData; + if (_hasSuccess) return NetworkStatusType.success; return NetworkStatusType.ready; } @@ -122,17 +126,59 @@ class NetworkStatus extends ChangeNotifier { }); } - /// Clear waiting/timeout/no-data status when data arrives + /// Show success status briefly when data loads + void setSuccess() { + _isWaitingForData = false; + _isTimedOut = false; + _hasNoData = false; + _hasSuccess = true; + _recentOfflineMisses = 0; + _waitingTimer?.cancel(); + _noDataResetTimer?.cancel(); + notifyListeners(); + + // Auto-clear success status after 2 seconds + _successResetTimer?.cancel(); + _successResetTimer = Timer(const Duration(seconds: 2), () { + if (_hasSuccess) { + _hasSuccess = false; + notifyListeners(); + } + }); + } + + /// Show no-data status briefly when tiles aren't available + void setNoData() { + _isWaitingForData = false; + _isTimedOut = false; + _hasSuccess = false; + _hasNoData = true; + _waitingTimer?.cancel(); + _successResetTimer?.cancel(); + notifyListeners(); + + // Auto-clear no-data status after 2 seconds + _noDataResetTimer?.cancel(); + _noDataResetTimer = Timer(const Duration(seconds: 2), () { + if (_hasNoData) { + _hasNoData = false; + notifyListeners(); + } + }); + } + + /// Clear waiting/timeout/no-data status (legacy method for compatibility) void clearWaiting() { - if (_isWaitingForData || _isTimedOut || _hasNoData) { + if (_isWaitingForData || _isTimedOut || _hasNoData || _hasSuccess) { _isWaitingForData = false; _isTimedOut = false; _hasNoData = false; + _hasSuccess = false; _recentOfflineMisses = 0; _waitingTimer?.cancel(); _noDataResetTimer?.cancel(); + _successResetTimer?.cancel(); notifyListeners(); - // Quietly clear waiting status - don't log routine data arrival } } diff --git a/lib/services/simple_tile_service.dart b/lib/services/simple_tile_service.dart index 5774abe..ef49d77 100644 --- a/lib/services/simple_tile_service.dart +++ b/lib/services/simple_tile_service.dart @@ -48,20 +48,13 @@ class SimpleTileHttpClient extends http.BaseClient { } Future _handleTileRequest(int z, int x, int y) async { - final startTime = DateTime.now(); - final isOffline = AppState.instance.offlineMode; - debugPrint('[SimpleTileService] Requesting tile $z/$x/$y (offline: $isOffline)'); - try { // Always go through MapDataProvider - it handles offline/online routing // MapDataProvider will get current provider from AppState final tileBytes = await _mapDataProvider.getTile(z: z, x: x, y: y, source: MapSource.auto); - final duration = DateTime.now().difference(startTime); - debugPrint('[SimpleTileService] SUCCESS tile $z/$x/$y in ${duration.inMilliseconds}ms'); - - // Clear waiting status - we got data - NetworkStatus.instance.clearWaiting(); + // Show success status briefly + NetworkStatus.instance.setSuccess(); // Serve tile with proper cache headers return http.StreamedResponse( @@ -76,12 +69,10 @@ class SimpleTileHttpClient extends http.BaseClient { ); } catch (e) { - final duration = DateTime.now().difference(startTime); - debugPrint('[SimpleTileService] FAILED tile $z/$x/$y in ${duration.inMilliseconds}ms: $e'); + debugPrint('[SimpleTileService] Could not get tile $z/$x/$y: $e'); - // 404 means no tiles available - clear waiting status - // This is true whether we're online or offline - NetworkStatus.instance.clearWaiting(); + // 404 means no tiles available - show "no data" status briefly + NetworkStatus.instance.setNoData(); // Return 404 and let flutter_map handle it gracefully return http.StreamedResponse( diff --git a/lib/widgets/network_status_indicator.dart b/lib/widgets/network_status_indicator.dart index 5c89cf7..66ef9a9 100644 --- a/lib/widgets/network_status_indicator.dart +++ b/lib/widgets/network_status_indicator.dart @@ -29,10 +29,16 @@ class NetworkStatusIndicator extends StatelessWidget { break; case NetworkStatusType.noData: - message = 'No offline data'; + message = 'No tiles here'; icon = Icons.cloud_off; color = Colors.grey; break; + + case NetworkStatusType.success: + message = 'Tiles loaded'; + icon = Icons.check_circle; + color = Colors.green; + break; case NetworkStatusType.issues: switch (networkStatus.currentIssueType) {