mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-02-12 16:52:51 +00:00
ho lee shet
This commit is contained in:
@@ -161,6 +161,8 @@ class AppState extends ChangeNotifier {
|
||||
_startUploader(); // Resume upload queue processing as we leave offline mode
|
||||
} else {
|
||||
_uploadQueueState.stopUploader(); // Stop uploader in offline mode
|
||||
// Cancel any active area downloads
|
||||
await OfflineAreaService().cancelActiveDownloads();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../app_state.dart';
|
||||
import '../../services/offline_area_service.dart';
|
||||
|
||||
class OfflineModeSection extends StatelessWidget {
|
||||
const OfflineModeSection({super.key});
|
||||
|
||||
Future<void> _handleOfflineModeChange(BuildContext context, AppState appState, bool value) async {
|
||||
// If enabling offline mode, check for active downloads
|
||||
if (value && !appState.offlineMode) {
|
||||
final offlineService = OfflineAreaService();
|
||||
if (offlineService.hasActiveDownloads) {
|
||||
// Show confirmation dialog
|
||||
final shouldProceed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Row(
|
||||
children: const [
|
||||
Icon(Icons.warning, color: Colors.orange),
|
||||
SizedBox(width: 8),
|
||||
Text('Active Downloads'),
|
||||
],
|
||||
),
|
||||
content: const Text(
|
||||
'Enabling offline mode will cancel any active area downloads. Do you want to continue?',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: const Text('Enable Offline Mode'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (shouldProceed != true) {
|
||||
return; // User cancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with the change
|
||||
await appState.setOfflineMode(value);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appState = context.watch<AppState>();
|
||||
@@ -14,7 +61,7 @@ class OfflineModeSection extends StatelessWidget {
|
||||
subtitle: const Text('Disable all network requests except for local/offline areas.'),
|
||||
trailing: Switch(
|
||||
value: appState.offlineMode,
|
||||
onChanged: (value) async => await appState.setOfflineMode(value),
|
||||
onChanged: (value) => _handleOfflineModeChange(context, appState, value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,27 @@ class OfflineAreaService {
|
||||
final List<OfflineArea> _areas = [];
|
||||
List<OfflineArea> get offlineAreas => List.unmodifiable(_areas);
|
||||
|
||||
/// Check if any areas are currently downloading
|
||||
bool get hasActiveDownloads => _areas.any((area) => area.status == OfflineAreaStatus.downloading);
|
||||
|
||||
/// Cancel all active downloads (used when enabling offline mode)
|
||||
Future<void> cancelActiveDownloads() async {
|
||||
final activeAreas = _areas.where((area) => area.status == OfflineAreaStatus.downloading).toList();
|
||||
for (final area in activeAreas) {
|
||||
area.status = OfflineAreaStatus.cancelled;
|
||||
if (!area.isPermanent) {
|
||||
// Clean up non-permanent areas
|
||||
final dir = Directory(area.directory);
|
||||
if (await dir.exists()) {
|
||||
await dir.delete(recursive: true);
|
||||
}
|
||||
_areas.remove(area);
|
||||
}
|
||||
}
|
||||
await saveAreasToDisk();
|
||||
debugPrint('OfflineAreaService: Cancelled ${activeAreas.length} active downloads due to offline mode');
|
||||
}
|
||||
|
||||
/// Ensure the service is initialized (areas loaded from disk)
|
||||
Future<void> ensureInitialized() async {
|
||||
if (_initialized) return;
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../widgets/tile_provider_with_cache.dart';
|
||||
import '../widgets/camera_provider_with_cache.dart';
|
||||
|
||||
// Enum for upload mode (Production, OSM Sandbox, Simulate)
|
||||
enum UploadMode { production, sandbox, simulate }
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import '../app_state.dart';
|
||||
import '../dev_config.dart';
|
||||
import '../services/offline_area_service.dart';
|
||||
import '../services/offline_areas/offline_tile_utils.dart';
|
||||
@@ -85,8 +87,10 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appState = context.watch<AppState>();
|
||||
final bounds = widget.controller.camera.visibleBounds;
|
||||
final maxZoom = _zoom.toInt();
|
||||
final isOfflineMode = appState.offlineMode;
|
||||
|
||||
// Use the calculated max possible zoom instead of fixed span
|
||||
final sliderMin = _minZoom?.toDouble() ?? 12.0;
|
||||
@@ -190,6 +194,33 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isOfflineMode)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.orange.withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.wifi_off, color: Colors.orange[700], size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Downloads disabled while in offline mode. Disable offline mode to download new areas.',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.orange[700],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -199,7 +230,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
onPressed: isOfflineMode ? null : () async {
|
||||
try {
|
||||
final id = DateTime.now().toIso8601String().replaceAll(':', '-');
|
||||
final appDocDir = await OfflineAreaService().getOfflineAreaDir();
|
||||
|
||||
@@ -5,16 +5,25 @@ import 'package:flutter/services.dart';
|
||||
import '../services/map_data_provider.dart';
|
||||
import '../app_state.dart';
|
||||
|
||||
/// Singleton in-memory tile cache and async provider for custom tiles.
|
||||
/// In-memory tile cache and async provider for custom tiles.
|
||||
class TileProviderWithCache extends TileProvider with ChangeNotifier {
|
||||
static final Map<String, Uint8List> _tileCache = {};
|
||||
static Map<String, Uint8List> get tileCache => _tileCache;
|
||||
|
||||
bool _disposed = false;
|
||||
|
||||
TileProviderWithCache();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
ImageProvider getImage(TileCoordinates coords, TileLayer options, {MapSource source = MapSource.auto}) {
|
||||
final key = '${coords.z}/${coords.x}/${coords.y}';
|
||||
|
||||
if (_tileCache.containsKey(key)) {
|
||||
final bytes = _tileCache[key]!;
|
||||
return MemoryImage(bytes);
|
||||
@@ -33,6 +42,7 @@ class TileProviderWithCache extends TileProvider with ChangeNotifier {
|
||||
void _fetchAndCacheTile(TileCoordinates coords, String key, {MapSource source = MapSource.auto}) async {
|
||||
// Don't fire multiple fetches for the same tile simultaneously
|
||||
if (_tileCache.containsKey(key)) return;
|
||||
|
||||
try {
|
||||
final bytes = await MapDataProvider().getTile(
|
||||
z: coords.z, x: coords.x, y: coords.y, source: source,
|
||||
@@ -40,7 +50,10 @@ class TileProviderWithCache extends TileProvider with ChangeNotifier {
|
||||
if (bytes.isNotEmpty) {
|
||||
_tileCache[key] = Uint8List.fromList(bytes);
|
||||
print('[TileProviderWithCache] Cached tile $key, bytes=${bytes.length}');
|
||||
notifyListeners(); // This updates any listening widgets
|
||||
// Only notify listeners if not disposed
|
||||
if (!_disposed) {
|
||||
notifyListeners(); // This updates any listening widgets
|
||||
}
|
||||
}
|
||||
// If bytes were empty, don't cache (will re-attempt next time)
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user