Popup message before submitting first node

This commit is contained in:
stopflock
2025-11-22 14:56:05 -06:00
parent 7ff04851f4
commit 961465ebb5
15 changed files with 301 additions and 2 deletions
+23
View File
@@ -7,8 +7,10 @@ import '../models/node_profile.dart';
import '../models/operator_profile.dart';
import '../services/localization_service.dart';
import '../services/node_cache.dart';
import '../services/changelog_service.dart';
import 'refine_tags_sheet.dart';
import 'proximity_warning_dialog.dart';
import 'submission_guide_dialog.dart';
class AddNodeSheet extends StatelessWidget {
const AddNodeSheet({super.key, required this.session});
@@ -16,6 +18,27 @@ class AddNodeSheet extends StatelessWidget {
final AddNodeSession session;
void _checkProximityAndCommit(BuildContext context, AppState appState, LocalizationService locService) {
_checkSubmissionGuideAndProceed(context, appState, locService);
}
void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async {
// Check if user has seen the submission guide
final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide();
if (!hasSeenGuide) {
// Show submission guide dialog first
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(),
);
}
// Now proceed with proximity check
_checkProximityOnly(context, appState, locService);
}
void _checkProximityOnly(BuildContext context, AppState appState, LocalizationService locService) {
// Only check proximity if we have a target location
if (session.target == null) {
_commitWithoutCheck(context, appState, locService);
+23
View File
@@ -7,10 +7,12 @@ import '../models/node_profile.dart';
import '../models/operator_profile.dart';
import '../services/localization_service.dart';
import '../services/node_cache.dart';
import '../services/changelog_service.dart';
import '../state/settings_state.dart';
import 'refine_tags_sheet.dart';
import 'advanced_edit_options_sheet.dart';
import 'proximity_warning_dialog.dart';
import 'submission_guide_dialog.dart';
class EditNodeSheet extends StatelessWidget {
const EditNodeSheet({super.key, required this.session});
@@ -18,6 +20,27 @@ class EditNodeSheet extends StatelessWidget {
final EditNodeSession session;
void _checkProximityAndCommit(BuildContext context, AppState appState, LocalizationService locService) {
_checkSubmissionGuideAndProceed(context, appState, locService);
}
void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async {
// Check if user has seen the submission guide
final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide();
if (!hasSeenGuide) {
// Show submission guide dialog first
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(),
);
}
// Now proceed with proximity check
_checkProximityOnly(context, appState, locService);
}
void _checkProximityOnly(BuildContext context, AppState appState, LocalizationService locService) {
// Check for nearby nodes within the configured distance, excluding the node being edited
final nearbyNodes = NodeCache.instance.findNodesWithinDistance(
session.target,
+155
View File
@@ -0,0 +1,155 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../services/changelog_service.dart';
import '../services/localization_service.dart';
class SubmissionGuideDialog extends StatefulWidget {
const SubmissionGuideDialog({super.key});
@override
State<SubmissionGuideDialog> createState() => _SubmissionGuideDialogState();
}
class _SubmissionGuideDialogState extends State<SubmissionGuideDialog> {
bool _dontShowAgain = false;
Future<void> _launchUrl(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
void _onClose() async {
if (_dontShowAgain) {
await ChangelogService().markSubmissionGuideSeen();
}
if (mounted) {
Navigator.of(context).pop();
}
}
@override
Widget build(BuildContext context) {
final locService = LocalizationService.instance;
return AnimatedBuilder(
animation: LocalizationService.instance,
builder: (context, child) => AlertDialog(
title: Text(locService.t('submissionGuide.title')),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Scrollable content
Flexible(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
locService.t('submissionGuide.description'),
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue.withOpacity(0.3)),
),
child: Text(
locService.t('submissionGuide.bestPractices'),
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
),
),
const SizedBox(height: 12),
Text(
locService.t('submissionGuide.placementNote'),
style: const TextStyle(fontSize: 13, fontStyle: FontStyle.italic),
),
const SizedBox(height: 16),
Text(
locService.t('submissionGuide.moreInfo'),
style: const TextStyle(fontSize: 13),
),
const SizedBox(height: 16),
// Resource links row
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildLinkButton(
locService.t('submissionGuide.identificationGuide'),
'https://deflock.me/identify'
),
_buildLinkButton(
locService.t('submissionGuide.osmWiki'),
'https://wiki.openstreetmap.org/wiki/Tag:man_made%3Dsurveillance'
),
],
),
],
),
),
),
const SizedBox(height: 16),
// Always visible checkbox at the bottom
Row(
children: [
Checkbox(
value: _dontShowAgain,
onChanged: (value) {
setState(() {
_dontShowAgain = value ?? false;
});
},
),
Expanded(
child: Text(
locService.t('submissionGuide.dontShowAgain'),
style: const TextStyle(fontSize: 13),
),
),
],
),
],
),
actions: [
TextButton(
onPressed: _onClose,
child: Text(locService.t('submissionGuide.gotIt')),
),
],
),
);
}
Widget _buildLinkButton(String text, String url) {
return Flexible(
child: GestureDetector(
onTap: () => _launchUrl(url),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
),
),
child: Text(
text,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.primary,
),
textAlign: TextAlign.center,
),
),
),
);
}
}