More camera -> node

This commit is contained in:
stopflock
2025-12-02 21:17:07 -06:00
parent 3d5edf320e
commit bb3d398c9c
10 changed files with 91 additions and 72 deletions

View File

@@ -10,6 +10,14 @@ Successfully refactored the largest file in the codebase (MapView, 880 lines) by
- **Total new code**: 4 new focused manager classes (351 lines total)
- **Net complexity reduction**: Converted monolithic widget into clean orchestrator + specialized managers
### Step 1.5: Terminology Update (Camera → Node)
- **Renamed 3 core files** to use "node" instead of "camera" terminology
- **Updated all class names** to reflect current multi-device scope (not just cameras)
- **Updated all method names** and comments for consistency
- **Updated all imports/references** across the entire codebase
- **Benefits**: Consistent terminology that reflects the app's expansion beyond just cameras to all surveillance devices
=======
### New Manager Classes Created
#### 1. MapDataManager (`lib/widgets/map/map_data_manager.dart`) - 92 lines
@@ -158,9 +166,20 @@ Expected reduction: ~400-500 lines
- `lib/widgets/map/map_interaction_manager.dart`
- `lib/widgets/map/marker_layer_builder.dart`
- `lib/widgets/map/overlay_layer_builder.dart`
- `lib/widgets/node_provider_with_cache.dart` (renamed from camera_provider_with_cache.dart)
- `lib/widgets/map/node_refresh_controller.dart` (renamed from camera_refresh_controller.dart)
- `lib/widgets/map/node_markers.dart` (renamed from camera_markers.dart)
### Modified Files
- `lib/widgets/map_view.dart` (880 → 572 lines)
- `lib/app_state.dart` (updated imports and references)
- `lib/state/upload_queue_state.dart` (updated all references)
- `lib/services/prefetch_area_service.dart` (updated references)
### Removed Files
- `lib/widgets/camera_provider_with_cache.dart` (renamed to node_provider_with_cache.dart)
- `lib/widgets/map/camera_refresh_controller.dart` (renamed to node_refresh_controller.dart)
- `lib/widgets/map/camera_markers.dart` (renamed to node_markers.dart)
### Total Impact
- **Lines removed**: 308 from MapView

View File

@@ -18,7 +18,7 @@ import 'services/node_cache.dart';
import 'services/tile_preview_service.dart';
import 'services/changelog_service.dart';
import 'services/operator_profile_service.dart';
import 'widgets/camera_provider_with_cache.dart';
import 'widgets/node_provider_with_cache.dart';
import 'services/profile_service.dart';
import 'widgets/proximity_warning_dialog.dart';
import 'widgets/reauth_messages_dialog.dart';
@@ -214,7 +214,7 @@ class AppState extends ChangeNotifier {
await _authState.init(_settingsState.uploadMode);
// Set up callback to repopulate pending nodes after cache clears
CameraProviderWithCache.instance.setOnCacheClearedCallback(() {
NodeProviderWithCache.instance.setOnCacheClearedCallback(() {
_uploadQueueState.repopulateCacheFromQueue();
});

View File

@@ -10,7 +10,7 @@ import '../dev_config.dart';
import 'map_data_submodules/nodes_from_overpass.dart';
import 'node_cache.dart';
import 'network_status.dart';
import '../widgets/camera_provider_with_cache.dart';
import '../widgets/node_provider_with_cache.dart';
/// Manages pre-fetching larger areas to reduce Overpass API calls.
/// Uses zoom level 10 areas and automatically splits if hitting node limits.
@@ -143,7 +143,7 @@ class PrefetchAreaService {
// We just need to handle the successful result here
// Notify UI that cache has been updated with fresh data
CameraProviderWithCache.instance.refreshDisplay();
NodeProviderWithCache.instance.refreshDisplay();
} catch (e) {
debugPrint('[PrefetchAreaService] Pre-fetch failed: $e');

View File

@@ -9,7 +9,7 @@ import '../models/osm_node.dart';
import '../models/node_profile.dart';
import '../services/node_cache.dart';
import '../services/uploader.dart';
import '../widgets/camera_provider_with_cache.dart';
import '../widgets/node_provider_with_cache.dart';
import 'settings_state.dart';
import 'session_state.dart';
@@ -113,7 +113,7 @@ class UploadQueueState extends ChangeNotifier {
_saveQueue();
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
}
}
@@ -150,7 +150,7 @@ class UploadQueueState extends ChangeNotifier {
NodeCache.instance.addOrUpdate([tempNode]);
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
notifyListeners();
}
@@ -238,7 +238,7 @@ class UploadQueueState extends ChangeNotifier {
NodeCache.instance.addOrUpdate([originalNode, editedNode]);
}
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
notifyListeners();
}
@@ -269,7 +269,7 @@ class UploadQueueState extends ChangeNotifier {
NodeCache.instance.addOrUpdate([nodeWithDeletionTag]);
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
notifyListeners();
}
@@ -284,7 +284,7 @@ class UploadQueueState extends ChangeNotifier {
_saveQueue();
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
notifyListeners();
}
@@ -296,7 +296,7 @@ class UploadQueueState extends ChangeNotifier {
_saveQueue();
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
notifyListeners();
}
@@ -665,7 +665,7 @@ class UploadQueueState extends ChangeNotifier {
}
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
}
// Handle successful deletion by removing the node from cache
@@ -675,7 +675,7 @@ class UploadQueueState extends ChangeNotifier {
NodeCache.instance.removeNodeById(item.originalNodeId!);
// Notify node provider to update the map
CameraProviderWithCache.instance.notifyListeners();
NodeProviderWithCache.instance.notifyListeners();
}
}

