Add buttons to show welcome message and submission guide on command from about section of settings

This commit is contained in:
stopflock
2025-11-22 17:19:57 -06:00
parent 961465ebb5
commit c6cc68c9b4
11 changed files with 195 additions and 71 deletions
+4 -1
View File
@@ -2,7 +2,10 @@
"1.5.0": {
"content": [
"• NEW: First-submission guide popup - provides essential guidance and links before your first device submission",
"• IMPROVED: Better onboarding for new contributors with links to identification guides and OSM wiki resources"
"• NEW: Manual access to dialogs in Settings > About - view welcome message and submission guide anytime",
"• IMPROVED: Better onboarding for new contributors with links to identification guides and OSM wiki resources",
"• IMPROVED: More descriptive button labels for external resources",
"• IMPROVED: Consistent button styling in About page"
]
},
"1.4.6": {
+6 -3
View File
@@ -392,7 +392,10 @@
"description": "DeFlock ist eine datenschutzorientierte mobile App zur Kartierung öffentlicher Überwachungsinfrastruktür mit OpenStreetMap. Dokumentieren Sie Kameras, ALPRs, Schussdetektoren und andere Überwachungsgeräte in Ihrer Gemeinde, um diese Infrastruktur sichtbar und durchsuchbar zu machen.",
"features": "• Offline-fähige Kartierung mit herunterladbaren Bereichen\n• Direkter Upload zu OpenStreetMap mit OAuth2\n• Integrierte Profile für große Hersteller\n• Datenschutzfreundlich - keine Nutzerdaten gesammelt\n• Multiple Kartenanbieter (OSM, Satellitenbilder)",
"initiative": "Teil der breiteren DeFlock-Initiative zur Förderung von Überwachungstransparenz.",
"footer": "Besuchen Sie: deflock.me\nGebaut mit Flutter • Open Source"
"footer": "Besuchen Sie: deflock.me\nGebaut mit Flutter • Open Source",
"showWelcome": "Willkommensnachricht anzeigen",
"showSubmissionGuide": "Einreichungsleitfaden anzeigen",
"viewReleaseNotes": "Release-Notizen anzeigen"
},
"welcome": {
"title": "Willkommen bei DeFlock",
@@ -411,8 +414,8 @@
"bestPractices": "• Nur Geräte erfassen, die Sie persönlich beobachtet haben\n• Zeit nehmen für genaue Identifikation von Typ und Hersteller\n• Präzise Positionierung - nah heranzoomen vor Markierung\n• Richtungsinformationen angeben, falls zutreffend\n• Tag-Auswahl vor dem Senden überprüfen",
"placementNote": "Denken Sie daran: Genaue, persönlich verifizierte Daten sind essentiell für die DeFlock-Community und das OpenStreetMap-Projekt.",
"moreInfo": "Für detaillierte Anleitungen zur Geräteerkennung und Kartierung:",
"identificationGuide": "ID-Leitfaden",
"osmWiki": "OSM Wiki",
"identificationGuide": "Identifikationsleitfaden",
"osmWiki": "OpenStreetMap Wiki",
"dontShowAgain": "Diese Anleitung nicht mehr anzeigen",
"gotIt": "Verstanden!"
},
+6 -3
View File
@@ -10,7 +10,10 @@
"description": "DeFlock is a privacy-focused mobile app for mapping public surveillance infrastructure using OpenStreetMap. Document cameras, ALPRs, gunshot detectors, and other surveillance devices in your community to make this infrastructure visible and searchable.",
"features": "• Offline-capable mapping with downloadable areas\n• Upload directly to OpenStreetMap with OAuth2\n• Built-in profiles for major manufacturers\n• Privacy-respecting - no user data collected\n• Multiple map tile providers (OSM, satellite imagery)",
"initiative": "Part of the broader DeFlock initiative to promote surveillance transparency.",
"footer": "Visit: deflock.me\nBuilt with Flutter • Open Source"
"footer": "Visit: deflock.me\nBuilt with Flutter • Open Source",
"showWelcome": "Show Welcome Message",
"showSubmissionGuide": "Show Submission Guide",
"viewReleaseNotes": "View Release Notes"
},
"welcome": {
"title": "Welcome to DeFlock",
@@ -29,8 +32,8 @@
"bestPractices": "• Only map devices you've personally observed firsthand\n• Take time to accurately identify the device type and manufacturer\n• Use precise positioning - zoom in close before placing the marker\n• Include direction information when applicable\n• Double-check your tag selections before submitting",
"placementNote": "Remember: Accurate, first-hand data is essential for the DeFlock community and OpenStreetMap project.",
"moreInfo": "For detailed guidance on device identification and mapping best practices:",
"identificationGuide": "ID Guide",
"osmWiki": "OSM Wiki",
"identificationGuide": "Identification Guide",
"osmWiki": "OpenStreetMap Wiki",
"dontShowAgain": "Don't show this guide again",
"gotIt": "Got It!"
},
+6 -3
View File
@@ -10,7 +10,10 @@
"description": "DeFlock es una aplicación móvil enfocada en la privacidad para mapear infraestructura de vigilancia pública usando OpenStreetMap. Documenta cámaras, ALPRs, detectores de disparos y otros dispositivos de vigilancia en tu comunidad para hacer visible y consultable esta infraestructura.",
"features": "• Mapeo con capacidad offline con áreas descargables\n• Subida directa a OpenStreetMap con OAuth2\n• Perfiles integrados para fabricantes principales\n• Respeta la privacidad - no se recopilan datos del usuario\n• Múltiples proveedores de mapas (OSM, imágenes satelitales)",
"initiative": "Parte de la iniciativa más amplia DeFlock para promover la transparencia en vigilancia.",
"footer": "Visita: deflock.me\nConstruido con Flutter • Código Abierto"
"footer": "Visita: deflock.me\nConstruido con Flutter • Código Abierto",
"showWelcome": "Mostrar Mensaje de Bienvenida",
"showSubmissionGuide": "Mostrar Guía de Envío",
"viewReleaseNotes": "Ver Notas de Lanzamiento"
},
"welcome": {
"title": "Bienvenido a DeFlock",
@@ -29,8 +32,8 @@
"bestPractices": "• Solo mapee dispositivos que haya observado personalmente\n• Tómese tiempo para identificar con precisión el tipo y fabricante\n• Use posicionamiento preciso - acerque antes de colocar el marcador\n• Incluya información de dirección cuando sea aplicable\n• Verifique sus selecciones de etiquetas antes de enviar",
"placementNote": "Recuerde: Los datos precisos y de primera mano son esenciales para la comunidad DeFlock y el proyecto OpenStreetMap.",
"moreInfo": "Para orientación detallada sobre identificación de dispositivos y mejores prácticas de mapeo:",
"identificationGuide": "Guía de ID",
"osmWiki": "Wiki OSM",
"identificationGuide": "Guía de Identificación",
"osmWiki": "Wiki de OpenStreetMap",
"dontShowAgain": "No mostrar esta guía otra vez",
"gotIt": "¡Entendido!"
},
+6 -3
View File
@@ -10,7 +10,10 @@
"description": "DeFlock est une application mobile axée sur la confidentialité pour cartographier l'infrastructure de surveillance publique en utilisant OpenStreetMap. Documentez les caméras, ALPRs, détecteurs de coups de feu et autres dispositifs de surveillance dans votre communauté pour rendre cette infrastructure visible et consultable.",
"features": "• Cartographie hors ligne avec zones téléchargeables\n• Upload direct vers OpenStreetMap avec OAuth2\n• Profils intégrés pour les principaux fabricants\n• Respectueux de la confidentialité - aucune donnée utilisateur collectée\n• Multiples fournisseurs de cartes (OSM, imagerie satellite)",
"initiative": "Partie de l'initiative plus large DeFlock pour promouvoir la transparence de la surveillance.",
"footer": "Visitez : deflock.me\nConstruit avec Flutter • Source Ouverte"
"footer": "Visitez : deflock.me\nConstruit avec Flutter • Source Ouverte",
"showWelcome": "Afficher le Message de Bienvenue",
"showSubmissionGuide": "Afficher le Guide de Soumission",
"viewReleaseNotes": "Voir les Notes de Version"
},
"welcome": {
"title": "Bienvenue dans DeFlock",
@@ -29,8 +32,8 @@
"bestPractices": "• Ne cartographiez que les dispositifs que vous avez observés personnellement\n• Prenez le temps d'identifier avec précision le type et le fabricant\n• Utilisez un positionnement précis - zoomez avant de placer le marqueur\n• Incluez les informations de direction quand c'est applicable\n• Vérifiez vos sélections d'étiquettes avant de soumettre",
"placementNote": "Rappelez-vous : Des données précises et de première main sont essentielles pour la communauté DeFlock et le projet OpenStreetMap.",
"moreInfo": "Pour des conseils détaillés sur l'identification des dispositifs et les meilleures pratiques de cartographie :",
"identificationGuide": "Guide ID",
"osmWiki": "Wiki OSM",
"identificationGuide": "Guide d'Identification",
"osmWiki": "Wiki OpenStreetMap",
"dontShowAgain": "Ne plus afficher ce guide",
"gotIt": "Compris !"
},
+6 -3
View File
@@ -10,7 +10,10 @@
"description": "DeFlock è un'app mobile orientata alla privacy per mappare l'infrastruttura di sorveglianza pubblica utilizzando OpenStreetMap. Documenta telecamere, ALPR, rilevatori di spari e altri dispositivi di sorveglianza nella tua comunità per rendere questa infrastruttura visibile e ricercabile.",
"features": "• Mappatura con capacità offline con aree scaricabili\n• Upload diretto su OpenStreetMap con OAuth2\n• Profili integrati per i principali produttori\n• Rispettoso della privacy - nessun dato utente raccolto\n• Multipli fornitori di mappe (OSM, immagini satellitari)",
"initiative": "Parte della più ampia iniziativa DeFlock per promuovere la trasparenza della sorveglianza.",
"footer": "Visita: deflock.me\nCostruito con Flutter • Open Source"
"footer": "Visita: deflock.me\nCostruito con Flutter • Open Source",
"showWelcome": "Mostra Messaggio di Benvenuto",
"showSubmissionGuide": "Mostra Guida di Invio",
"viewReleaseNotes": "Visualizza Note di Rilascio"
},
"welcome": {
"title": "Benvenuto in DeFlock",
@@ -29,8 +32,8 @@
"bestPractices": "• Mappa solo dispositivi che hai osservato personalmente\n• Prenditi tempo per identificare accuratamente tipo e produttore\n• Usa posizionamento preciso - ingrandisci prima di piazzare il marcatore\n• Includi informazioni sulla direzione quando applicabile\n• Controlla le tue selezioni di tag prima di inviare",
"placementNote": "Ricorda: Dati accurati e di prima mano sono essenziali per la comunità DeFlock e il progetto OpenStreetMap.",
"moreInfo": "Per una guida dettagliata sull'identificazione dei dispositivi e le migliori pratiche di mappatura:",
"identificationGuide": "Guida ID",
"osmWiki": "Wiki OSM",
"identificationGuide": "Guida di Identificazione",
"osmWiki": "Wiki OpenStreetMap",
"dontShowAgain": "Non mostrare più questa guida",
"gotIt": "Capito!"
},
+6 -3
View File
@@ -10,7 +10,10 @@
"description": "DeFlock é um aplicativo móvel focado na privacidade para mapear infraestrutura de vigilância pública usando OpenStreetMap. Documente câmeras, ALPRs, detectores de tiros e outros dispositivos de vigilância em sua comunidade para tornar essa infraestrutura visível e pesquisável.",
"features": "• Mapeamento com capacidade offline com áreas para download\n• Upload direto para OpenStreetMap com OAuth2\n• Perfis integrados para principais fabricantes\n• Respeitoso à privacidade - nenhum dado do usuário coletado\n• Múltiplos provedores de mapas (OSM, imagens de satélite)",
"initiative": "Parte da iniciativa mais ampla DeFlock para promover transparência na vigilância.",
"footer": "Visite: deflock.me\nConstruído com Flutter • Código Aberto"
"footer": "Visite: deflock.me\nConstruído com Flutter • Código Aberto",
"showWelcome": "Mostrar Mensagem de Boas-vindas",
"showSubmissionGuide": "Mostrar Guia de Submissão",
"viewReleaseNotes": "Ver Notas de Lançamento"
},
"welcome": {
"title": "Bem-vindo ao DeFlock",
@@ -29,8 +32,8 @@
"bestPractices": "• Mapear apenas dispositivos que você observou pessoalmente\n• Dedicar tempo para identificar com precisão tipo e fabricante\n• Usar posicionamento preciso - aproximar antes de colocar o marcador\n• Incluir informações de direção quando aplicável\n• Verificar suas seleções de tags antes de submeter",
"placementNote": "Lembre-se: Dados precisos e de primeira mão são essenciais para a comunidade DeFlock e o projeto OpenStreetMap.",
"moreInfo": "Para orientação detalhada sobre identificação de dispositivos e melhores práticas de mapeamento:",
"identificationGuide": "Guia de ID",
"osmWiki": "Wiki OSM",
"identificationGuide": "Guia de Identificação",
"osmWiki": "Wiki OpenStreetMap",
"dontShowAgain": "Não mostrar este guia novamente",
"gotIt": "Entendi!"
},
+5 -2
View File
@@ -10,7 +10,10 @@
"description": "DeFlock 是一款注重隐私的移动应用,使用 OpenStreetMap 绘制公共监控基础设施。记录您社区中的摄像头、车牌识别系统、枪击探测器和其他监控设备,使这些基础设施可见且可搜索。",
"features": "• 具有可下载区域的离线映射功能\n• 使用 OAuth2 直接上传到 OpenStreetMap\n• 主要制造商的内置配置文件\n• 尊重隐私 - 不收集用户数据\n• 多个地图提供商(OSM、卫星图像)",
"initiative": "DeFlock 更广泛倡议的一部分,旨在促进监控透明化。",
"footer": "访问:deflock.me\n使用 Flutter 构建 • 开源"
"footer": "访问:deflock.me\n使用 Flutter 构建 • 开源",
"showWelcome": "显示欢迎消息",
"showSubmissionGuide": "显示提交指南",
"viewReleaseNotes": "查看发布说明"
},
"welcome": {
"title": "欢迎使用 DeFlock",
@@ -30,7 +33,7 @@
"placementNote": "请记住:准确的第一手数据对 DeFlock 社区和 OpenStreetMap 项目至关重要。",
"moreInfo": "有关设备识别和映射最佳实践的详细指导:",
"identificationGuide": "识别指南",
"osmWiki": "OSM Wiki",
"osmWiki": "OpenStreetMap Wiki",
"dontShowAgain": "不再显示此指南",
"gotIt": "明白了!"
},
+50 -10
View File
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../services/localization_service.dart';
import '../widgets/welcome_dialog.dart';
import '../widgets/submission_guide_dialog.dart';
class AboutScreen extends StatelessWidget {
const AboutScreen({super.key});
@@ -74,16 +76,8 @@ class AboutScreen extends StatelessWidget {
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
// Release Notes button
Center(
child: OutlinedButton.icon(
onPressed: () {
Navigator.pushNamed(context, '/settings/release-notes');
},
icon: const Icon(Icons.article_outlined),
label: const Text('View Release Notes'),
),
),
// Information dialogs section
_buildDialogButtons(context),
const SizedBox(height: 24),
_buildHelpLinks(context),
],
@@ -190,4 +184,50 @@ class AboutScreen extends StatelessWidget {
),
);
}
Widget _buildDialogButtons(BuildContext context) {
final locService = LocalizationService.instance;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Welcome Message button
OutlinedButton.icon(
onPressed: () {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => const WelcomeDialog(showDontShowAgain: false),
);
},
icon: const Icon(Icons.waving_hand_outlined),
label: Text(locService.t('about.showWelcome')),
),
const SizedBox(height: 8),
// Submission Guide button
OutlinedButton.icon(
onPressed: () {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(showDontShowAgain: false),
);
},
icon: const Icon(Icons.info_outline),
label: Text(locService.t('about.showSubmissionGuide')),
),
const SizedBox(height: 8),
// Release Notes button
OutlinedButton.icon(
onPressed: () {
Navigator.pushNamed(context, '/settings/release-notes');
},
icon: const Icon(Icons.article_outlined),
label: Text(locService.t('about.viewReleaseNotes')),
),
],
);
}
}
+50 -20
View File
@@ -4,7 +4,9 @@ import '../services/changelog_service.dart';
import '../services/localization_service.dart';
class SubmissionGuideDialog extends StatefulWidget {
const SubmissionGuideDialog({super.key});
const SubmissionGuideDialog({super.key, this.showDontShowAgain = true});
final bool showDontShowAgain;
@override
State<SubmissionGuideDialog> createState() => _SubmissionGuideDialogState();
@@ -12,6 +14,7 @@ class SubmissionGuideDialog extends StatefulWidget {
class _SubmissionGuideDialogState extends State<SubmissionGuideDialog> {
bool _dontShowAgain = false;
bool _isInitialized = false;
Future<void> _launchUrl(String url) async {
final uri = Uri.parse(url);
@@ -20,8 +23,29 @@ class _SubmissionGuideDialogState extends State<SubmissionGuideDialog> {
}
}
@override
void initState() {
super.initState();
_loadCurrentState();
}
Future<void> _loadCurrentState() async {
if (!widget.showDontShowAgain) {
// When manually opened, show the actual current state
final hasSeenSubmissionGuide = await ChangelogService().hasSeenSubmissionGuide();
setState(() {
_dontShowAgain = hasSeenSubmissionGuide;
_isInitialized = true;
});
} else {
setState(() {
_isInitialized = true;
});
}
}
void _onClose() async {
if (_dontShowAgain) {
if (_dontShowAgain && widget.showDontShowAgain) {
await ChangelogService().markSubmissionGuideSeen();
}
@@ -96,25 +120,31 @@ class _SubmissionGuideDialogState extends State<SubmissionGuideDialog> {
),
),
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),
// Always visible checkbox, but disabled when manually opened
if (_isInitialized)
Row(
children: [
Checkbox(
value: _dontShowAgain,
onChanged: widget.showDontShowAgain ? (value) {
setState(() {
_dontShowAgain = value ?? false;
});
} : null,
),
),
],
),
Expanded(
child: Text(
locService.t('submissionGuide.dontShowAgain'),
style: TextStyle(
fontSize: 13,
color: widget.showDontShowAgain
? null
: Theme.of(context).disabledColor,
),
),
),
],
),
],
),
actions: [
+50 -20
View File
@@ -4,7 +4,9 @@ import '../services/changelog_service.dart';
import '../services/localization_service.dart';
class WelcomeDialog extends StatefulWidget {
const WelcomeDialog({super.key});
const WelcomeDialog({super.key, this.showDontShowAgain = true});
final bool showDontShowAgain;
@override
State<WelcomeDialog> createState() => _WelcomeDialogState();
@@ -12,6 +14,7 @@ class WelcomeDialog extends StatefulWidget {
class _WelcomeDialogState extends State<WelcomeDialog> {
bool _dontShowAgain = false;
bool _isInitialized = false;
Future<void> _launchUrl(String url) async {
final uri = Uri.parse(url);
@@ -20,8 +23,29 @@ class _WelcomeDialogState extends State<WelcomeDialog> {
}
}
@override
void initState() {
super.initState();
_loadCurrentState();
}
Future<void> _loadCurrentState() async {
if (!widget.showDontShowAgain) {
// When manually opened, show the actual current state
final hasSeenWelcome = await ChangelogService().hasSeenWelcome();
setState(() {
_dontShowAgain = hasSeenWelcome;
_isInitialized = true;
});
} else {
setState(() {
_isInitialized = true;
});
}
}
void _onClose() async {
if (_dontShowAgain) {
if (_dontShowAgain && widget.showDontShowAgain) {
await ChangelogService().markWelcomeSeen();
}
@@ -103,25 +127,31 @@ class _WelcomeDialogState extends State<WelcomeDialog> {
),
),
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('welcome.dontShowAgain'),
style: const TextStyle(fontSize: 13),
// Always visible checkbox, but disabled when manually opened
if (_isInitialized)
Row(
children: [
Checkbox(
value: _dontShowAgain,
onChanged: widget.showDontShowAgain ? (value) {
setState(() {
_dontShowAgain = value ?? false;
});
} : null,
),
),
],
),
Expanded(
child: Text(
locService.t('welcome.dontShowAgain'),
style: TextStyle(
fontSize: 13,
color: widget.showDontShowAgain
? null
: Theme.of(context).disabledColor,
),
),
),
],
),
],
),
actions: [