diff --git a/.gitignore b/.gitignore index 5fb7318..b7dd43f 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,10 @@ Thumbs.db *.keystore .env +# ─────────────────────────────── +# For now - not targeting these +# ─────────────────────────────── +linux/ +macos/ +web/ +windows/ \ No newline at end of file diff --git a/lib/app_state.dart b/lib/app_state.dart index 294c42c..c2f5839 100644 --- a/lib/app_state.dart +++ b/lib/app_state.dart @@ -72,7 +72,7 @@ class AppState extends ChangeNotifier { } // Upload mode: production, sandbox, or simulate (in-memory, no uploads) - UploadMode _uploadMode = UploadMode.production; + UploadMode _uploadMode = UploadMode.simulate; static const String _uploadModePrefsKey = 'upload_mode'; UploadMode get uploadMode => _uploadMode; Future setUploadMode(UploadMode mode) async { diff --git a/lib/dev_config.dart b/lib/dev_config.dart index b12ba62..5e6f0ea 100644 --- a/lib/dev_config.dart +++ b/lib/dev_config.dart @@ -10,6 +10,18 @@ const double kTileEstimateKb = 25.0; const double kDirectionConeHalfAngle = 20.0; // degrees const double kDirectionConeBaseLength = 0.0012; // multiplier +// Add Camera pin vertical offset (for pin tip to match coordinate on map) +const double kAddPinYOffset = -16.0; + +// Bottom overlay vertical positions (distance from bottom of screen) +const double kAttributionBottom = 5.0; +const double kScaleBarBottom = 40.0; +const double kZoomIndicatorBottom = 70.0; + +// Client name and version for OSM uploads ("created_by" tag) +const String kClientName = 'FlockMap'; +const String kClientVersion = '0.8.1'; + // Marker/camera interaction const int kCameraMinZoomLevel = 10; // Minimum zoom to show cameras or warning const Duration kMarkerTapTimeout = Duration(milliseconds: 250); diff --git a/lib/models/camera_profile.dart b/lib/models/camera_profile.dart index 0804856..e27ac69 100644 --- a/lib/models/camera_profile.dart +++ b/lib/models/camera_profile.dart @@ -20,10 +20,7 @@ class CameraProfile { name: 'Generic Flock', tags: const { 'man_made': 'surveillance', - 'surveillance': 'public', - 'surveillance:zone': 'traffic', - 'surveillance:type': 'ALPR', // left for backward compatibility — you may want to revisit per OSM best practice - 'camera:type': 'fixed', + 'surveillance:type': 'ALPR', 'manufacturer': 'Flock Safety', 'manufacturer:wikidata': 'Q108485435', }, diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index f6b9189..3476e5c 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -27,10 +27,12 @@ class _HomeScreenState extends State { void _openAddCameraSheet() { final appState = context.read(); appState.startAddSession(); - final session = appState.session!; // guaranteed non‑null now + final session = appState.session!; // guaranteed non‑null now - _scaffoldKey.currentState!.showBottomSheet( - (ctx) => AddCameraSheet(session: session), + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (ctx) => AddCameraSheet(session: session), ); } @@ -66,31 +68,49 @@ class _HomeScreenState extends State { if (_followMe) setState(() => _followMe = false); }, ), - floatingActionButton: appState.session == null - ? Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - FloatingActionButton.extended( - onPressed: _openAddCameraSheet, + bottomNavigationBar: BottomAppBar( + elevation: 10, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: ElevatedButton.icon( icon: const Icon(Icons.add_location_alt), label: const Text('Tag Camera'), - heroTag: 'tag_camera_fab', - ), - const SizedBox(height: 12), - FloatingActionButton.extended( - onPressed: () => showDialog( - context: context, - builder: (ctx) => DownloadAreaDialog(controller: _mapController), + onPressed: () { + if (appState.session == null) { + _openAddCameraSheet(); + } + }, + style: ElevatedButton.styleFrom( + minimumSize: const Size(0, 48), + textStyle: const TextStyle(fontSize: 16), ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: ElevatedButton.icon( icon: const Icon(Icons.download_for_offline), label: const Text('Download'), - heroTag: 'download_fab', + onPressed: appState.session == null + ? () => showDialog( + context: context, + builder: (ctx) => DownloadAreaDialog(controller: _mapController), + ) + : null, // Disabled while camera sheet active + style: ElevatedButton.styleFrom( + minimumSize: const Size(0, 48), + textStyle: const TextStyle(fontSize: 16), + ), ), - ], - ) - : null, - floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + ), + ], + ), + ), + ), ), ); } diff --git a/lib/services/uploader.dart b/lib/services/uploader.dart index e48243f..04d4ca2 100644 --- a/lib/services/uploader.dart +++ b/lib/services/uploader.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:http/http.dart' as http; import '../models/pending_upload.dart'; - +import '../dev_config.dart'; import '../app_state.dart'; class Uploader { @@ -20,7 +20,7 @@ class Uploader { final csXml = ''' - + '''; diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index aa77029..dce7e5a 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -311,7 +311,7 @@ class _MapViewState extends State { // Built-in scale bar from flutter_map Scalebar( alignment: Alignment.bottomLeft, - padding: EdgeInsets.only(left: 8, bottom: 54), // above attribution + padding: EdgeInsets.only(left: 8, bottom: kScaleBarBottom), // from dev_config, above attribution & BottomAppBar textStyle: TextStyle(color: Colors.black, fontWeight: FontWeight.bold), lineColor: Colors.black, strokeWidth: 3, @@ -353,7 +353,7 @@ class _MapViewState extends State { // Zoom indicator, positioned above scale bar Positioned( left: 10, - bottom: 92, + bottom: kZoomIndicatorBottom, // from dev_config child: Container( padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), decoration: BoxDecoration( @@ -377,7 +377,7 @@ class _MapViewState extends State { ), // Attribution overlay Positioned( - bottom: 20, + bottom: kAttributionBottom, // from dev_config left: 10, child: Container( color: Colors.white70, @@ -391,9 +391,12 @@ class _MapViewState extends State { // Fixed pin when adding camera if (session != null) - const IgnorePointer( + IgnorePointer( child: Center( - child: Icon(Icons.place, size: 40, color: Colors.redAccent), + child: Transform.translate( + offset: Offset(0, kAddPinYOffset), + child: Icon(Icons.place, size: 40, color: Colors.redAccent), + ), ), ), ],