mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-05-22 08:17:23 +02:00
fix appstate repeated instantiation
This commit is contained in:
@@ -24,7 +24,9 @@ class AddCameraSession {
|
||||
|
||||
// ------------------ AppState ------------------
|
||||
class AppState extends ChangeNotifier {
|
||||
static late AppState instance;
|
||||
AppState() {
|
||||
instance = this;
|
||||
_init();
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class ProfileListSection extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
} else if (value == 'delete') {
|
||||
_showDeleteProfileDialog(context, appState, p);
|
||||
_showDeleteProfileDialog(context, p);
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -86,30 +86,31 @@ class ProfileListSection extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeleteProfileDialog(BuildContext context, AppState appState, CameraProfile profile) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Delete Profile'),
|
||||
content: Text('Are you sure you want to delete "${profile.name}"?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
appState.deleteProfile(profile);
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Profile deleted')),
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
void _showDeleteProfileDialog(BuildContext context, CameraProfile profile) {
|
||||
final appState = context.read<AppState>();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Delete Profile'),
|
||||
content: Text('Are you sure you want to delete "${profile.name}"?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
appState.deleteProfile(profile);
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Profile deleted')),
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class QueueSection extends StatelessWidget {
|
||||
? const Text('Sandbox mode – uploads go to OSM Sandbox')
|
||||
: const Text('Tap to view queue'),
|
||||
onTap: appState.pendingCount > 0
|
||||
? () => _showQueueDialog(context, appState)
|
||||
? () => _showQueueDialog(context)
|
||||
: null,
|
||||
),
|
||||
if (appState.pendingCount > 0)
|
||||
@@ -57,7 +57,8 @@ class QueueSection extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _showQueueDialog(BuildContext context, AppState appState) {
|
||||
void _showQueueDialog(BuildContext context) {
|
||||
final appState = context.read<AppState>();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
|
||||
@@ -21,11 +21,11 @@ class MapDataProvider {
|
||||
factory MapDataProvider() => _instance;
|
||||
MapDataProvider._();
|
||||
|
||||
AppState get _appState => AppState(); // Use singleton for now
|
||||
// REMOVED: AppState get _appState => AppState();
|
||||
|
||||
bool get isOfflineMode => _appState.offlineMode;
|
||||
bool get isOfflineMode => AppState.instance.offlineMode;
|
||||
void setOfflineMode(bool enabled) {
|
||||
_appState.setOfflineMode(enabled);
|
||||
AppState.instance.setOfflineMode(enabled);
|
||||
}
|
||||
|
||||
/// Fetch cameras from OSM/Overpass or local storage, depending on source/offline mode.
|
||||
@@ -35,9 +35,10 @@ class MapDataProvider {
|
||||
UploadMode uploadMode = UploadMode.production,
|
||||
MapSource source = MapSource.auto,
|
||||
}) async {
|
||||
print('[MapDataProvider] getCameras called, source=$source, offlineMode=$isOfflineMode');
|
||||
final offline = AppState.instance.offlineMode;
|
||||
print('[MapDataProvider] getCameras called, source=$source, offlineMode=$offline');
|
||||
// Resolve source:
|
||||
if (isOfflineMode && source != MapSource.local) {
|
||||
if (offline && source != MapSource.local) {
|
||||
print('[MapDataProvider] BLOCKED by offlineMode for getCameras');
|
||||
throw OfflineModeException("Cannot fetch remote cameras in offline mode.");
|
||||
}
|
||||
@@ -56,8 +57,9 @@ class MapDataProvider {
|
||||
required int y,
|
||||
MapSource source = MapSource.auto,
|
||||
}) async {
|
||||
print('[MapDataProvider] getTile called for $z/$x/$y, source=$source, offlineMode=$isOfflineMode');
|
||||
if (isOfflineMode && source != MapSource.local) {
|
||||
final offline = AppState.instance.offlineMode;
|
||||
print('[MapDataProvider] getTile called for $z/$x/$y, source=$source, offlineMode=$offline');
|
||||
if (offline && source != MapSource.local) {
|
||||
print('[MapDataProvider] BLOCKED by offlineMode for $z/$x/$y');
|
||||
throw OfflineModeException("Cannot fetch remote tiles in offline mode.");
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ class OfflineAreaService {
|
||||
if (!area.isPermanent) {
|
||||
final cameras = await camerasFromOverpass(
|
||||
bounds: bounds,
|
||||
profiles: AppState().enabledProfiles,
|
||||
profiles: AppState.instance.enabledProfiles,
|
||||
);
|
||||
area.cameras = cameras;
|
||||
await saveCameras(cameras, directory);
|
||||
|
||||
@@ -95,15 +95,16 @@ class _MapViewState extends State<MapView> {
|
||||
List<String> _lastProfileIds = [];
|
||||
UploadMode? _lastUploadMode;
|
||||
|
||||
void _maybeRefreshCameras(AppState appState) {
|
||||
void _maybeRefreshCameras() {
|
||||
final appState = context.read<AppState>();
|
||||
final currProfileIds = appState.enabledProfiles.map((p) => p.id).toList();
|
||||
final currMode = appState.uploadMode;
|
||||
if (_lastProfileIds.isEmpty ||
|
||||
if (_lastProfileIds.isEmpty ||
|
||||
currProfileIds.length != _lastProfileIds.length ||
|
||||
!_lastProfileIds.asMap().entries.every((entry) => currProfileIds[entry.key] == entry.value) ||
|
||||
_lastUploadMode != currMode) {
|
||||
// If this is first load, or list/ids/mode changed, refetch
|
||||
_debounce(() => _refreshCameras(appState));
|
||||
_debounce(_refreshCameras);
|
||||
_lastProfileIds = List.from(currProfileIds);
|
||||
_lastUploadMode = currMode;
|
||||
}
|
||||
@@ -149,7 +150,8 @@ class _MapViewState extends State<MapView> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _refreshCameras(AppState appState) async {
|
||||
Future<void> _refreshCameras() async {
|
||||
final appState = context.read<AppState>();
|
||||
LatLngBounds? bounds;
|
||||
try {
|
||||
bounds = _controller.camera.visibleBounds;
|
||||
@@ -181,7 +183,7 @@ class _MapViewState extends State<MapView> {
|
||||
// Refetch only if profiles or mode changed
|
||||
// This avoids repeated fetches on every build
|
||||
// We track last seen values (local to the State class)
|
||||
_maybeRefreshCameras(appState);
|
||||
_maybeRefreshCameras();
|
||||
|
||||
// Seed add‑mode target once, after first controller center is available.
|
||||
if (session != null && session.target == null) {
|
||||
@@ -235,7 +237,7 @@ class _MapViewState extends State<MapView> {
|
||||
if (session != null) {
|
||||
appState.updateSession(target: pos.center);
|
||||
}
|
||||
_debounce(() => _refreshCameras(appState));
|
||||
_debounce(_refreshCameras);
|
||||
},
|
||||
),
|
||||
children: [
|
||||
|
||||
@@ -4,13 +4,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import '../services/map_data_provider.dart';
|
||||
import '../app_state.dart';
|
||||
|
||||
/// Singleton in-memory tile cache and async provider for custom tiles.
|
||||
class TileProviderWithCache extends TileProvider {
|
||||
static final Map<String, Uint8List> _tileCache = {};
|
||||
static Map<String, Uint8List> get tileCache => _tileCache;
|
||||
final VoidCallback? onTileCacheUpdated;
|
||||
|
||||
TileProviderWithCache({this.onTileCacheUpdated});
|
||||
|
||||
@override
|
||||
@@ -29,7 +29,9 @@ class TileProviderWithCache extends TileProvider {
|
||||
// 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);
|
||||
final bytes = await MapDataProvider().getTile(
|
||||
z: coords.z, x: coords.x, y: coords.y,
|
||||
);
|
||||
if (bytes.isNotEmpty) {
|
||||
_tileCache[key] = Uint8List.fromList(bytes);
|
||||
print('[TileProviderWithCache] Cached tile $key, bytes=${bytes.length}');
|
||||
|
||||
Reference in New Issue
Block a user