mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-08-14 15:10:36 +02: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
|
_startUploader(); // Resume upload queue processing as we leave offline mode
|
||||||
} else {
|
} else {
|
||||||
_uploadQueueState.stopUploader(); // Stop uploader in offline mode
|
_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:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../app_state.dart';
|
import '../../app_state.dart';
|
||||||
|
import '../../services/offline_area_service.dart';
|
||||||
|
|
||||||
class OfflineModeSection extends StatelessWidget {
|
class OfflineModeSection extends StatelessWidget {
|
||||||
const OfflineModeSection({super.key});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final appState = context.watch<AppState>();
|
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.'),
|
subtitle: const Text('Disable all network requests except for local/offline areas.'),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
value: appState.offlineMode,
|
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 = [];
|
final List<OfflineArea> _areas = [];
|
||||||
List<OfflineArea> get offlineAreas => List.unmodifiable(_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)
|
/// Ensure the service is initialized (areas loaded from disk)
|
||||||
Future<void> ensureInitialized() async {
|
Future<void> ensureInitialized() async {
|
||||||
if (_initialized) return;
|
if (_initialized) return;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import '../widgets/tile_provider_with_cache.dart';
|
import '../widgets/tile_provider_with_cache.dart';
|
||||||
|
import '../widgets/camera_provider_with_cache.dart';
|
||||||
|
|
||||||
// Enum for upload mode (Production, OSM Sandbox, Simulate)
|
// Enum for upload mode (Production, OSM Sandbox, Simulate)
|
||||||
enum UploadMode { production, sandbox, simulate }
|
enum UploadMode { production, sandbox, simulate }
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import '../app_state.dart';
|
||||||
import '../dev_config.dart';
|
import '../dev_config.dart';
|
||||||
import '../services/offline_area_service.dart';
|
import '../services/offline_area_service.dart';
|
||||||
import '../services/offline_areas/offline_tile_utils.dart';
|
import '../services/offline_areas/offline_tile_utils.dart';
|
||||||
@@ -85,8 +87,10 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final appState = context.watch<AppState>();
|
||||||
final bounds = widget.controller.camera.visibleBounds;
|
final bounds = widget.controller.camera.visibleBounds;
|
||||||
final maxZoom = _zoom.toInt();
|
final maxZoom = _zoom.toInt();
|
||||||
|
final isOfflineMode = appState.offlineMode;
|
||||||
|
|
||||||
// Use the calculated max possible zoom instead of fixed span
|
// Use the calculated max possible zoom instead of fixed span
|
||||||
final sliderMin = _minZoom?.toDouble() ?? 12.0;
|
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'),
|
child: const Text('Cancel'),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: isOfflineMode ? null : () async {
|
||||||
try {
|
try {
|
||||||
final id = DateTime.now().toIso8601String().replaceAll(':', '-');
|
final id = DateTime.now().toIso8601String().replaceAll(':', '-');
|
||||||
final appDocDir = await OfflineAreaService().getOfflineAreaDir();
|
final appDocDir = await OfflineAreaService().getOfflineAreaDir();
|
||||||
|
|||||||
@@ -5,16 +5,25 @@ import 'package:flutter/services.dart';
|
|||||||
import '../services/map_data_provider.dart';
|
import '../services/map_data_provider.dart';
|
||||||
import '../app_state.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 {
|
class TileProviderWithCache extends TileProvider with ChangeNotifier {
|
||||||
static final Map<String, Uint8List> _tileCache = {};
|
static final Map<String, Uint8List> _tileCache = {};
|
||||||
static Map<String, Uint8List> get tileCache => _tileCache;
|
static Map<String, Uint8List> get tileCache => _tileCache;
|
||||||
|
|
||||||
|
bool _disposed = false;
|
||||||
|
|
||||||
TileProviderWithCache();
|
TileProviderWithCache();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_disposed = true;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImageProvider getImage(TileCoordinates coords, TileLayer options, {MapSource source = MapSource.auto}) {
|
ImageProvider getImage(TileCoordinates coords, TileLayer options, {MapSource source = MapSource.auto}) {
|
||||||
final key = '${coords.z}/${coords.x}/${coords.y}';
|
final key = '${coords.z}/${coords.x}/${coords.y}';
|
||||||
|
|
||||||
if (_tileCache.containsKey(key)) {
|
if (_tileCache.containsKey(key)) {
|
||||||
final bytes = _tileCache[key]!;
|
final bytes = _tileCache[key]!;
|
||||||
return MemoryImage(bytes);
|
return MemoryImage(bytes);
|
||||||
@@ -33,6 +42,7 @@ class TileProviderWithCache extends TileProvider with ChangeNotifier {
|
|||||||
void _fetchAndCacheTile(TileCoordinates coords, String key, {MapSource source = MapSource.auto}) async {
|
void _fetchAndCacheTile(TileCoordinates coords, String key, {MapSource source = MapSource.auto}) async {
|
||||||
// Don't fire multiple fetches for the same tile simultaneously
|
// Don't fire multiple fetches for the same tile simultaneously
|
||||||
if (_tileCache.containsKey(key)) return;
|
if (_tileCache.containsKey(key)) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final bytes = await MapDataProvider().getTile(
|
final bytes = await MapDataProvider().getTile(
|
||||||
z: coords.z, x: coords.x, y: coords.y, source: source,
|
z: coords.z, x: coords.x, y: coords.y, source: source,
|
||||||
@@ -40,7 +50,10 @@ class TileProviderWithCache extends TileProvider with ChangeNotifier {
|
|||||||
if (bytes.isNotEmpty) {
|
if (bytes.isNotEmpty) {
|
||||||
_tileCache[key] = Uint8List.fromList(bytes);
|
_tileCache[key] = Uint8List.fromList(bytes);
|
||||||
print('[TileProviderWithCache] Cached tile $key, bytes=${bytes.length}');
|
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)
|
// If bytes were empty, don't cache (will re-attempt next time)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user