From 3ddebd266472d3347f5f1029dc51c2856b2f8822 Mon Sep 17 00:00:00 2001 From: stopflock Date: Fri, 29 Aug 2025 16:44:34 -0500 Subject: [PATCH] nodes, not cameras --- lib/app_state.dart | 6 ++-- lib/dev_config.dart | 14 ++++----- lib/screens/home_screen.dart | 18 +++++------ lib/screens/settings_screen.dart | 4 +-- ...as_section.dart => max_nodes_section.dart} | 16 +++++----- .../operator_profile_list_section.dart | 2 +- lib/services/map_data_provider.dart | 10 +++---- lib/services/uploader.dart | 4 +-- lib/state/session_state.dart | 30 +++++++++---------- lib/state/upload_queue_state.dart | 4 +-- ..._camera_sheet.dart => add_node_sheet.dart} | 12 ++++---- ...camera_sheet.dart => edit_node_sheet.dart} | 16 +++++----- lib/widgets/map/direction_cones.dart | 4 +-- lib/widgets/map/map_overlays.dart | 4 +-- lib/widgets/refine_tags_sheet.dart | 2 +- 15 files changed, 73 insertions(+), 73 deletions(-) rename lib/screens/settings_screen_sections/{max_cameras_section.dart => max_nodes_section.dart} (80%) rename lib/widgets/{add_camera_sheet.dart => add_node_sheet.dart} (95%) rename lib/widgets/{edit_camera_sheet.dart => edit_node_sheet.dart} (94%) diff --git a/lib/app_state.dart b/lib/app_state.dart index 920f691..c00e255 100644 --- a/lib/app_state.dart +++ b/lib/app_state.dart @@ -17,7 +17,7 @@ import 'state/upload_queue_state.dart'; // Re-export types export 'state/settings_state.dart' show UploadMode, FollowMeMode; -export 'state/session_state.dart' show AddCameraSession, EditCameraSession; +export 'state/session_state.dart' show AddNodeSession, EditNodeSession; // ------------------ AppState ------------------ class AppState extends ChangeNotifier { @@ -69,8 +69,8 @@ class AppState extends ChangeNotifier { List get operatorProfiles => _operatorProfileState.profiles; // Session state - AddCameraSession? get session => _sessionState.session; - EditCameraSession? get editSession => _sessionState.editSession; + AddNodeSession? get session => _sessionState.session; + EditNodeSession? get editSession => _sessionState.editSession; // Settings state bool get offlineMode => _settingsState.offlineMode; diff --git a/lib/dev_config.dart b/lib/dev_config.dart index 35fd929..3803f8c 100644 --- a/lib/dev_config.dart +++ b/lib/dev_config.dart @@ -28,8 +28,8 @@ const double kAddPinYOffset = 0.0; const String kClientName = 'FlockMap'; const String kClientVersion = '0.9.7'; -// Marker/camera interaction -const int kCameraMinZoomLevel = 10; // Minimum zoom to show cameras or warning +// Marker/node interaction +const int kCameraMinZoomLevel = 10; // Minimum zoom to show nodes or warning const Duration kMarkerTapTimeout = Duration(milliseconds: 250); const Duration kDebounceCameraRefresh = Duration(milliseconds: 500); @@ -62,8 +62,8 @@ const int kAbsoluteMaxZoom = 19; const double kCameraIconDiameter = 20.0; const double kCameraRingThickness = 4.0; const double kCameraDotOpacity = 0.4; // Opacity for the grey dot interior -const Color kCameraRingColorReal = Color(0xC43F55F3); // Real cameras from OSM - blue -const Color kCameraRingColorMock = Color(0xC4FFFFFF); // Add camera mock point - white -const Color kCameraRingColorPending = Color(0xC49C27B0); // Submitted/pending cameras - purple -const Color kCameraRingColorEditing = Color(0xC4FF9800); // Camera being edited - orange -const Color kCameraRingColorPendingEdit = Color(0xC4757575); // Original camera with pending edit - grey \ No newline at end of file +const Color kCameraRingColorReal = Color(0xC43F55F3); // Real nodes from OSM - blue +const Color kCameraRingColorMock = Color(0xC4FFFFFF); // Add node mock point - white +const Color kCameraRingColorPending = Color(0xC49C27B0); // Submitted/pending nodes - purple +const Color kCameraRingColorEditing = Color(0xC4FF9800); // Node being edited - orange +const Color kCameraRingColorPendingEdit = Color(0xC4757575); // Original node with pending edit - grey \ No newline at end of file diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index d8d70ff..af4d69c 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -7,8 +7,8 @@ import '../app_state.dart'; import '../dev_config.dart'; import '../widgets/map_view.dart'; -import '../widgets/add_camera_sheet.dart'; -import '../widgets/edit_camera_sheet.dart'; +import '../widgets/add_node_sheet.dart'; +import '../widgets/edit_node_sheet.dart'; import '../widgets/camera_provider_with_cache.dart'; import '../widgets/download_area_dialog.dart'; @@ -70,7 +70,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { } } - void _openAddCameraSheet() { + void _openAddNodeSheet() { final appState = context.read(); // Disable follow-me when adding a camera so the map doesn't jump around appState.setFollowMeMode(FollowMeMode.off); @@ -79,11 +79,11 @@ class _HomeScreenState extends State with TickerProviderStateMixin { final session = appState.session!; // guaranteed non‑null now _scaffoldKey.currentState!.showBottomSheet( - (ctx) => AddCameraSheet(session: session), + (ctx) => AddNodeSheet(session: session), ); } - void _openEditCameraSheet() { + void _openEditNodeSheet() { final appState = context.read(); // Disable follow-me when editing a camera so the map doesn't jump around appState.setFollowMeMode(FollowMeMode.off); @@ -91,7 +91,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { final session = appState.editSession!; // should be non-null when this is called _scaffoldKey.currentState!.showBottomSheet( - (ctx) => EditCameraSheet(session: session), + (ctx) => EditNodeSheet(session: session), ); } @@ -102,7 +102,7 @@ class _HomeScreenState extends State with TickerProviderStateMixin { // Auto-open edit sheet when edit session starts if (appState.editSession != null && !_editSheetShown) { _editSheetShown = true; - WidgetsBinding.instance.addPostFrameCallback((_) => _openEditCameraSheet()); + WidgetsBinding.instance.addPostFrameCallback((_) => _openEditNodeSheet()); } else if (appState.editSession == null) { _editSheetShown = false; } @@ -169,8 +169,8 @@ class _HomeScreenState extends State with TickerProviderStateMixin { Expanded( child: ElevatedButton.icon( icon: Icon(Icons.add_location_alt), - label: Text('Tag Camera'), - onPressed: _openAddCameraSheet, + label: Text('Tag Node'), + onPressed: _openAddNodeSheet, style: ElevatedButton.styleFrom( minimumSize: Size(0, 48), textStyle: TextStyle(fontSize: 16), diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 9972adb..a8d201f 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -7,7 +7,7 @@ import 'settings_screen_sections/queue_section.dart'; import 'settings_screen_sections/offline_areas_section.dart'; import 'settings_screen_sections/offline_mode_section.dart'; import 'settings_screen_sections/about_section.dart'; -import 'settings_screen_sections/max_cameras_section.dart'; +import 'settings_screen_sections/max_nodes_section.dart'; import 'settings_screen_sections/tile_provider_section.dart'; class SettingsScreen extends StatelessWidget { @@ -30,7 +30,7 @@ class SettingsScreen extends StatelessWidget { Divider(), OperatorProfileListSection(), Divider(), - MaxCamerasSection(), + MaxNodesSection(), Divider(), TileProviderSection(), Divider(), diff --git a/lib/screens/settings_screen_sections/max_cameras_section.dart b/lib/screens/settings_screen_sections/max_nodes_section.dart similarity index 80% rename from lib/screens/settings_screen_sections/max_cameras_section.dart rename to lib/screens/settings_screen_sections/max_nodes_section.dart index 41307fe..37f9f91 100644 --- a/lib/screens/settings_screen_sections/max_cameras_section.dart +++ b/lib/screens/settings_screen_sections/max_nodes_section.dart @@ -2,21 +2,21 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../app_state.dart'; -class MaxCamerasSection extends StatefulWidget { - const MaxCamerasSection({super.key}); +class MaxNodesSection extends StatefulWidget { + const MaxNodesSection({super.key}); @override - State createState() => _MaxCamerasSectionState(); + State createState() => _MaxNodesSectionState(); } -class _MaxCamerasSectionState extends State { +class _MaxNodesSectionState extends State { late TextEditingController _controller; @override void initState() { super.initState(); - final maxCameras = context.read().maxCameras; - _controller = TextEditingController(text: maxCameras.toString()); + final maxNodes = context.read().maxCameras; + _controller = TextEditingController(text: maxNodes.toString()); } @override @@ -35,11 +35,11 @@ class _MaxCamerasSectionState extends State { children: [ ListTile( leading: const Icon(Icons.filter_alt), - title: const Text('Max cameras fetched/drawn'), + title: const Text('Max nodes fetched/drawn'), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Set an upper limit for the number of cameras on the map (default: 250).'), + const Text('Set an upper limit for the number of nodes on the map (default: 250).'), if (showWarning) Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), diff --git a/lib/screens/settings_screen_sections/operator_profile_list_section.dart b/lib/screens/settings_screen_sections/operator_profile_list_section.dart index 9a42692..b1b500e 100644 --- a/lib/screens/settings_screen_sections/operator_profile_list_section.dart +++ b/lib/screens/settings_screen_sections/operator_profile_list_section.dart @@ -40,7 +40,7 @@ class OperatorProfileListSection extends StatelessWidget { const Padding( padding: EdgeInsets.all(16.0), child: Text( - 'No operator profiles defined. Create one to apply operator tags to camera submissions.', + 'No operator profiles defined. Create one to apply operator tags to node submissions.', style: TextStyle(color: Colors.grey), textAlign: TextAlign.center, ), diff --git a/lib/services/map_data_provider.dart b/lib/services/map_data_provider.dart index 8f76a91..58117ef 100644 --- a/lib/services/map_data_provider.dart +++ b/lib/services/map_data_provider.dart @@ -31,7 +31,7 @@ class MapDataProvider { AppState.instance.setOfflineMode(enabled); } - /// Fetch cameras from OSM/Overpass or local storage. + /// Fetch surveillance nodes from OSM/Overpass or local storage. /// Remote is default. If source is MapSource.auto, remote is tried first unless offline. Future> getCameras({ required LatLngBounds bounds, @@ -44,7 +44,7 @@ class MapDataProvider { // Explicit remote request: error if offline, else always remote if (source == MapSource.remote) { if (offline) { - throw OfflineModeException("Cannot fetch remote cameras in offline mode."); + throw OfflineModeException("Cannot fetch remote nodes in offline mode."); } return camerasFromOverpass( bounds: bounds, @@ -79,7 +79,7 @@ class MapDataProvider { pageSize: AppState.instance.maxCameras, ); } catch (e) { - debugPrint('[MapDataProvider] Remote camera fetch failed, error: $e. Falling back to local.'); + debugPrint('[MapDataProvider] Remote node fetch failed, error: $e. Falling back to local.'); return fetchLocalCameras( bounds: bounds, profiles: profiles, @@ -89,7 +89,7 @@ class MapDataProvider { } } - /// Bulk/paged camera fetch for offline downloads (handling paging, dedup, and Overpass retries) + /// Bulk/paged node fetch for offline downloads (handling paging, dedup, and Overpass retries) /// Only use for offline area download, not for map browsing! Ignores maxCameras config. Future> getAllCamerasForDownload({ required LatLngBounds bounds, @@ -100,7 +100,7 @@ class MapDataProvider { }) async { final offline = AppState.instance.offlineMode; if (offline) { - throw OfflineModeException("Cannot fetch remote cameras for offline area download in offline mode."); + throw OfflineModeException("Cannot fetch remote nodes for offline area download in offline mode."); } return camerasFromOverpass( bounds: bounds, diff --git a/lib/services/uploader.dart b/lib/services/uploader.dart index 555c1d9..7beb6c4 100644 --- a/lib/services/uploader.dart +++ b/lib/services/uploader.dart @@ -14,7 +14,7 @@ class Uploader { Future upload(PendingUpload p) async { try { - print('Uploader: Starting upload for camera at ${p.coord.latitude}, ${p.coord.longitude}'); + print('Uploader: Starting upload for node at ${p.coord.latitude}, ${p.coord.longitude}'); // 1. open changeset final action = p.isEdit ? 'Update' : 'Add'; @@ -22,7 +22,7 @@ class Uploader { - + '''; print('Uploader: Creating changeset...'); diff --git a/lib/state/session_state.dart b/lib/state/session_state.dart index 383c97a..3d0abda 100644 --- a/lib/state/session_state.dart +++ b/lib/state/session_state.dart @@ -5,25 +5,25 @@ import '../models/camera_profile.dart'; import '../models/operator_profile.dart'; import '../models/osm_camera_node.dart'; -// ------------------ AddCameraSession ------------------ -class AddCameraSession { - AddCameraSession({required this.profile, this.directionDegrees = 0}); +// ------------------ AddNodeSession ------------------ +class AddNodeSession { + AddNodeSession({required this.profile, this.directionDegrees = 0}); CameraProfile profile; OperatorProfile? operatorProfile; double directionDegrees; LatLng? target; } -// ------------------ EditCameraSession ------------------ -class EditCameraSession { - EditCameraSession({ +// ------------------ EditNodeSession ------------------ +class EditNodeSession { + EditNodeSession({ required this.originalNode, required this.profile, required this.directionDegrees, required this.target, }); - final OsmCameraNode originalNode; // The original camera being edited + final OsmCameraNode originalNode; // The original node being edited CameraProfile profile; OperatorProfile? operatorProfile; double directionDegrees; @@ -31,19 +31,19 @@ class EditCameraSession { } class SessionState extends ChangeNotifier { - AddCameraSession? _session; - EditCameraSession? _editSession; + AddNodeSession? _session; + EditNodeSession? _editSession; // Getters - AddCameraSession? get session => _session; - EditCameraSession? get editSession => _editSession; + AddNodeSession? get session => _session; + EditNodeSession? get editSession => _editSession; void startAddSession(List enabledProfiles) { final submittableProfiles = enabledProfiles.where((p) => p.isSubmittable).toList(); final defaultProfile = submittableProfiles.isNotEmpty ? submittableProfiles.first : enabledProfiles.first; // Fallback to any enabled profile - _session = AddCameraSession(profile: defaultProfile); + _session = AddNodeSession(profile: defaultProfile); _editSession = null; // Clear any edit session notifyListeners(); } @@ -64,7 +64,7 @@ class SessionState extends ChangeNotifier { } } - _editSession = EditCameraSession( + _editSession = EditNodeSession( originalNode: node, profile: matchingProfile, directionDegrees: node.directionDeg ?? 0, @@ -150,7 +150,7 @@ class SessionState extends ChangeNotifier { notifyListeners(); } - AddCameraSession? commitSession() { + AddNodeSession? commitSession() { if (_session?.target == null) return null; final session = _session!; @@ -159,7 +159,7 @@ class SessionState extends ChangeNotifier { return session; } - EditCameraSession? commitEditSession() { + EditNodeSession? commitEditSession() { if (_editSession == null) return null; final session = _editSession!; diff --git a/lib/state/upload_queue_state.dart b/lib/state/upload_queue_state.dart index 7d8df56..f96dd0b 100644 --- a/lib/state/upload_queue_state.dart +++ b/lib/state/upload_queue_state.dart @@ -25,7 +25,7 @@ class UploadQueueState extends ChangeNotifier { } // Add a completed session to the upload queue - void addFromSession(AddCameraSession session, {required UploadMode uploadMode}) { + void addFromSession(AddNodeSession session, {required UploadMode uploadMode}) { final upload = PendingUpload( coord: session.target!, direction: session.directionDegrees, @@ -58,7 +58,7 @@ class UploadQueueState extends ChangeNotifier { } // Add a completed edit session to the upload queue - void addFromEditSession(EditCameraSession session, {required UploadMode uploadMode}) { + void addFromEditSession(EditNodeSession session, {required UploadMode uploadMode}) { final upload = PendingUpload( coord: session.target, direction: session.directionDegrees, diff --git a/lib/widgets/add_camera_sheet.dart b/lib/widgets/add_node_sheet.dart similarity index 95% rename from lib/widgets/add_camera_sheet.dart rename to lib/widgets/add_node_sheet.dart index 748ec08..8234a44 100644 --- a/lib/widgets/add_camera_sheet.dart +++ b/lib/widgets/add_node_sheet.dart @@ -6,10 +6,10 @@ import '../models/camera_profile.dart'; import '../models/operator_profile.dart'; import 'refine_tags_sheet.dart'; -class AddCameraSheet extends StatelessWidget { - const AddCameraSheet({super.key, required this.session}); +class AddNodeSheet extends StatelessWidget { + const AddNodeSheet({super.key, required this.session}); - final AddCameraSession session; + final AddNodeSession session; @override Widget build(BuildContext context) { @@ -19,7 +19,7 @@ class AddCameraSheet extends StatelessWidget { appState.commitSession(); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Camera queued for upload')), + const SnackBar(content: Text('Node queued for upload')), ); } @@ -111,7 +111,7 @@ class AddCameraSheet extends StatelessWidget { SizedBox(width: 6), Expanded( child: Text( - 'Enable a submittable profile in Settings to submit new cameras.', + 'Enable a submittable profile in Settings to submit new nodes.', style: TextStyle(color: Colors.red, fontSize: 13), ), ), @@ -127,7 +127,7 @@ class AddCameraSheet extends StatelessWidget { SizedBox(width: 6), Expanded( child: Text( - 'This profile is for map viewing only. Please select a submittable profile to submit new cameras.', + 'This profile is for map viewing only. Please select a submittable profile to submit new nodes.', style: TextStyle(color: Colors.orange, fontSize: 13), ), ), diff --git a/lib/widgets/edit_camera_sheet.dart b/lib/widgets/edit_node_sheet.dart similarity index 94% rename from lib/widgets/edit_camera_sheet.dart rename to lib/widgets/edit_node_sheet.dart index 9b638de..2ef90bd 100644 --- a/lib/widgets/edit_camera_sheet.dart +++ b/lib/widgets/edit_node_sheet.dart @@ -7,10 +7,10 @@ import '../models/operator_profile.dart'; import '../state/settings_state.dart'; import 'refine_tags_sheet.dart'; -class EditCameraSheet extends StatelessWidget { - const EditCameraSheet({super.key, required this.session}); +class EditNodeSheet extends StatelessWidget { + const EditNodeSheet({super.key, required this.session}); - final EditCameraSession session; + final EditNodeSession session; @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class EditCameraSheet extends StatelessWidget { appState.commitEditSession(); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Camera edit queued for upload')), + const SnackBar(content: Text('Node edit queued for upload')), ); } @@ -65,7 +65,7 @@ class EditCameraSheet extends StatelessWidget { ), const SizedBox(height: 8), Text( - 'Edit Camera #${session.originalNode.id}', + 'Edit Node #${session.originalNode.id}', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 16), @@ -118,7 +118,7 @@ class EditCameraSheet extends StatelessWidget { SizedBox(width: 6), Expanded( child: Text( - 'Cannot submit edits on production nodes to sandbox. Switch to Production mode in Settings to edit cameras.', + 'Cannot submit edits on production nodes to sandbox. Switch to Production mode in Settings to edit nodes.', style: TextStyle(color: Colors.blue, fontSize: 13), ), ), @@ -134,7 +134,7 @@ class EditCameraSheet extends StatelessWidget { SizedBox(width: 6), Expanded( child: Text( - 'Enable a submittable profile in Settings to edit cameras.', + 'Enable a submittable profile in Settings to edit nodes.', style: TextStyle(color: Colors.red, fontSize: 13), ), ), @@ -150,7 +150,7 @@ class EditCameraSheet extends StatelessWidget { SizedBox(width: 6), Expanded( child: Text( - 'This profile is for map viewing only. Please select a submittable profile to edit cameras.', + 'This profile is for map viewing only. Please select a submittable profile to edit nodes.', style: TextStyle(color: Colors.orange, fontSize: 13), ), ), diff --git a/lib/widgets/map/direction_cones.dart b/lib/widgets/map/direction_cones.dart index 068e862..e69a841 100644 --- a/lib/widgets/map/direction_cones.dart +++ b/lib/widgets/map/direction_cones.dart @@ -12,8 +12,8 @@ class DirectionConesBuilder { static List buildDirectionCones({ required List cameras, required double zoom, - AddCameraSession? session, - EditCameraSession? editSession, + AddNodeSession? session, + EditNodeSession? editSession, }) { final overlays = []; diff --git a/lib/widgets/map/map_overlays.dart b/lib/widgets/map/map_overlays.dart index 5564c9f..c0d8329 100644 --- a/lib/widgets/map/map_overlays.dart +++ b/lib/widgets/map/map_overlays.dart @@ -10,8 +10,8 @@ import 'layer_selector_button.dart'; class MapOverlays extends StatelessWidget { final MapController mapController; final UploadMode uploadMode; - final AddCameraSession? session; - final EditCameraSession? editSession; + final AddNodeSession? session; + final EditNodeSession? editSession; final String? attribution; // Attribution for current tile provider const MapOverlays({ diff --git a/lib/widgets/refine_tags_sheet.dart b/lib/widgets/refine_tags_sheet.dart index b6a5eec..98800dd 100644 --- a/lib/widgets/refine_tags_sheet.dart +++ b/lib/widgets/refine_tags_sheet.dart @@ -66,7 +66,7 @@ class _RefineTagsSheetState extends State { ), SizedBox(height: 4), Text( - 'Create operator profiles in Settings to apply additional tags to your camera submissions.', + 'Create operator profiles in Settings to apply additional tags to your node submissions.', style: TextStyle(color: Colors.grey), textAlign: TextAlign.center, ),