more status indicator improvements

This commit is contained in:
stopflock
2025-08-26 19:37:27 -05:00
parent 84e057c986
commit d56a6e8e7c
4 changed files with 63 additions and 24 deletions
+1 -5
View File
@@ -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.");
}
}
+50 -4
View File
@@ -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
}
}
+5 -14
View File
@@ -48,20 +48,13 @@ class SimpleTileHttpClient extends http.BaseClient {
}
Future<http.StreamedResponse> _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(
+7 -1
View File
@@ -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) {