mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-02-27 16:04:13 +00:00
Link to OSM in node_details sheet. Add option to open node in other editors.
This commit is contained in:
@@ -107,10 +107,8 @@ cp lib/keys.dart.example lib/keys.dart
|
||||
- Persistent cache for MY submissions: clean up when we see that node appear in overpass results or when older than 24h
|
||||
- Dropdown on "refine tags" page to select acceptable options for camera:mount=
|
||||
- Tutorial / info guide before submitting first node
|
||||
- Link to OSM node in node_details_sheet
|
||||
- Link to "my changes" on osm (username edit history)
|
||||
- Option to "extract node from way" for nodes attached to a way to allow moving
|
||||
- Option to "open in other editor" for advanced edits: StreetComplete/EveryDoor/Vespucci/GO!! Map/OSM.org(iD)/Rapid/Level0/OSMand/OrganicMaps/CoMaps
|
||||
|
||||
### On Pause
|
||||
- Suspected locations expansion to more regions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"1.3.3": {
|
||||
"content": "• NEW: Added builtin surveillance device profiles for Rekor and Axis Communications ALPR cameras"
|
||||
"content": "• NEW: Added builtin surveillance device profiles for Rekor and Axis Communications ALPR cameras\n• NEW: Smart constraint system prevents moving nodes that are part of OSM ways/relations to protect data integrity\n• NEW: \"View on OSM\" button in node details opens the node page on OpenStreetMap.org\n• NEW: \"Advanced Edit\" options with links to iD Editor, RapiD, Vespucci, StreetComplete, Go Map!!, and EveryDoor\n• IMPROVED: Network status messages are now fully localized in all supported languages\n• IMPROVED: Map interaction controls for constrained nodes - zoom/rotate allowed, movement prevented\n• IMPROVED: Debounced snap-back system keeps constrained nodes centered smoothly\n• FIXED: Eliminated duplicate changelog service calls on app updates\n• FIXED: Network status now correctly says \"Node data slow\" instead of \"Camera data slow\""
|
||||
},
|
||||
"1.3.2": {
|
||||
"content": "• HOTFIX: Temporarily disabled node editing to prevent OSM database issues while a bug is resolved\n• UX: Fixed Android navigation bar covering settings page content"
|
||||
|
||||
155
lib/widgets/advanced_edit_options_sheet.dart
Normal file
155
lib/widgets/advanced_edit_options_sheet.dart
Normal file
@@ -0,0 +1,155 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../models/osm_node.dart';
|
||||
import '../services/localization_service.dart';
|
||||
|
||||
class AdvancedEditOptionsSheet extends StatelessWidget {
|
||||
final OsmNode node;
|
||||
|
||||
const AdvancedEditOptionsSheet({super.key, required this.node});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locService = LocalizationService.instance;
|
||||
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Advanced Editing Options',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'These editors offer more advanced features for complex edits.',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Web Editors Section
|
||||
Text(
|
||||
'Web Editors',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildEditorTile(
|
||||
context: context,
|
||||
icon: Icons.public,
|
||||
title: 'iD Editor',
|
||||
subtitle: 'Full-featured web editor - always works',
|
||||
onTap: () => _launchEditor(context, 'https://www.openstreetmap.org/edit?editor=id&node=${node.id}'),
|
||||
),
|
||||
_buildEditorTile(
|
||||
context: context,
|
||||
icon: Icons.speed,
|
||||
title: 'RapiD Editor',
|
||||
subtitle: 'AI-assisted editing with Facebook data',
|
||||
onTap: () => _launchEditor(context, 'https://rapideditor.org/edit#map=19/0/0&nodes=${node.id}'),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Mobile Editors Section
|
||||
Text(
|
||||
'Mobile Editors',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
if (Platform.isAndroid) ...[
|
||||
_buildEditorTile(
|
||||
context: context,
|
||||
icon: Icons.android,
|
||||
title: 'Vespucci',
|
||||
subtitle: 'Advanced Android OSM editor',
|
||||
onTap: () => _launchEditor(context, 'vespucci://edit?node=${node.id}'),
|
||||
),
|
||||
_buildEditorTile(
|
||||
context: context,
|
||||
icon: Icons.place,
|
||||
title: 'StreetComplete',
|
||||
subtitle: 'Survey-based mapping app',
|
||||
onTap: () => _launchEditor(context, 'streetcomplete://quest?node=${node.id}'),
|
||||
),
|
||||
_buildEditorTile(
|
||||
context: context,
|
||||
icon: Icons.map,
|
||||
title: 'EveryDoor',
|
||||
subtitle: 'Fast POI editing',
|
||||
onTap: () => _launchEditor(context, 'everydoor://edit?node=${node.id}'),
|
||||
),
|
||||
],
|
||||
|
||||
if (Platform.isIOS) ...[
|
||||
_buildEditorTile(
|
||||
context: context,
|
||||
icon: Icons.phone_iphone,
|
||||
title: 'Go Map!!',
|
||||
subtitle: 'iOS OSM editor',
|
||||
onTap: () => _launchEditor(context, 'gomaposm://edit?node=${node.id}'),
|
||||
),
|
||||
],
|
||||
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(locService.t('actions.close')),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditorTile({
|
||||
required BuildContext context,
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return ListTile(
|
||||
leading: Icon(icon, size: 24),
|
||||
title: Text(title),
|
||||
subtitle: Text(subtitle),
|
||||
trailing: const Icon(Icons.launch, size: 18),
|
||||
onTap: onTap,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
||||
);
|
||||
}
|
||||
|
||||
void _launchEditor(BuildContext context, String url) async {
|
||||
Navigator.pop(context); // Close the sheet first
|
||||
|
||||
try {
|
||||
final uri = Uri.parse(url);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not open $url - app may not be installed')),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Could not open editor - app may not be installed')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import '../models/operator_profile.dart';
|
||||
import '../services/localization_service.dart';
|
||||
import '../state/settings_state.dart';
|
||||
import 'refine_tags_sheet.dart';
|
||||
import 'advanced_edit_options_sheet.dart';
|
||||
|
||||
class EditNodeSheet extends StatelessWidget {
|
||||
const EditNodeSheet({super.key, required this.session});
|
||||
@@ -214,15 +215,33 @@ class EditNodeSheet extends StatelessWidget {
|
||||
if (session.originalNode.isConstrained)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||
child: Row(
|
||||
child: Column(
|
||||
children: [
|
||||
const Icon(Icons.info_outline, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
locService.t('editNode.cannotMoveConstrainedNode'),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.info_outline, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
locService.t('editNode.cannotMoveConstrainedNode'),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => _openAdvancedEdit(context),
|
||||
icon: const Icon(Icons.open_in_new, size: 16),
|
||||
label: const Text('Use Advanced Editor'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(0, 32),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -349,4 +368,12 @@ class EditNodeSheet extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _openAdvancedEdit(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => AdvancedEditOptionsSheet(node: session.originalNode),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../models/osm_node.dart';
|
||||
import '../app_state.dart';
|
||||
import '../services/localization_service.dart';
|
||||
import 'advanced_edit_options_sheet.dart';
|
||||
|
||||
class NodeTagSheet extends StatelessWidget {
|
||||
final OsmNode node;
|
||||
@@ -67,6 +69,36 @@ class NodeTagSheet extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
void _viewOnOSM() async {
|
||||
final url = 'https://www.openstreetmap.org/node/${node.id}';
|
||||
try {
|
||||
final uri = Uri.parse(url);
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} else {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Could not open OSM website')),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Could not open OSM website')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _openAdvancedEdit() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => AdvancedEditOptionsSheet(node: node),
|
||||
);
|
||||
}
|
||||
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
|
||||
@@ -108,6 +140,30 @@ class NodeTagSheet extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// First row: View and Advanced buttons
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () => _viewOnOSM(),
|
||||
icon: const Icon(Icons.open_in_new, size: 16),
|
||||
label: const Text('View on OSM'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
if (isEditable) ...[
|
||||
OutlinedButton.icon(
|
||||
onPressed: _openAdvancedEdit,
|
||||
icon: const Icon(Icons.open_in_new, size: 18),
|
||||
label: const Text('Advanced'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size(0, 36),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// Second row: Edit, Delete, and Close buttons
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
|
||||
Reference in New Issue
Block a user