mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-02-12 16:52:51 +00:00
v2.4.1, adds profile import via deeplink, moves profile save button, fixes FOV clearing, disable direction slider while submitting with 360-fov profile
This commit is contained in:
@@ -104,10 +104,7 @@ cp lib/keys.dart.example lib/keys.dart
|
||||
## Roadmap
|
||||
|
||||
### Needed Bugfixes
|
||||
- 360 FOV means no direction slider
|
||||
- Fix rendering of 0-360 FOV ring
|
||||
- Move profile save button
|
||||
- Fix iOS not taking FOV values, cannot remove FOV
|
||||
- Node data fetching super slow; retries not working?
|
||||
- Clean up tile cache; implement some max size or otherwise trim unused / old tiles to prevent infinite memory growth
|
||||
- Filter NSI suggestions based on what has already been typed in
|
||||
@@ -129,6 +126,7 @@ cp lib/keys.dart.example lib/keys.dart
|
||||
- Save named locations to more easily navigate to home or work
|
||||
|
||||
### Maybes
|
||||
- "Universal Links" for better handling of profile import when app not installed?
|
||||
- Yellow ring for devices missing specific tag details
|
||||
- Android Auto / CarPlay
|
||||
- "Cache accumulating" offline area?
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
{
|
||||
"2.4.1": {
|
||||
"content": [
|
||||
"• Save button moved to top-right corner of profile editor screens",
|
||||
"• Fixed issue where FOV values could not be removed from profiles",
|
||||
"• Direction slider is now disabled for profiles with 360° FOV"
|
||||
]
|
||||
},
|
||||
"2.4.0": {
|
||||
"content": [
|
||||
"• Profile import from website links",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
/// Sentinel value for copyWith methods to distinguish between null and not provided
|
||||
const Object _notProvided = Object();
|
||||
|
||||
/// A bundle of preset OSM tags that describe a particular surveillance node model/type.
|
||||
class NodeProfile {
|
||||
final String id;
|
||||
@@ -217,7 +220,7 @@ class NodeProfile {
|
||||
bool? requiresDirection,
|
||||
bool? submittable,
|
||||
bool? editable,
|
||||
double? fov,
|
||||
Object? fov = _notProvided,
|
||||
}) =>
|
||||
NodeProfile(
|
||||
id: id ?? this.id,
|
||||
@@ -227,7 +230,7 @@ class NodeProfile {
|
||||
requiresDirection: requiresDirection ?? this.requiresDirection,
|
||||
submittable: submittable ?? this.submittable,
|
||||
editable: editable ?? this.editable,
|
||||
fov: fov ?? this.fov,
|
||||
fov: fov == _notProvided ? this.fov : fov as double?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
||||
@@ -55,6 +55,12 @@ class _OperatorProfileEditorState extends State<OperatorProfileEditor> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.profile.name.isEmpty ? locService.t('operatorProfileEditor.newOperatorProfile') : locService.t('operatorProfileEditor.editOperatorProfile')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _save,
|
||||
child: Text(locService.t('profileEditor.saveProfile')),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
@@ -87,10 +93,6 @@ class _OperatorProfileEditorState extends State<OperatorProfileEditor> {
|
||||
const SizedBox(height: 8),
|
||||
..._buildTagRows(),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: _save,
|
||||
child: Text(locService.t('profileEditor.saveProfile')),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -69,6 +69,12 @@ class _ProfileEditorState extends State<ProfileEditor> {
|
||||
title: Text(!widget.profile.editable
|
||||
? locService.t('profileEditor.viewProfile')
|
||||
: (widget.profile.name.isEmpty ? locService.t('profileEditor.newProfile') : locService.t('profileEditor.editProfile'))),
|
||||
actions: widget.profile.editable ? [
|
||||
TextButton(
|
||||
onPressed: _save,
|
||||
child: Text(locService.t('profileEditor.saveProfile')),
|
||||
),
|
||||
] : null,
|
||||
),
|
||||
body: ListView(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
@@ -135,11 +141,6 @@ class _ProfileEditorState extends State<ProfileEditor> {
|
||||
const SizedBox(height: 8),
|
||||
..._buildTagRows(),
|
||||
const SizedBox(height: 24),
|
||||
if (widget.profile.editable)
|
||||
ElevatedButton(
|
||||
onPressed: _save,
|
||||
child: Text(locService.t('profileEditor.saveProfile')),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -157,6 +157,15 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
|
||||
Widget _buildDirectionControls(BuildContext context, AppState appState, AddNodeSession session, LocalizationService locService) {
|
||||
final requiresDirection = session.profile != null && session.profile!.requiresDirection;
|
||||
final is360Fov = session.profile?.fov == 360;
|
||||
final enableDirectionControls = requiresDirection && !is360Fov;
|
||||
|
||||
// Force direction to 0 when FOV is 360 (omnidirectional)
|
||||
if (is360Fov && session.directionDegrees != 0) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
appState.updateSession(directionDeg: 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Format direction display text with bold for current direction
|
||||
String directionsText = '';
|
||||
@@ -206,7 +215,7 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
divisions: 359,
|
||||
value: session.directionDegrees,
|
||||
label: session.directionDegrees.round().toString(),
|
||||
onChanged: requiresDirection ? (v) => appState.updateSession(directionDeg: v) : null,
|
||||
onChanged: enableDirectionControls ? (v) => appState.updateSession(directionDeg: v) : null,
|
||||
),
|
||||
),
|
||||
// Direction control buttons - always show but grey out when direction not required
|
||||
@@ -216,9 +225,9 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
icon: Icon(
|
||||
Icons.remove,
|
||||
size: 20,
|
||||
color: requiresDirection ? null : Theme.of(context).disabledColor,
|
||||
color: enableDirectionControls ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
onPressed: requiresDirection && session.directions.length > 1
|
||||
onPressed: enableDirectionControls && session.directions.length > 1
|
||||
? () => appState.removeDirection()
|
||||
: null,
|
||||
tooltip: requiresDirection ? 'Remove current direction' : 'Direction not required for this profile',
|
||||
@@ -230,9 +239,9 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: requiresDirection && session.directions.length < 8 ? null : Theme.of(context).disabledColor,
|
||||
color: enableDirectionControls && session.directions.length < 8 ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
onPressed: requiresDirection && session.directions.length < 8 ? () => appState.addDirection() : null,
|
||||
onPressed: enableDirectionControls && session.directions.length < 8 ? () => appState.addDirection() : null,
|
||||
tooltip: requiresDirection
|
||||
? (session.directions.length >= 8 ? 'Maximum 8 directions allowed' : 'Add new direction')
|
||||
: 'Direction not required for this profile',
|
||||
@@ -244,9 +253,9 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
|
||||
icon: Icon(
|
||||
Icons.repeat,
|
||||
size: 20,
|
||||
color: requiresDirection ? null : Theme.of(context).disabledColor,
|
||||
color: enableDirectionControls ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
onPressed: requiresDirection && session.directions.length > 1
|
||||
onPressed: enableDirectionControls && session.directions.length > 1
|
||||
? () => appState.cycleDirection()
|
||||
: null,
|
||||
tooltip: requiresDirection ? 'Cycle through directions' : 'Direction not required for this profile',
|
||||
|
||||
@@ -137,6 +137,15 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
|
||||
Widget _buildDirectionControls(BuildContext context, AppState appState, EditNodeSession session, LocalizationService locService) {
|
||||
final requiresDirection = session.profile != null && session.profile!.requiresDirection;
|
||||
final is360Fov = session.profile?.fov == 360;
|
||||
final enableDirectionControls = requiresDirection && !is360Fov;
|
||||
|
||||
// Force direction to 0 when FOV is 360 (omnidirectional)
|
||||
if (is360Fov && session.directionDegrees != 0) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
appState.updateEditSession(directionDeg: 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Format direction display text with bold for current direction
|
||||
String directionsText = '';
|
||||
@@ -186,7 +195,7 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
divisions: 359,
|
||||
value: session.directionDegrees,
|
||||
label: session.directionDegrees.round().toString(),
|
||||
onChanged: requiresDirection ? (v) => appState.updateEditSession(directionDeg: v) : null,
|
||||
onChanged: enableDirectionControls ? (v) => appState.updateEditSession(directionDeg: v) : null,
|
||||
),
|
||||
),
|
||||
// Direction control buttons - always show but grey out when direction not required
|
||||
@@ -196,9 +205,9 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
icon: Icon(
|
||||
Icons.remove,
|
||||
size: 20,
|
||||
color: requiresDirection ? null : Theme.of(context).disabledColor,
|
||||
color: enableDirectionControls ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
onPressed: requiresDirection && session.directions.length > 1
|
||||
onPressed: enableDirectionControls && session.directions.length > 1
|
||||
? () => appState.removeDirection()
|
||||
: null,
|
||||
tooltip: requiresDirection ? 'Remove current direction' : 'Direction not required for this profile',
|
||||
@@ -210,9 +219,9 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: requiresDirection && session.directions.length < 8 ? null : Theme.of(context).disabledColor,
|
||||
color: enableDirectionControls && session.directions.length < 8 ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
onPressed: requiresDirection && session.directions.length < 8 ? () => appState.addDirection() : null,
|
||||
onPressed: enableDirectionControls && session.directions.length < 8 ? () => appState.addDirection() : null,
|
||||
tooltip: requiresDirection
|
||||
? (session.directions.length >= 8 ? 'Maximum 8 directions allowed' : 'Add new direction')
|
||||
: 'Direction not required for this profile',
|
||||
@@ -224,9 +233,9 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
|
||||
icon: Icon(
|
||||
Icons.repeat,
|
||||
size: 20,
|
||||
color: requiresDirection ? null : Theme.of(context).disabledColor,
|
||||
color: enableDirectionControls ? null : Theme.of(context).disabledColor,
|
||||
),
|
||||
onPressed: requiresDirection && session.directions.length > 1
|
||||
onPressed: enableDirectionControls && session.directions.length > 1
|
||||
? () => appState.cycleDirection()
|
||||
: null,
|
||||
tooltip: requiresDirection ? 'Cycle through directions' : 'Direction not required for this profile',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: deflockapp
|
||||
description: Map public surveillance infrastructure with OpenStreetMap
|
||||
publish_to: "none"
|
||||
version: 2.4.0+39 # The thing after the + is the version code, incremented with each release
|
||||
version: 2.4.1+39 # The thing after the + is the version code, incremented with each release
|
||||
|
||||
environment:
|
||||
sdk: ">=3.5.0 <4.0.0" # oauth2_client 4.x needs Dart 3.5+
|
||||
|
||||
Reference in New Issue
Block a user