View File

@@ -5,7 +5,7 @@ import 'package:latlong2/latlong.dart';
import '../../models/osm_node.dart';
import '../../app_state.dart';
import '../camera_provider_with_cache.dart';
import '../node_provider_with_cache.dart';
import '../../dev_config.dart';
/// Manages data fetching, filtering, and node limit logic for the map.
@@ -41,7 +41,7 @@ class MapDataManager {
if (currentZoom >= minZoom) {
// Above minimum zoom - get cached nodes directly (no Provider needed)
allNodes = (mapBounds != null)
? CameraProviderWithCache.instance.getCachedNodesForBounds(mapBounds)
? NodeProviderWithCache.instance.getCachedNodesForBounds(mapBounds)
: <OsmNode>[];
// Filter out invalid coordinates before applying limit

View File

@@ -10,7 +10,7 @@ import '../../state/session_state.dart';
import '../../dev_config.dart';
import '../camera_icon.dart';
import '../provisional_pin.dart';
import 'camera_markers.dart';
import 'node_markers.dart';
import 'suspected_location_markers.dart';
/// Enumeration for different pin types in navigation
@@ -65,8 +65,8 @@ class MarkerLayerBuilder {
// Determine if we should dim node markers (when suspected location is selected)
final shouldDimNodes = appState.selectedSuspectedLocation != null;
final markers = CameraMarkersBuilder.buildCameraMarkers(
cameras: nodesToRender,
final markers = NodeMarkersBuilder.buildNodeMarkers(
nodes: nodesToRender,
mapController: mapController.mapController,
userLocation: userLocation,
selectedNodeId: selectedNodeId,

View File

@@ -8,13 +8,13 @@ import '../../models/osm_node.dart';
import '../node_tag_sheet.dart';
import '../camera_icon.dart';
/// Smart marker widget for camera with single/double tap distinction
class CameraMapMarker extends StatefulWidget {
/// Smart marker widget for surveillance node with single/double tap distinction
class NodeMapMarker extends StatefulWidget {
final OsmNode node;
final MapController mapController;
final void Function(OsmNode)? onNodeTap;
const CameraMapMarker({
const NodeMapMarker({
required this.node,
required this.mapController,
this.onNodeTap,
@@ -22,10 +22,10 @@ class CameraMapMarker extends StatefulWidget {
}) : super(key: key);
@override
State<CameraMapMarker> createState() => _CameraMapMarkerState();
State<NodeMapMarker> createState() => _NodeMapMarkerState();
}
class _CameraMapMarkerState extends State<CameraMapMarker> {
class _NodeMapMarkerState extends State<NodeMapMarker> {
Timer? _tapTimer;
// From dev_config.dart for build-time parameters
static const Duration tapTimeout = kMarkerTapTimeout;
@@ -60,7 +60,7 @@ class _CameraMapMarkerState extends State<CameraMapMarker> {
@override
Widget build(BuildContext context) {
// Check camera state
// Check node state
final isPendingUpload = widget.node.tags.containsKey('_pending_upload') &&
widget.node.tags['_pending_upload'] == 'true';
final isPendingEdit = widget.node.tags.containsKey('_pending_edit') &&
@@ -87,10 +87,10 @@ class _CameraMapMarkerState extends State<CameraMapMarker> {
}
}
/// Helper class to build marker layers for cameras and user location
class CameraMarkersBuilder {
static List<Marker> buildCameraMarkers({
required List<OsmNode> cameras,
/// Helper class to build marker layers for surveillance nodes and user location
class NodeMarkersBuilder {
static List<Marker> buildNodeMarkers({
required List<OsmNode> nodes,
required MapController mapController,
LatLng? userLocation,
int? selectedNodeId,
@@ -98,9 +98,9 @@ class CameraMarkersBuilder {
bool shouldDim = false,
}) {
final markers = <Marker>[
// Camera markers
...cameras
.where(_isValidCameraCoordinate)
// Node markers
...nodes
.where(_isValidNodeCoordinate)
.map((n) {
// Check if this node should be highlighted (selected) or dimmed
final isSelected = selectedNodeId == n.id;
@@ -112,7 +112,7 @@ class CameraMarkersBuilder {
height: kNodeIconDiameter,
child: Opacity(
opacity: shouldDimNode ? 0.5 : 1.0,
child: CameraMapMarker(
child: NodeMapMarker(
node: n,
mapController: mapController,
onNodeTap: onNodeTap,
@@ -134,7 +134,7 @@ class CameraMarkersBuilder {
return markers;
}
static bool _isValidCameraCoordinate(OsmNode node) {
static bool _isValidNodeCoordinate(OsmNode node) {
return (node.coord.latitude != 0 || node.coord.longitude != 0) &&
node.coord.latitude.abs() <= 90 &&
node.coord.longitude.abs() <= 180;

View File

@@ -6,31 +6,31 @@ import 'package:latlong2/latlong.dart';
import '../../models/node_profile.dart';
import '../../app_state.dart' show UploadMode;
import '../../services/prefetch_area_service.dart';
import '../camera_provider_with_cache.dart';
import '../node_provider_with_cache.dart';
import '../../dev_config.dart';
/// Manages camera data refreshing, profile change detection, and camera cache operations.
/// Handles debounced camera fetching and profile-based cache invalidation.
class CameraRefreshController {
late final CameraProviderWithCache _cameraProvider;
/// Manages node data refreshing, profile change detection, and node cache operations.
/// Handles debounced node fetching and profile-based cache invalidation.
class NodeRefreshController {
late final NodeProviderWithCache _nodeProvider;
List<NodeProfile>? _lastEnabledProfiles;
VoidCallback? _onCamerasUpdated;
VoidCallback? _onNodesUpdated;
/// Initialize the camera refresh controller
void initialize({required VoidCallback onCamerasUpdated}) {
_cameraProvider = CameraProviderWithCache.instance;
_onCamerasUpdated = onCamerasUpdated;
_cameraProvider.addListener(_onCamerasUpdated!);
/// Initialize the node refresh controller
void initialize({required VoidCallback onNodesUpdated}) {
_nodeProvider = NodeProviderWithCache.instance;
_onNodesUpdated = onNodesUpdated;
_nodeProvider.addListener(_onNodesUpdated!);
}
/// Dispose of resources and listeners
void dispose() {
if (_onCamerasUpdated != null) {
_cameraProvider.removeListener(_onCamerasUpdated!);
if (_onNodesUpdated != null) {
_nodeProvider.removeListener(_onNodesUpdated!);
}
}
/// Check if camera profiles changed and handle cache clearing if needed.
/// Check if node profiles changed and handle cache clearing if needed.
/// Returns true if profiles changed (triggering a refresh).
bool checkAndHandleProfileChanges({
required List<NodeProfile> currentEnabledProfiles,
@@ -42,13 +42,13 @@ class CameraRefreshController {
// Handle profile change with cache clearing and refresh
WidgetsBinding.instance.addPostFrameCallback((_) {
// Clear camera cache to ensure fresh data for new profile combination
_cameraProvider.clearCache();
// Clear node cache to ensure fresh data for new profile combination
_nodeProvider.clearCache();
// Clear pre-fetch area since profiles changed
PrefetchAreaService().clearPreFetchedArea();
// Force display refresh first (for immediate UI update)
_cameraProvider.refreshDisplay();
// Notify that profiles changed (triggers camera refresh)
_nodeProvider.refreshDisplay();
// Notify that profiles changed (triggers node refresh)
onProfilesChanged();
});
@@ -57,8 +57,8 @@ class CameraRefreshController {
return false;
}
/// Refresh cameras from provider for the current map view
void refreshCamerasFromProvider({
/// Refresh nodes from provider for the current map view
void refreshNodesFromProvider({
required AnimatedMapController controller,
required List<NodeProfile> enabledProfiles,
required UploadMode uploadMode,
@@ -85,15 +85,15 @@ class CameraRefreshController {
return;
}
_cameraProvider.fetchAndUpdate(
_nodeProvider.fetchAndUpdate(
bounds: bounds,
profiles: enabledProfiles,
uploadMode: uploadMode,
);
}
/// Get the camera provider instance for external access
CameraProviderWithCache get cameraProvider => _cameraProvider;
/// Get the node provider instance for external access
NodeProviderWithCache get nodeProvider => _nodeProvider;
/// Helper to check if two profile lists are equal by comparing IDs
bool _profileListsEqual(List<NodeProfile> list1, List<NodeProfile> list2) {

View File

@@ -14,11 +14,11 @@ import '../models/suspected_location.dart';
import '../models/tile_provider.dart';
import '../state/session_state.dart';
import 'debouncer.dart';
import 'camera_provider_with_cache.dart';
import 'node_provider_with_cache.dart';
import 'map/map_overlays.dart';
import 'map/map_position_manager.dart';
import 'map/tile_layer_manager.dart';
import 'map/camera_refresh_controller.dart';
import 'map/node_refresh_controller.dart';
import 'map/gps_controller.dart';
import 'map/map_data_manager.dart';
import 'map/map_interaction_manager.dart';
@@ -69,7 +69,7 @@ class MapViewState extends State<MapView> {
late final MapPositionManager _positionManager;
late final TileLayerManager _tileManager;
late final CameraRefreshController _cameraController;
late final NodeRefreshController _nodeController;
late final GpsController _gpsController;
late final MapDataManager _dataManager;
late final MapInteractionManager _interactionManager;
@@ -93,8 +93,8 @@ class MapViewState extends State<MapView> {
_positionManager = MapPositionManager();
_tileManager = TileLayerManager();
_tileManager.initialize();
_cameraController = CameraRefreshController();
_cameraController.initialize(onCamerasUpdated: _onNodesUpdated);
_nodeController = NodeRefreshController();
_nodeController.initialize(onNodesUpdated: _onNodesUpdated);
_gpsController = GpsController();
_dataManager = MapDataManager();
_interactionManager = MapInteractionManager();
@@ -167,7 +167,7 @@ class MapViewState extends State<MapView> {
return [];
}
return mapBounds != null
? CameraProviderWithCache.instance.getCachedNodesForBounds(mapBounds)
? NodeProviderWithCache.instance.getCachedNodesForBounds(mapBounds)
: [];
} catch (e) {
debugPrint('[MapView] Could not get nearby nodes: $e');
@@ -209,7 +209,7 @@ class MapViewState extends State<MapView> {
_cameraDebounce.dispose();
_tileDebounce.dispose();
_mapPositionDebounce.dispose();
_cameraController.dispose();
_nodeController.dispose();
_tileManager.dispose();
_gpsController.dispose();
PrefetchAreaService().dispose();
@@ -241,7 +241,7 @@ class MapViewState extends State<MapView> {
void _refreshNodesFromProvider() {
final appState = context.read<AppState>();
_cameraController.refreshCamerasFromProvider(
_nodeController.refreshNodesFromProvider(
controller: _controller,
enabledProfiles: appState.enabledProfiles,
uploadMode: appState.uploadMode,
@@ -273,8 +273,8 @@ class MapViewState extends State<MapView> {
final session = appState.session;
final editSession = appState.editSession;
// Check if enabled profiles changed and refresh cameras if needed
_cameraController.checkAndHandleProfileChanges(
// Check if enabled profiles changed and refresh nodes if needed
_nodeController.checkAndHandleProfileChanges(
currentEnabledProfiles: appState.enabledProfiles,
onProfilesChanged: _refreshNodesFromProvider,
);

View File

@@ -12,10 +12,10 @@ import '../app_state.dart';
/// Provides surveillance nodes for a map view, using an in-memory cache and optionally
/// merging in new results from Overpass via MapDataProvider when not offline.
class CameraProviderWithCache extends ChangeNotifier {
static final CameraProviderWithCache instance = CameraProviderWithCache._internal();
factory CameraProviderWithCache() => instance;
CameraProviderWithCache._internal();
class NodeProviderWithCache extends ChangeNotifier {
static final NodeProviderWithCache instance = NodeProviderWithCache._internal();
factory NodeProviderWithCache() => instance;
NodeProviderWithCache._internal();
Timer? _debounceTimer;
@@ -61,7 +61,7 @@ class CameraProviderWithCache extends ChangeNotifier {
notifyListeners();
}
} catch (e) {
debugPrint('[CameraProviderWithCache] Node fetch failed: $e');
debugPrint('[NodeProviderWithCache] Node fetch failed: $e');
// Cache already holds whatever is available for the view
}
});
@@ -114,4 +114,4 @@ class CameraProviderWithCache extends ChangeNotifier {
}
return true;
}
}
}