From 5d943ff1a40731100238cfb02d4bea466c073451 Mon Sep 17 00:00:00 2001 From: Janez T Date: Tue, 28 Oct 2025 11:20:30 +0100 Subject: [PATCH] feat: Add welcome wizard for new users - Implemented a multi-page welcome wizard to introduce new users to the app's features. - Added localization support for the wizard in English, Spanish, French, Croatian, Italian, and Slovenian. - Integrated wizard completion state management using SharedPreferences. - Updated main app flow to show the wizard if it hasn't been completed. - Added a settings option to view the welcome tutorial again. --- .github/workflows/build-multiplatform.yml | 26 +- lib/l10n/app_de.arb | 35 +- lib/l10n/app_en.arb | 160 +++++++++ lib/l10n/app_es.arb | 35 +- lib/l10n/app_fr.arb | 35 +- lib/l10n/app_hr.arb | 35 +- lib/l10n/app_it.arb | 35 +- lib/l10n/app_localizations.dart | 192 +++++++++++ lib/l10n/app_localizations_de.dart | 115 +++++++ lib/l10n/app_localizations_en.dart | 109 ++++++ lib/l10n/app_localizations_es.dart | 114 +++++++ lib/l10n/app_localizations_fr.dart | 115 +++++++ lib/l10n/app_localizations_hr.dart | 113 ++++++ lib/l10n/app_localizations_it.dart | 114 +++++++ lib/l10n/app_localizations_sl.dart | 114 +++++++ lib/l10n/app_sl.arb | 35 +- lib/main.dart | 32 +- lib/screens/settings_screen.dart | 20 ++ lib/screens/welcome_wizard_screen.dart | 399 ++++++++++++++++++++++ lib/services/wizard_preferences.dart | 42 +++ 20 files changed, 1856 insertions(+), 19 deletions(-) create mode 100644 lib/screens/welcome_wizard_screen.dart create mode 100644 lib/services/wizard_preferences.dart diff --git a/.github/workflows/build-multiplatform.yml b/.github/workflows/build-multiplatform.yml index cb27a2e..084bfde 100644 --- a/.github/workflows/build-multiplatform.yml +++ b/.github/workflows/build-multiplatform.yml @@ -583,8 +583,15 @@ jobs: id: commits run: | # Get last 10 commit messages with hash and date - git log -10 --pretty=format:'{"hash":"%h","date":"%ci","message":"%s","author":"%an"}' | \ - jq -s '.' > commits.json + # Use jq to properly escape all strings + git log -10 --pretty=format:'%h|%ci|%s|%an' | while IFS='|' read -r hash date message author; do + jq -n \ + --arg hash "$hash" \ + --arg date "$date" \ + --arg message "$message" \ + --arg author "$author" \ + '{hash: $hash, date: $date, message: $message, author: $author}' + done | jq -s '.' > commits.json # Also create a simple text version for easier parsing git log -10 --pretty=format:'%h|%ci|%s|%an' > commits.txt @@ -1099,6 +1106,13 @@ jobs: // Embedded commits data (no fetch needed) const commitsData = ${COMMITS_DATA}; + // HTML escape function to prevent XSS + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + // Display commits (function() { const list = document.getElementById('commits-list'); @@ -1120,11 +1134,11 @@ jobs: return \`
- \${commit.hash} - \${dateStr} + \${escapeHtml(commit.hash)} + \${escapeHtml(dateStr)}
-
\${commit.message}
-
by \${commit.author}
+
\${escapeHtml(commit.message)}
+
by \${escapeHtml(commit.author)}
\`; }).join(''); diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index b13cb33..8634009 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -2728,5 +2728,38 @@ "channelCreatedSuccessfully": "Kanal erfolgreich erstellt", "channelCreationFailed": "Kanal konnte nicht erstellt werden: {error}", "allChannelSlotsInUse": "Alle Kanalplätze sind belegt (maximal 39 benutzerdefinierte Kanäle)", - "createChannel": "Kanal erstellen" + "createChannel": "Kanal erstellen", + + "wizardBack": "Zurück", + "wizardSkip": "Überspringen", + "wizardNext": "Weiter", + "wizardGetStarted": "Loslegen", + "wizardWelcomeTitle": "Willkommen bei MeshCore SAR", + "wizardWelcomeDescription": "Ein leistungsstarkes Offline-Kommunikationstool für Such- und Rettungseinsätze. Verbinden Sie sich mit Ihrem Team über Mesh-Funktechnologie, wenn herkömmliche Netzwerke nicht verfügbar sind.", + "wizardConnectingTitle": "Verbindung zum Radio", + "wizardConnectingDescription": "Verbinden Sie Ihr Smartphone über Bluetooth mit einem MeshCore-Funkgerät, um offline zu kommunizieren.", + "wizardConnectingFeature1": "Nach MeshCore-Geräten in der Nähe suchen", + "wizardConnectingFeature2": "Mit Ihrem Funkgerät über Bluetooth koppeln", + "wizardConnectingFeature3": "Funktioniert vollständig offline - kein Internet erforderlich", + "wizardSimpleModeTitle": "Einfacher Modus", + "wizardSimpleModeDescription": "Neu im Mesh-Netzwerk? Aktivieren Sie den einfachen Modus für eine optimierte Benutzeroberfläche mit nur wesentlichen Funktionen.", + "wizardSimpleModeFeature1": "Anfängerfreundliche Benutzeroberfläche mit Kernfunktionen", + "wizardSimpleModeFeature2": "Jederzeit in den erweiterten Modus in den Einstellungen wechseln", + "wizardChannelTitle": "Kanäle", + "wizardChannelDescription": "Senden Sie Nachrichten an alle auf einem Kanal, perfekt für teamweite Ankündigungen und Koordination.", + "wizardChannelFeature1": "Öffentlicher Kanal für allgemeine Teamkommunikation", + "wizardChannelFeature2": "Erstellen Sie benutzerdefinierte Kanäle für bestimmte Gruppen", + "wizardChannelFeature3": "Nachrichten werden automatisch über das Mesh weitergeleitet", + "wizardContactsTitle": "Kontakte", + "wizardContactsDescription": "Ihre Teammitglieder erscheinen automatisch, wenn sie dem Mesh-Netzwerk beitreten. Senden Sie ihnen direkte Nachrichten oder sehen Sie ihren Standort.", + "wizardContactsFeature1": "Kontakte werden automatisch erkannt", + "wizardContactsFeature2": "Private Direktnachrichten senden", + "wizardContactsFeature3": "Batteriestand und letzte Aktivität anzeigen", + "wizardMapTitle": "Karte & Standort", + "wizardMapDescription": "Verfolgen Sie Ihr Team in Echtzeit und markieren Sie wichtige Standorte für Such- und Rettungseinsätze.", + "wizardMapFeature1": "SAR-Markierungen für gefundene Personen, Feuer und Sammelstellen", + "wizardMapFeature2": "GPS-Verfolgung von Teammitgliedern in Echtzeit", + "wizardMapFeature3": "Offline-Karten für entlegene Gebiete herunterladen", + "wizardMapFeature4": "Formen zeichnen und taktische Informationen teilen", + "viewWelcomeTutorial": "Willkommens-Tutorial ansehen" } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0f85723..5277cfc 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -3426,5 +3426,165 @@ "createChannel": "Create Channel", "@createChannel": { "description": "Button text for creating a channel" + }, + + "wizardBack": "Back", + "@wizardBack": { + "description": "Wizard back button text" + }, + + "wizardSkip": "Skip", + "@wizardSkip": { + "description": "Wizard skip button text" + }, + + "wizardNext": "Next", + "@wizardNext": { + "description": "Wizard next button text" + }, + + "wizardGetStarted": "Get Started", + "@wizardGetStarted": { + "description": "Wizard final button text to complete onboarding" + }, + + "wizardWelcomeTitle": "Welcome to MeshCore SAR", + "@wizardWelcomeTitle": { + "description": "Welcome wizard first page title" + }, + + "wizardWelcomeDescription": "A powerful off-grid communication tool for search and rescue operations. Connect with your team using mesh radio technology when traditional networks are unavailable.", + "@wizardWelcomeDescription": { + "description": "Welcome wizard first page description" + }, + + "wizardConnectingTitle": "Connecting to Your Radio", + "@wizardConnectingTitle": { + "description": "Wizard connecting page title" + }, + + "wizardConnectingDescription": "Connect your smartphone to a MeshCore radio device via Bluetooth to start communicating off-grid.", + "@wizardConnectingDescription": { + "description": "Wizard connecting page description" + }, + + "wizardConnectingFeature1": "Scan for nearby MeshCore devices", + "@wizardConnectingFeature1": { + "description": "Wizard connecting feature 1" + }, + + "wizardConnectingFeature2": "Pair with your radio via Bluetooth", + "@wizardConnectingFeature2": { + "description": "Wizard connecting feature 2" + }, + + "wizardConnectingFeature3": "Works completely offline - no internet required", + "@wizardConnectingFeature3": { + "description": "Wizard connecting feature 3" + }, + + "wizardSimpleModeTitle": "Simple Mode", + "@wizardSimpleModeTitle": { + "description": "Wizard simple mode page title" + }, + + "wizardSimpleModeDescription": "New to mesh networking? Enable Simple Mode for a streamlined interface with essential features only.", + "@wizardSimpleModeDescription": { + "description": "Wizard simple mode page description" + }, + + "wizardSimpleModeFeature1": "Beginner-friendly interface with core functions", + "@wizardSimpleModeFeature1": { + "description": "Wizard simple mode feature 1" + }, + + "wizardSimpleModeFeature2": "Switch to Advanced Mode anytime in Settings", + "@wizardSimpleModeFeature2": { + "description": "Wizard simple mode feature 2" + }, + + "wizardChannelTitle": "Channels", + "@wizardChannelTitle": { + "description": "Wizard channel page title" + }, + + "wizardChannelDescription": "Broadcast messages to everyone on a channel, perfect for team-wide announcements and coordination.", + "@wizardChannelDescription": { + "description": "Wizard channel page description" + }, + + "wizardChannelFeature1": "Public Channel for general team communication", + "@wizardChannelFeature1": { + "description": "Wizard channel feature 1" + }, + + "wizardChannelFeature2": "Create custom channels for specific groups", + "@wizardChannelFeature2": { + "description": "Wizard channel feature 2" + }, + + "wizardChannelFeature3": "Messages are automatically relayed by the mesh", + "@wizardChannelFeature3": { + "description": "Wizard channel feature 3" + }, + + "wizardContactsTitle": "Contacts", + "@wizardContactsTitle": { + "description": "Wizard contacts page title" + }, + + "wizardContactsDescription": "Your team members appear automatically as they join the mesh network. Send them direct messages or view their location.", + "@wizardContactsDescription": { + "description": "Wizard contacts page description" + }, + + "wizardContactsFeature1": "Contacts discovered automatically", + "@wizardContactsFeature1": { + "description": "Wizard contacts feature 1" + }, + + "wizardContactsFeature2": "Send private direct messages", + "@wizardContactsFeature2": { + "description": "Wizard contacts feature 2" + }, + + "wizardContactsFeature3": "View battery level and last seen time", + "@wizardContactsFeature3": { + "description": "Wizard contacts feature 3" + }, + + "wizardMapTitle": "Map & Location", + "@wizardMapTitle": { + "description": "Wizard map page title" + }, + + "wizardMapDescription": "Track your team in real-time and mark important locations for search and rescue operations.", + "@wizardMapDescription": { + "description": "Wizard map page description" + }, + + "wizardMapFeature1": "SAR markers for found persons, fires, and staging areas", + "@wizardMapFeature1": { + "description": "Wizard map feature 1" + }, + + "wizardMapFeature2": "Real-time GPS tracking of team members", + "@wizardMapFeature2": { + "description": "Wizard map feature 2" + }, + + "wizardMapFeature3": "Download offline maps for remote areas", + "@wizardMapFeature3": { + "description": "Wizard map feature 3" + }, + + "wizardMapFeature4": "Draw shapes and share tactical information", + "@wizardMapFeature4": { + "description": "Wizard map feature 4" + }, + + "viewWelcomeTutorial": "View Welcome Tutorial", + "@viewWelcomeTutorial": { + "description": "Settings option to re-show welcome wizard" } } diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 0dd57cf..efe2888 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -2723,5 +2723,38 @@ "channelCreatedSuccessfully": "Canal creado exitosamente", "channelCreationFailed": "Error al crear el canal: {error}", "allChannelSlotsInUse": "Todos los espacios de canales están en uso (máximo 39 canales personalizados)", - "createChannel": "Crear Canal" + "createChannel": "Crear Canal", + + "wizardBack": "Atrás", + "wizardSkip": "Omitir", + "wizardNext": "Siguiente", + "wizardGetStarted": "Comenzar", + "wizardWelcomeTitle": "Bienvenido a MeshCore SAR", + "wizardWelcomeDescription": "Una poderosa herramienta de comunicación sin conexión para operaciones de búsqueda y rescate. Conéctese con su equipo usando tecnología de radio en malla cuando las redes tradicionales no estén disponibles.", + "wizardConnectingTitle": "Conectando a su Radio", + "wizardConnectingDescription": "Conecte su smartphone a un dispositivo de radio MeshCore vía Bluetooth para comenzar a comunicarse sin conexión.", + "wizardConnectingFeature1": "Buscar dispositivos MeshCore cercanos", + "wizardConnectingFeature2": "Emparejar con su radio vía Bluetooth", + "wizardConnectingFeature3": "Funciona completamente sin conexión - no se requiere internet", + "wizardSimpleModeTitle": "Modo Simple", + "wizardSimpleModeDescription": "¿Nuevo en redes en malla? Habilite el modo simple para una interfaz optimizada con solo funciones esenciales.", + "wizardSimpleModeFeature1": "Interfaz amigable para principiantes con funciones principales", + "wizardSimpleModeFeature2": "Cambie al modo avanzado en cualquier momento en Configuración", + "wizardChannelTitle": "Canales", + "wizardChannelDescription": "Transmita mensajes a todos en un canal, perfecto para anuncios y coordinación de todo el equipo.", + "wizardChannelFeature1": "Canal público para comunicación general del equipo", + "wizardChannelFeature2": "Cree canales personalizados para grupos específicos", + "wizardChannelFeature3": "Los mensajes se retransmiten automáticamente por la malla", + "wizardContactsTitle": "Contactos", + "wizardContactsDescription": "Los miembros de su equipo aparecen automáticamente cuando se unen a la red en malla. Envíeles mensajes directos o vea su ubicación.", + "wizardContactsFeature1": "Contactos descubiertos automáticamente", + "wizardContactsFeature2": "Enviar mensajes directos privados", + "wizardContactsFeature3": "Ver nivel de batería y hora de última vista", + "wizardMapTitle": "Mapa & Ubicación", + "wizardMapDescription": "Rastree a su equipo en tiempo real y marque ubicaciones importantes para operaciones de búsqueda y rescate.", + "wizardMapFeature1": "Marcadores SAR para personas encontradas, incendios y áreas de preparación", + "wizardMapFeature2": "Rastreo GPS en tiempo real de miembros del equipo", + "wizardMapFeature3": "Descargar mapas sin conexión para áreas remotas", + "wizardMapFeature4": "Dibujar formas y compartir información táctica", + "viewWelcomeTutorial": "Ver tutorial de bienvenida" } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 27744db..4fc448a 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -2731,5 +2731,38 @@ "channelCreatedSuccessfully": "Canal créé avec succès", "channelCreationFailed": "Échec de la création du canal : {error}", "allChannelSlotsInUse": "Tous les emplacements de canaux sont utilisés (maximum 39 canaux personnalisés)", - "createChannel": "Créer un Canal" + "createChannel": "Créer un Canal", + + "wizardBack": "Retour", + "wizardSkip": "Passer", + "wizardNext": "Suivant", + "wizardGetStarted": "Commencer", + "wizardWelcomeTitle": "Bienvenue dans MeshCore SAR", + "wizardWelcomeDescription": "Un outil de communication hors ligne puissant pour les opérations de recherche et de sauvetage. Connectez-vous avec votre équipe en utilisant la technologie radio maillée lorsque les réseaux traditionnels ne sont pas disponibles.", + "wizardConnectingTitle": "Connexion à votre Radio", + "wizardConnectingDescription": "Connectez votre smartphone à un appareil radio MeshCore via Bluetooth pour commencer à communiquer hors ligne.", + "wizardConnectingFeature1": "Rechercher les appareils MeshCore à proximité", + "wizardConnectingFeature2": "Coupler avec votre radio via Bluetooth", + "wizardConnectingFeature3": "Fonctionne entièrement hors ligne - aucun internet requis", + "wizardSimpleModeTitle": "Mode Simple", + "wizardSimpleModeDescription": "Nouveau dans les réseaux maillés ? Activez le mode simple pour une interface simplifiée avec seulement les fonctions essentielles.", + "wizardSimpleModeFeature1": "Interface conviviale pour débutants avec les fonctions principales", + "wizardSimpleModeFeature2": "Passer en mode avancé à tout moment dans les paramètres", + "wizardChannelTitle": "Canaux", + "wizardChannelDescription": "Diffusez des messages à tous sur un canal, parfait pour les annonces et la coordination de toute l'équipe.", + "wizardChannelFeature1": "Canal public pour la communication générale de l'équipe", + "wizardChannelFeature2": "Créer des canaux personnalisés pour des groupes spécifiques", + "wizardChannelFeature3": "Les messages sont automatiquement relayés par le maillage", + "wizardContactsTitle": "Contacts", + "wizardContactsDescription": "Les membres de votre équipe apparaissent automatiquement lorsqu'ils rejoignent le réseau maillé. Envoyez-leur des messages directs ou consultez leur emplacement.", + "wizardContactsFeature1": "Contacts découverts automatiquement", + "wizardContactsFeature2": "Envoyer des messages directs privés", + "wizardContactsFeature3": "Voir le niveau de batterie et l'heure de dernière vue", + "wizardMapTitle": "Carte & Localisation", + "wizardMapDescription": "Suivez votre équipe en temps réel et marquez les emplacements importants pour les opérations de recherche et de sauvetage.", + "wizardMapFeature1": "Marqueurs SAR pour les personnes retrouvées, les incendies et les zones de rassemblement", + "wizardMapFeature2": "Suivi GPS en temps réel des membres de l'équipe", + "wizardMapFeature3": "Télécharger des cartes hors ligne pour les zones éloignées", + "wizardMapFeature4": "Dessiner des formes et partager des informations tactiques", + "viewWelcomeTutorial": "Voir le tutoriel de bienvenue" } diff --git a/lib/l10n/app_hr.arb b/lib/l10n/app_hr.arb index 6a44a94..863a4bc 100644 --- a/lib/l10n/app_hr.arb +++ b/lib/l10n/app_hr.arb @@ -1029,5 +1029,38 @@ "channelCreatedSuccessfully": "Kanal uspješno kreiran", "channelCreationFailed": "Neuspješno kreiranje kanala: {error}", "allChannelSlotsInUse": "Svi slotovi kanala su zauzeti (maksimalno 39 prilagođenih kanala)", - "createChannel": "Kreiraj kanal" + "createChannel": "Kreiraj kanal", + + "wizardBack": "Natrag", + "wizardSkip": "Preskoči", + "wizardNext": "Dalje", + "wizardGetStarted": "Započni", + "wizardWelcomeTitle": "Dobrodošli u MeshCore SAR", + "wizardWelcomeDescription": "Moćan alat za komunikaciju bez mreže za spasilačke operacije. Povežite se sa svojim timom koristeći mesh radio tehnologiju kada tradicionalne mreže nisu dostupne.", + "wizardConnectingTitle": "Povezivanje s Radiom", + "wizardConnectingDescription": "Povežite svoj pametni telefon s MeshCore radio uređajem putem Bluetootha kako biste započeli komunikaciju bez mreže.", + "wizardConnectingFeature1": "Skeniraj obližnje MeshCore uređaje", + "wizardConnectingFeature2": "Uparite se s vašim radiom putem Bluetootha", + "wizardConnectingFeature3": "Radi potpuno offline - internet nije potreban", + "wizardSimpleModeTitle": "Jednostavan Način", + "wizardSimpleModeDescription": "Novi u mesh umrežavanju? Omogućite jednostavan način za pojednostavljeno sučelje samo s bitnim funkcijama.", + "wizardSimpleModeFeature1": "Sučelje prijateljsko početnicima s osnovnim funkcijama", + "wizardSimpleModeFeature2": "Prebacite na Napredni način bilo kada u Postavkama", + "wizardChannelTitle": "Kanali", + "wizardChannelDescription": "Emitirajte poruke svima na kanalu, savršeno za objave i koordinaciju cijele ekipe.", + "wizardChannelFeature1": "Javni kanal za opću komunikaciju ekipe", + "wizardChannelFeature2": "Stvorite prilagođene kanale za specifične grupe", + "wizardChannelFeature3": "Poruke se automatski prosljeđuju putem mreže", + "wizardContactsTitle": "Kontakti", + "wizardContactsDescription": "Vaši članovi tima se automatski pojavljuju kada se pridruže mesh mreži. Pošaljite im direktne poruke ili pogledajte njihovu lokaciju.", + "wizardContactsFeature1": "Kontakti se automatski otkrivaju", + "wizardContactsFeature2": "Šaljite privatne direktne poruke", + "wizardContactsFeature3": "Pogledajte razinu baterije i vrijeme zadnjeg viđenja", + "wizardMapTitle": "Karta & Lokacija", + "wizardMapDescription": "Pratite svoj tim u stvarnom vremenu i označite važne lokacije za spasilačke operacije.", + "wizardMapFeature1": "SAR oznake za pronađene osobe, požare i područja postavljanja", + "wizardMapFeature2": "GPS praćenje članova tima u stvarnom vremenu", + "wizardMapFeature3": "Preuzmite offline karte za udaljena područja", + "wizardMapFeature4": "Crtajte oblike i dijelite taktičke informacije", + "viewWelcomeTutorial": "Pogledaj uputu dobrodošlice" } diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index d005f5e..45190f7 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -2731,5 +2731,38 @@ "channelCreatedSuccessfully": "Canale creato con successo", "channelCreationFailed": "Creazione del canale fallita: {error}", "allChannelSlotsInUse": "Tutti gli slot dei canali sono in uso (massimo 39 canali personalizzati)", - "createChannel": "Crea Canale" + "createChannel": "Crea Canale", + + "wizardBack": "Indietro", + "wizardSkip": "Salta", + "wizardNext": "Avanti", + "wizardGetStarted": "Inizia", + "wizardWelcomeTitle": "Benvenuto in MeshCore SAR", + "wizardWelcomeDescription": "Un potente strumento di comunicazione offline per operazioni di ricerca e soccorso. Connettiti con il tuo team usando la tecnologia radio mesh quando le reti tradizionali non sono disponibili.", + "wizardConnectingTitle": "Connessione alla Radio", + "wizardConnectingDescription": "Collega il tuo smartphone a un dispositivo radio MeshCore tramite Bluetooth per iniziare a comunicare offline.", + "wizardConnectingFeature1": "Cerca dispositivi MeshCore nelle vicinanze", + "wizardConnectingFeature2": "Accoppia con la tua radio tramite Bluetooth", + "wizardConnectingFeature3": "Funziona completamente offline - non è richiesta connessione internet", + "wizardSimpleModeTitle": "Modalità Semplice", + "wizardSimpleModeDescription": "Nuovo alle reti mesh? Abilita la modalità semplice per un'interfaccia semplificata con solo le funzioni essenziali.", + "wizardSimpleModeFeature1": "Interfaccia intuitiva per principianti con funzioni principali", + "wizardSimpleModeFeature2": "Passa alla modalità avanzata in qualsiasi momento dalle Impostazioni", + "wizardChannelTitle": "Canali", + "wizardChannelDescription": "Trasmetti messaggi a tutti su un canale, perfetto per annunci e coordinamento di tutto il team.", + "wizardChannelFeature1": "Canale pubblico per comunicazione generale del team", + "wizardChannelFeature2": "Crea canali personalizzati per gruppi specifici", + "wizardChannelFeature3": "I messaggi vengono automaticamente inoltrati attraverso la rete mesh", + "wizardContactsTitle": "Contatti", + "wizardContactsDescription": "I membri del tuo team appaiono automaticamente quando si uniscono alla rete mesh. Invia loro messaggi diretti o visualizza la loro posizione.", + "wizardContactsFeature1": "Contatti scoperti automaticamente", + "wizardContactsFeature2": "Invia messaggi diretti privati", + "wizardContactsFeature3": "Visualizza livello batteria e ultima volta visto", + "wizardMapTitle": "Mappa & Posizione", + "wizardMapDescription": "Traccia il tuo team in tempo reale e segna posizioni importanti per operazioni di ricerca e soccorso.", + "wizardMapFeature1": "Marcatori SAR per persone trovate, incendi e aree di staging", + "wizardMapFeature2": "Tracciamento GPS in tempo reale dei membri del team", + "wizardMapFeature3": "Scarica mappe offline per aree remote", + "wizardMapFeature4": "Disegna forme e condividi informazioni tattiche", + "viewWelcomeTutorial": "Visualizza tutorial di benvenuto" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index a373d3e..2ca7381 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3650,6 +3650,198 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Create Channel'** String get createChannel; + + /// Wizard back button text + /// + /// In en, this message translates to: + /// **'Back'** + String get wizardBack; + + /// Wizard skip button text + /// + /// In en, this message translates to: + /// **'Skip'** + String get wizardSkip; + + /// Wizard next button text + /// + /// In en, this message translates to: + /// **'Next'** + String get wizardNext; + + /// Wizard final button text to complete onboarding + /// + /// In en, this message translates to: + /// **'Get Started'** + String get wizardGetStarted; + + /// Welcome wizard first page title + /// + /// In en, this message translates to: + /// **'Welcome to MeshCore SAR'** + String get wizardWelcomeTitle; + + /// Welcome wizard first page description + /// + /// In en, this message translates to: + /// **'A powerful off-grid communication tool for search and rescue operations. Connect with your team using mesh radio technology when traditional networks are unavailable.'** + String get wizardWelcomeDescription; + + /// Wizard connecting page title + /// + /// In en, this message translates to: + /// **'Connecting to Your Radio'** + String get wizardConnectingTitle; + + /// Wizard connecting page description + /// + /// In en, this message translates to: + /// **'Connect your smartphone to a MeshCore radio device via Bluetooth to start communicating off-grid.'** + String get wizardConnectingDescription; + + /// Wizard connecting feature 1 + /// + /// In en, this message translates to: + /// **'Scan for nearby MeshCore devices'** + String get wizardConnectingFeature1; + + /// Wizard connecting feature 2 + /// + /// In en, this message translates to: + /// **'Pair with your radio via Bluetooth'** + String get wizardConnectingFeature2; + + /// Wizard connecting feature 3 + /// + /// In en, this message translates to: + /// **'Works completely offline - no internet required'** + String get wizardConnectingFeature3; + + /// Wizard simple mode page title + /// + /// In en, this message translates to: + /// **'Simple Mode'** + String get wizardSimpleModeTitle; + + /// Wizard simple mode page description + /// + /// In en, this message translates to: + /// **'New to mesh networking? Enable Simple Mode for a streamlined interface with essential features only.'** + String get wizardSimpleModeDescription; + + /// Wizard simple mode feature 1 + /// + /// In en, this message translates to: + /// **'Beginner-friendly interface with core functions'** + String get wizardSimpleModeFeature1; + + /// Wizard simple mode feature 2 + /// + /// In en, this message translates to: + /// **'Switch to Advanced Mode anytime in Settings'** + String get wizardSimpleModeFeature2; + + /// Wizard channel page title + /// + /// In en, this message translates to: + /// **'Channels'** + String get wizardChannelTitle; + + /// Wizard channel page description + /// + /// In en, this message translates to: + /// **'Broadcast messages to everyone on a channel, perfect for team-wide announcements and coordination.'** + String get wizardChannelDescription; + + /// Wizard channel feature 1 + /// + /// In en, this message translates to: + /// **'Public Channel for general team communication'** + String get wizardChannelFeature1; + + /// Wizard channel feature 2 + /// + /// In en, this message translates to: + /// **'Create custom channels for specific groups'** + String get wizardChannelFeature2; + + /// Wizard channel feature 3 + /// + /// In en, this message translates to: + /// **'Messages are automatically relayed by the mesh'** + String get wizardChannelFeature3; + + /// Wizard contacts page title + /// + /// In en, this message translates to: + /// **'Contacts'** + String get wizardContactsTitle; + + /// Wizard contacts page description + /// + /// In en, this message translates to: + /// **'Your team members appear automatically as they join the mesh network. Send them direct messages or view their location.'** + String get wizardContactsDescription; + + /// Wizard contacts feature 1 + /// + /// In en, this message translates to: + /// **'Contacts discovered automatically'** + String get wizardContactsFeature1; + + /// Wizard contacts feature 2 + /// + /// In en, this message translates to: + /// **'Send private direct messages'** + String get wizardContactsFeature2; + + /// Wizard contacts feature 3 + /// + /// In en, this message translates to: + /// **'View battery level and last seen time'** + String get wizardContactsFeature3; + + /// Wizard map page title + /// + /// In en, this message translates to: + /// **'Map & Location'** + String get wizardMapTitle; + + /// Wizard map page description + /// + /// In en, this message translates to: + /// **'Track your team in real-time and mark important locations for search and rescue operations.'** + String get wizardMapDescription; + + /// Wizard map feature 1 + /// + /// In en, this message translates to: + /// **'SAR markers for found persons, fires, and staging areas'** + String get wizardMapFeature1; + + /// Wizard map feature 2 + /// + /// In en, this message translates to: + /// **'Real-time GPS tracking of team members'** + String get wizardMapFeature2; + + /// Wizard map feature 3 + /// + /// In en, this message translates to: + /// **'Download offline maps for remote areas'** + String get wizardMapFeature3; + + /// Wizard map feature 4 + /// + /// In en, this message translates to: + /// **'Draw shapes and share tactical information'** + String get wizardMapFeature4; + + /// Settings option to re-show welcome wizard + /// + /// In en, this message translates to: + /// **'View Welcome Tutorial'** + String get viewWelcomeTutorial; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 55f26aa..1600466 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2042,4 +2042,119 @@ class AppLocalizationsDe extends AppLocalizations { @override String get createChannel => 'Kanal erstellen'; + + @override + String get wizardBack => 'Zurück'; + + @override + String get wizardSkip => 'Überspringen'; + + @override + String get wizardNext => 'Weiter'; + + @override + String get wizardGetStarted => 'Loslegen'; + + @override + String get wizardWelcomeTitle => 'Willkommen bei MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'Ein leistungsstarkes Offline-Kommunikationstool für Such- und Rettungseinsätze. Verbinden Sie sich mit Ihrem Team über Mesh-Funktechnologie, wenn herkömmliche Netzwerke nicht verfügbar sind.'; + + @override + String get wizardConnectingTitle => 'Verbindung zum Radio'; + + @override + String get wizardConnectingDescription => + 'Verbinden Sie Ihr Smartphone über Bluetooth mit einem MeshCore-Funkgerät, um offline zu kommunizieren.'; + + @override + String get wizardConnectingFeature1 => + 'Nach MeshCore-Geräten in der Nähe suchen'; + + @override + String get wizardConnectingFeature2 => + 'Mit Ihrem Funkgerät über Bluetooth koppeln'; + + @override + String get wizardConnectingFeature3 => + 'Funktioniert vollständig offline - kein Internet erforderlich'; + + @override + String get wizardSimpleModeTitle => 'Einfacher Modus'; + + @override + String get wizardSimpleModeDescription => + 'Neu im Mesh-Netzwerk? Aktivieren Sie den einfachen Modus für eine optimierte Benutzeroberfläche mit nur wesentlichen Funktionen.'; + + @override + String get wizardSimpleModeFeature1 => + 'Anfängerfreundliche Benutzeroberfläche mit Kernfunktionen'; + + @override + String get wizardSimpleModeFeature2 => + 'Jederzeit in den erweiterten Modus in den Einstellungen wechseln'; + + @override + String get wizardChannelTitle => 'Kanäle'; + + @override + String get wizardChannelDescription => + 'Senden Sie Nachrichten an alle auf einem Kanal, perfekt für teamweite Ankündigungen und Koordination.'; + + @override + String get wizardChannelFeature1 => + 'Öffentlicher Kanal für allgemeine Teamkommunikation'; + + @override + String get wizardChannelFeature2 => + 'Erstellen Sie benutzerdefinierte Kanäle für bestimmte Gruppen'; + + @override + String get wizardChannelFeature3 => + 'Nachrichten werden automatisch über das Mesh weitergeleitet'; + + @override + String get wizardContactsTitle => 'Kontakte'; + + @override + String get wizardContactsDescription => + 'Ihre Teammitglieder erscheinen automatisch, wenn sie dem Mesh-Netzwerk beitreten. Senden Sie ihnen direkte Nachrichten oder sehen Sie ihren Standort.'; + + @override + String get wizardContactsFeature1 => 'Kontakte werden automatisch erkannt'; + + @override + String get wizardContactsFeature2 => 'Private Direktnachrichten senden'; + + @override + String get wizardContactsFeature3 => + 'Batteriestand und letzte Aktivität anzeigen'; + + @override + String get wizardMapTitle => 'Karte & Standort'; + + @override + String get wizardMapDescription => + 'Verfolgen Sie Ihr Team in Echtzeit und markieren Sie wichtige Standorte für Such- und Rettungseinsätze.'; + + @override + String get wizardMapFeature1 => + 'SAR-Markierungen für gefundene Personen, Feuer und Sammelstellen'; + + @override + String get wizardMapFeature2 => + 'GPS-Verfolgung von Teammitgliedern in Echtzeit'; + + @override + String get wizardMapFeature3 => + 'Offline-Karten für entlegene Gebiete herunterladen'; + + @override + String get wizardMapFeature4 => + 'Formen zeichnen und taktische Informationen teilen'; + + @override + String get viewWelcomeTutorial => 'Willkommens-Tutorial ansehen'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 2129c27..a669f26 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -2021,4 +2021,113 @@ class AppLocalizationsEn extends AppLocalizations { @override String get createChannel => 'Create Channel'; + + @override + String get wizardBack => 'Back'; + + @override + String get wizardSkip => 'Skip'; + + @override + String get wizardNext => 'Next'; + + @override + String get wizardGetStarted => 'Get Started'; + + @override + String get wizardWelcomeTitle => 'Welcome to MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'A powerful off-grid communication tool for search and rescue operations. Connect with your team using mesh radio technology when traditional networks are unavailable.'; + + @override + String get wizardConnectingTitle => 'Connecting to Your Radio'; + + @override + String get wizardConnectingDescription => + 'Connect your smartphone to a MeshCore radio device via Bluetooth to start communicating off-grid.'; + + @override + String get wizardConnectingFeature1 => 'Scan for nearby MeshCore devices'; + + @override + String get wizardConnectingFeature2 => 'Pair with your radio via Bluetooth'; + + @override + String get wizardConnectingFeature3 => + 'Works completely offline - no internet required'; + + @override + String get wizardSimpleModeTitle => 'Simple Mode'; + + @override + String get wizardSimpleModeDescription => + 'New to mesh networking? Enable Simple Mode for a streamlined interface with essential features only.'; + + @override + String get wizardSimpleModeFeature1 => + 'Beginner-friendly interface with core functions'; + + @override + String get wizardSimpleModeFeature2 => + 'Switch to Advanced Mode anytime in Settings'; + + @override + String get wizardChannelTitle => 'Channels'; + + @override + String get wizardChannelDescription => + 'Broadcast messages to everyone on a channel, perfect for team-wide announcements and coordination.'; + + @override + String get wizardChannelFeature1 => + 'Public Channel for general team communication'; + + @override + String get wizardChannelFeature2 => + 'Create custom channels for specific groups'; + + @override + String get wizardChannelFeature3 => + 'Messages are automatically relayed by the mesh'; + + @override + String get wizardContactsTitle => 'Contacts'; + + @override + String get wizardContactsDescription => + 'Your team members appear automatically as they join the mesh network. Send them direct messages or view their location.'; + + @override + String get wizardContactsFeature1 => 'Contacts discovered automatically'; + + @override + String get wizardContactsFeature2 => 'Send private direct messages'; + + @override + String get wizardContactsFeature3 => 'View battery level and last seen time'; + + @override + String get wizardMapTitle => 'Map & Location'; + + @override + String get wizardMapDescription => + 'Track your team in real-time and mark important locations for search and rescue operations.'; + + @override + String get wizardMapFeature1 => + 'SAR markers for found persons, fires, and staging areas'; + + @override + String get wizardMapFeature2 => 'Real-time GPS tracking of team members'; + + @override + String get wizardMapFeature3 => 'Download offline maps for remote areas'; + + @override + String get wizardMapFeature4 => 'Draw shapes and share tactical information'; + + @override + String get viewWelcomeTutorial => 'View Welcome Tutorial'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 1617ffe..945fae9 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2047,4 +2047,118 @@ class AppLocalizationsEs extends AppLocalizations { @override String get createChannel => 'Crear Canal'; + + @override + String get wizardBack => 'Atrás'; + + @override + String get wizardSkip => 'Omitir'; + + @override + String get wizardNext => 'Siguiente'; + + @override + String get wizardGetStarted => 'Comenzar'; + + @override + String get wizardWelcomeTitle => 'Bienvenido a MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'Una poderosa herramienta de comunicación sin conexión para operaciones de búsqueda y rescate. Conéctese con su equipo usando tecnología de radio en malla cuando las redes tradicionales no estén disponibles.'; + + @override + String get wizardConnectingTitle => 'Conectando a su Radio'; + + @override + String get wizardConnectingDescription => + 'Conecte su smartphone a un dispositivo de radio MeshCore vía Bluetooth para comenzar a comunicarse sin conexión.'; + + @override + String get wizardConnectingFeature1 => + 'Buscar dispositivos MeshCore cercanos'; + + @override + String get wizardConnectingFeature2 => 'Emparejar con su radio vía Bluetooth'; + + @override + String get wizardConnectingFeature3 => + 'Funciona completamente sin conexión - no se requiere internet'; + + @override + String get wizardSimpleModeTitle => 'Modo Simple'; + + @override + String get wizardSimpleModeDescription => + '¿Nuevo en redes en malla? Habilite el modo simple para una interfaz optimizada con solo funciones esenciales.'; + + @override + String get wizardSimpleModeFeature1 => + 'Interfaz amigable para principiantes con funciones principales'; + + @override + String get wizardSimpleModeFeature2 => + 'Cambie al modo avanzado en cualquier momento en Configuración'; + + @override + String get wizardChannelTitle => 'Canales'; + + @override + String get wizardChannelDescription => + 'Transmita mensajes a todos en un canal, perfecto para anuncios y coordinación de todo el equipo.'; + + @override + String get wizardChannelFeature1 => + 'Canal público para comunicación general del equipo'; + + @override + String get wizardChannelFeature2 => + 'Cree canales personalizados para grupos específicos'; + + @override + String get wizardChannelFeature3 => + 'Los mensajes se retransmiten automáticamente por la malla'; + + @override + String get wizardContactsTitle => 'Contactos'; + + @override + String get wizardContactsDescription => + 'Los miembros de su equipo aparecen automáticamente cuando se unen a la red en malla. Envíeles mensajes directos o vea su ubicación.'; + + @override + String get wizardContactsFeature1 => 'Contactos descubiertos automáticamente'; + + @override + String get wizardContactsFeature2 => 'Enviar mensajes directos privados'; + + @override + String get wizardContactsFeature3 => + 'Ver nivel de batería y hora de última vista'; + + @override + String get wizardMapTitle => 'Mapa & Ubicación'; + + @override + String get wizardMapDescription => + 'Rastree a su equipo en tiempo real y marque ubicaciones importantes para operaciones de búsqueda y rescate.'; + + @override + String get wizardMapFeature1 => + 'Marcadores SAR para personas encontradas, incendios y áreas de preparación'; + + @override + String get wizardMapFeature2 => + 'Rastreo GPS en tiempo real de miembros del equipo'; + + @override + String get wizardMapFeature3 => + 'Descargar mapas sin conexión para áreas remotas'; + + @override + String get wizardMapFeature4 => + 'Dibujar formas y compartir información táctica'; + + @override + String get viewWelcomeTutorial => 'Ver tutorial de bienvenida'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 5b3aefb..94f1825 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2052,4 +2052,119 @@ class AppLocalizationsFr extends AppLocalizations { @override String get createChannel => 'Créer un Canal'; + + @override + String get wizardBack => 'Retour'; + + @override + String get wizardSkip => 'Passer'; + + @override + String get wizardNext => 'Suivant'; + + @override + String get wizardGetStarted => 'Commencer'; + + @override + String get wizardWelcomeTitle => 'Bienvenue dans MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'Un outil de communication hors ligne puissant pour les opérations de recherche et de sauvetage. Connectez-vous avec votre équipe en utilisant la technologie radio maillée lorsque les réseaux traditionnels ne sont pas disponibles.'; + + @override + String get wizardConnectingTitle => 'Connexion à votre Radio'; + + @override + String get wizardConnectingDescription => + 'Connectez votre smartphone à un appareil radio MeshCore via Bluetooth pour commencer à communiquer hors ligne.'; + + @override + String get wizardConnectingFeature1 => + 'Rechercher les appareils MeshCore à proximité'; + + @override + String get wizardConnectingFeature2 => + 'Coupler avec votre radio via Bluetooth'; + + @override + String get wizardConnectingFeature3 => + 'Fonctionne entièrement hors ligne - aucun internet requis'; + + @override + String get wizardSimpleModeTitle => 'Mode Simple'; + + @override + String get wizardSimpleModeDescription => + 'Nouveau dans les réseaux maillés ? Activez le mode simple pour une interface simplifiée avec seulement les fonctions essentielles.'; + + @override + String get wizardSimpleModeFeature1 => + 'Interface conviviale pour débutants avec les fonctions principales'; + + @override + String get wizardSimpleModeFeature2 => + 'Passer en mode avancé à tout moment dans les paramètres'; + + @override + String get wizardChannelTitle => 'Canaux'; + + @override + String get wizardChannelDescription => + 'Diffusez des messages à tous sur un canal, parfait pour les annonces et la coordination de toute l\'équipe.'; + + @override + String get wizardChannelFeature1 => + 'Canal public pour la communication générale de l\'équipe'; + + @override + String get wizardChannelFeature2 => + 'Créer des canaux personnalisés pour des groupes spécifiques'; + + @override + String get wizardChannelFeature3 => + 'Les messages sont automatiquement relayés par le maillage'; + + @override + String get wizardContactsTitle => 'Contacts'; + + @override + String get wizardContactsDescription => + 'Les membres de votre équipe apparaissent automatiquement lorsqu\'ils rejoignent le réseau maillé. Envoyez-leur des messages directs ou consultez leur emplacement.'; + + @override + String get wizardContactsFeature1 => 'Contacts découverts automatiquement'; + + @override + String get wizardContactsFeature2 => 'Envoyer des messages directs privés'; + + @override + String get wizardContactsFeature3 => + 'Voir le niveau de batterie et l\'heure de dernière vue'; + + @override + String get wizardMapTitle => 'Carte & Localisation'; + + @override + String get wizardMapDescription => + 'Suivez votre équipe en temps réel et marquez les emplacements importants pour les opérations de recherche et de sauvetage.'; + + @override + String get wizardMapFeature1 => + 'Marqueurs SAR pour les personnes retrouvées, les incendies et les zones de rassemblement'; + + @override + String get wizardMapFeature2 => + 'Suivi GPS en temps réel des membres de l\'équipe'; + + @override + String get wizardMapFeature3 => + 'Télécharger des cartes hors ligne pour les zones éloignées'; + + @override + String get wizardMapFeature4 => + 'Dessiner des formes et partager des informations tactiques'; + + @override + String get viewWelcomeTutorial => 'Voir le tutoriel de bienvenue'; } diff --git a/lib/l10n/app_localizations_hr.dart b/lib/l10n/app_localizations_hr.dart index cad6995..baa63c1 100644 --- a/lib/l10n/app_localizations_hr.dart +++ b/lib/l10n/app_localizations_hr.dart @@ -2030,4 +2030,117 @@ class AppLocalizationsHr extends AppLocalizations { @override String get createChannel => 'Kreiraj kanal'; + + @override + String get wizardBack => 'Natrag'; + + @override + String get wizardSkip => 'Preskoči'; + + @override + String get wizardNext => 'Dalje'; + + @override + String get wizardGetStarted => 'Započni'; + + @override + String get wizardWelcomeTitle => 'Dobrodošli u MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'Moćan alat za komunikaciju bez mreže za spasilačke operacije. Povežite se sa svojim timom koristeći mesh radio tehnologiju kada tradicionalne mreže nisu dostupne.'; + + @override + String get wizardConnectingTitle => 'Povezivanje s Radiom'; + + @override + String get wizardConnectingDescription => + 'Povežite svoj pametni telefon s MeshCore radio uređajem putem Bluetootha kako biste započeli komunikaciju bez mreže.'; + + @override + String get wizardConnectingFeature1 => 'Skeniraj obližnje MeshCore uređaje'; + + @override + String get wizardConnectingFeature2 => + 'Uparite se s vašim radiom putem Bluetootha'; + + @override + String get wizardConnectingFeature3 => + 'Radi potpuno offline - internet nije potreban'; + + @override + String get wizardSimpleModeTitle => 'Jednostavan Način'; + + @override + String get wizardSimpleModeDescription => + 'Novi u mesh umrežavanju? Omogućite jednostavan način za pojednostavljeno sučelje samo s bitnim funkcijama.'; + + @override + String get wizardSimpleModeFeature1 => + 'Sučelje prijateljsko početnicima s osnovnim funkcijama'; + + @override + String get wizardSimpleModeFeature2 => + 'Prebacite na Napredni način bilo kada u Postavkama'; + + @override + String get wizardChannelTitle => 'Kanali'; + + @override + String get wizardChannelDescription => + 'Emitirajte poruke svima na kanalu, savršeno za objave i koordinaciju cijele ekipe.'; + + @override + String get wizardChannelFeature1 => 'Javni kanal za opću komunikaciju ekipe'; + + @override + String get wizardChannelFeature2 => + 'Stvorite prilagođene kanale za specifične grupe'; + + @override + String get wizardChannelFeature3 => + 'Poruke se automatski prosljeđuju putem mreže'; + + @override + String get wizardContactsTitle => 'Kontakti'; + + @override + String get wizardContactsDescription => + 'Vaši članovi tima se automatski pojavljuju kada se pridruže mesh mreži. Pošaljite im direktne poruke ili pogledajte njihovu lokaciju.'; + + @override + String get wizardContactsFeature1 => 'Kontakti se automatski otkrivaju'; + + @override + String get wizardContactsFeature2 => 'Šaljite privatne direktne poruke'; + + @override + String get wizardContactsFeature3 => + 'Pogledajte razinu baterije i vrijeme zadnjeg viđenja'; + + @override + String get wizardMapTitle => 'Karta & Lokacija'; + + @override + String get wizardMapDescription => + 'Pratite svoj tim u stvarnom vremenu i označite važne lokacije za spasilačke operacije.'; + + @override + String get wizardMapFeature1 => + 'SAR oznake za pronađene osobe, požare i područja postavljanja'; + + @override + String get wizardMapFeature2 => + 'GPS praćenje članova tima u stvarnom vremenu'; + + @override + String get wizardMapFeature3 => + 'Preuzmite offline karte za udaljena područja'; + + @override + String get wizardMapFeature4 => + 'Crtajte oblike i dijelite taktičke informacije'; + + @override + String get viewWelcomeTutorial => 'Pogledaj uputu dobrodošlice'; } diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 9fadc29..25d5134 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -2042,4 +2042,118 @@ class AppLocalizationsIt extends AppLocalizations { @override String get createChannel => 'Crea Canale'; + + @override + String get wizardBack => 'Indietro'; + + @override + String get wizardSkip => 'Salta'; + + @override + String get wizardNext => 'Avanti'; + + @override + String get wizardGetStarted => 'Inizia'; + + @override + String get wizardWelcomeTitle => 'Benvenuto in MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'Un potente strumento di comunicazione offline per operazioni di ricerca e soccorso. Connettiti con il tuo team usando la tecnologia radio mesh quando le reti tradizionali non sono disponibili.'; + + @override + String get wizardConnectingTitle => 'Connessione alla Radio'; + + @override + String get wizardConnectingDescription => + 'Collega il tuo smartphone a un dispositivo radio MeshCore tramite Bluetooth per iniziare a comunicare offline.'; + + @override + String get wizardConnectingFeature1 => + 'Cerca dispositivi MeshCore nelle vicinanze'; + + @override + String get wizardConnectingFeature2 => + 'Accoppia con la tua radio tramite Bluetooth'; + + @override + String get wizardConnectingFeature3 => + 'Funziona completamente offline - non è richiesta connessione internet'; + + @override + String get wizardSimpleModeTitle => 'Modalità Semplice'; + + @override + String get wizardSimpleModeDescription => + 'Nuovo alle reti mesh? Abilita la modalità semplice per un\'interfaccia semplificata con solo le funzioni essenziali.'; + + @override + String get wizardSimpleModeFeature1 => + 'Interfaccia intuitiva per principianti con funzioni principali'; + + @override + String get wizardSimpleModeFeature2 => + 'Passa alla modalità avanzata in qualsiasi momento dalle Impostazioni'; + + @override + String get wizardChannelTitle => 'Canali'; + + @override + String get wizardChannelDescription => + 'Trasmetti messaggi a tutti su un canale, perfetto per annunci e coordinamento di tutto il team.'; + + @override + String get wizardChannelFeature1 => + 'Canale pubblico per comunicazione generale del team'; + + @override + String get wizardChannelFeature2 => + 'Crea canali personalizzati per gruppi specifici'; + + @override + String get wizardChannelFeature3 => + 'I messaggi vengono automaticamente inoltrati attraverso la rete mesh'; + + @override + String get wizardContactsTitle => 'Contatti'; + + @override + String get wizardContactsDescription => + 'I membri del tuo team appaiono automaticamente quando si uniscono alla rete mesh. Invia loro messaggi diretti o visualizza la loro posizione.'; + + @override + String get wizardContactsFeature1 => 'Contatti scoperti automaticamente'; + + @override + String get wizardContactsFeature2 => 'Invia messaggi diretti privati'; + + @override + String get wizardContactsFeature3 => + 'Visualizza livello batteria e ultima volta visto'; + + @override + String get wizardMapTitle => 'Mappa & Posizione'; + + @override + String get wizardMapDescription => + 'Traccia il tuo team in tempo reale e segna posizioni importanti per operazioni di ricerca e soccorso.'; + + @override + String get wizardMapFeature1 => + 'Marcatori SAR per persone trovate, incendi e aree di staging'; + + @override + String get wizardMapFeature2 => + 'Tracciamento GPS in tempo reale dei membri del team'; + + @override + String get wizardMapFeature3 => 'Scarica mappe offline per aree remote'; + + @override + String get wizardMapFeature4 => + 'Disegna forme e condividi informazioni tattiche'; + + @override + String get viewWelcomeTutorial => 'Visualizza tutorial di benvenuto'; } diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 36e412b..29b98e4 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -2029,4 +2029,118 @@ class AppLocalizationsSl extends AppLocalizations { @override String get createChannel => 'Ustvari kanal'; + + @override + String get wizardBack => 'Nazaj'; + + @override + String get wizardSkip => 'Preskoči'; + + @override + String get wizardNext => 'Naprej'; + + @override + String get wizardGetStarted => 'Začni'; + + @override + String get wizardWelcomeTitle => 'Dobrodošli v MeshCore SAR'; + + @override + String get wizardWelcomeDescription => + 'Močno orodje za komunikacijo brez omrežja za reševalne operacije. Povežite se s svojo ekipo z uporabo mrežne radijske tehnologije, ko tradicionalna omrežja niso na voljo.'; + + @override + String get wizardConnectingTitle => 'Povezava z Radijem'; + + @override + String get wizardConnectingDescription => + 'Povežite svoj pametni telefon z napravo MeshCore radio preko Bluetootha za začetek komunikacije brez omrežja.'; + + @override + String get wizardConnectingFeature1 => 'Iskanje bližnjih naprav MeshCore'; + + @override + String get wizardConnectingFeature2 => + 'Združite s svojim radijem preko Bluetootha'; + + @override + String get wizardConnectingFeature3 => + 'Deluje popolnoma brez povezave - internet ni potreben'; + + @override + String get wizardSimpleModeTitle => 'Preprost Način'; + + @override + String get wizardSimpleModeDescription => + 'Novi v mrežnem omrežju? Omogočite preprost način za poenostavljen vmesnik z le bistvenimi funkcijami.'; + + @override + String get wizardSimpleModeFeature1 => + 'Vmesnik prijazen za začetnike z osnovnimi funkcijami'; + + @override + String get wizardSimpleModeFeature2 => + 'Kadarkoli preklopite na napredni način v nastavitvah'; + + @override + String get wizardChannelTitle => 'Kanali'; + + @override + String get wizardChannelDescription => + 'Oddajajte sporočila vsem na kanalu, popolno za objave in usklajevanje cele ekipe.'; + + @override + String get wizardChannelFeature1 => + 'Javni kanal za splošno komunikacijo ekipe'; + + @override + String get wizardChannelFeature2 => + 'Ustvarite kanale po meri za določene skupine'; + + @override + String get wizardChannelFeature3 => + 'Sporočila so samodejno posredovana preko mreže'; + + @override + String get wizardContactsTitle => 'Stiki'; + + @override + String get wizardContactsDescription => + 'Vaši člani ekipe se samodejno prikažejo, ko se pridružijo mrežnemu omrežju. Pošljite jim neposredna sporočila ali si oglejte njihovo lokacijo.'; + + @override + String get wizardContactsFeature1 => 'Stiki se odkrijejo samodejno'; + + @override + String get wizardContactsFeature2 => + 'Pošiljajte zasebna neposredna sporočila'; + + @override + String get wizardContactsFeature3 => + 'Oglejte si raven baterije in čas zadnjega ogleda'; + + @override + String get wizardMapTitle => 'Zemljevid & Lokacija'; + + @override + String get wizardMapDescription => + 'Sledite svoji ekipi v realnem času in označite pomembne lokacije za reševalne operacije.'; + + @override + String get wizardMapFeature1 => + 'SAR označevalci za najdene osebe, požare in območja postavitve'; + + @override + String get wizardMapFeature2 => 'Sledenje GPS članov ekipe v realnem času'; + + @override + String get wizardMapFeature3 => + 'Prenesite zemljevide brez povezave za oddaljene kraje'; + + @override + String get wizardMapFeature4 => + 'Narišite oblike in delite taktične informacije'; + + @override + String get viewWelcomeTutorial => 'Ogled vadnice dobrodošlice'; } diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 54a5647..95cec08 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -1029,5 +1029,38 @@ "channelCreatedSuccessfully": "Kanal uspešno ustvarjen", "channelCreationFailed": "Neuspešno ustvarjanje kanala: {error}", "allChannelSlotsInUse": "Vsa mesta za kanale so zasedena (maksimalno 39 prilagojenih kanalov)", - "createChannel": "Ustvari kanal" + "createChannel": "Ustvari kanal", + + "wizardBack": "Nazaj", + "wizardSkip": "Preskoči", + "wizardNext": "Naprej", + "wizardGetStarted": "Začni", + "wizardWelcomeTitle": "Dobrodošli v MeshCore SAR", + "wizardWelcomeDescription": "Močno orodje za komunikacijo brez omrežja za reševalne operacije. Povežite se s svojo ekipo z uporabo mrežne radijske tehnologije, ko tradicionalna omrežja niso na voljo.", + "wizardConnectingTitle": "Povezava z Radijem", + "wizardConnectingDescription": "Povežite svoj pametni telefon z napravo MeshCore radio preko Bluetootha za začetek komunikacije brez omrežja.", + "wizardConnectingFeature1": "Iskanje bližnjih naprav MeshCore", + "wizardConnectingFeature2": "Združite s svojim radijem preko Bluetootha", + "wizardConnectingFeature3": "Deluje popolnoma brez povezave - internet ni potreben", + "wizardSimpleModeTitle": "Preprost Način", + "wizardSimpleModeDescription": "Novi v mrežnem omrežju? Omogočite preprost način za poenostavljen vmesnik z le bistvenimi funkcijami.", + "wizardSimpleModeFeature1": "Vmesnik prijazen za začetnike z osnovnimi funkcijami", + "wizardSimpleModeFeature2": "Kadarkoli preklopite na napredni način v nastavitvah", + "wizardChannelTitle": "Kanali", + "wizardChannelDescription": "Oddajajte sporočila vsem na kanalu, popolno za objave in usklajevanje cele ekipe.", + "wizardChannelFeature1": "Javni kanal za splošno komunikacijo ekipe", + "wizardChannelFeature2": "Ustvarite kanale po meri za določene skupine", + "wizardChannelFeature3": "Sporočila so samodejno posredovana preko mreže", + "wizardContactsTitle": "Stiki", + "wizardContactsDescription": "Vaši člani ekipe se samodejno prikažejo, ko se pridružijo mrežnemu omrežju. Pošljite jim neposredna sporočila ali si oglejte njihovo lokacijo.", + "wizardContactsFeature1": "Stiki se odkrijejo samodejno", + "wizardContactsFeature2": "Pošiljajte zasebna neposredna sporočila", + "wizardContactsFeature3": "Oglejte si raven baterije in čas zadnjega ogleda", + "wizardMapTitle": "Zemljevid & Lokacija", + "wizardMapDescription": "Sledite svoji ekipi v realnem času in označite pomembne lokacije za reševalne operacije.", + "wizardMapFeature1": "SAR označevalci za najdene osebe, požare in območja postavitve", + "wizardMapFeature2": "Sledenje GPS članov ekipe v realnem času", + "wizardMapFeature3": "Prenesite zemljevide brez povezave za oddaljene kraje", + "wizardMapFeature4": "Narišite oblike in delite taktične informacije", + "viewWelcomeTutorial": "Ogled vadnice dobrodošlice" } diff --git a/lib/main.dart b/lib/main.dart index bc32089..a9d3270 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,8 +15,10 @@ import 'services/tile_cache_service.dart'; import 'services/notification_service.dart'; import 'services/locale_preferences.dart'; import 'services/update_checker_service.dart'; +import 'services/wizard_preferences.dart'; import 'widgets/update_dialog.dart'; import 'screens/home_screen.dart'; +import 'screens/welcome_wizard_screen.dart'; import 'theme/app_theme.dart'; import 'l10n/app_localizations.dart'; @@ -36,6 +38,7 @@ class _MeshCoreSarAppState extends State { Locale? _locale; bool _isInitialized = false; bool _shouldShowPermissionDialog = false; + bool _wizardCompleted = true; // Will be updated in _initializeApp() @override void initState() { @@ -46,6 +49,10 @@ class _MeshCoreSarAppState extends State { Future _initializeApp() async { await _loadThemePreference(); await _loadLocalePreference(); + + // Check if welcome wizard has been completed + final wizardCompleted = await WizardPreferences.isWizardCompleted(); + // Initialize notification service await NotificationService().initialize(); @@ -57,6 +64,7 @@ class _MeshCoreSarAppState extends State { _checkForUpdates(); setState(() { + _wizardCompleted = wizardCompleted; _isInitialized = true; }); } @@ -102,6 +110,12 @@ class _MeshCoreSarAppState extends State { }); } + void _handleWizardCompleted() { + setState(() { + _wizardCompleted = true; + }); + } + /// Check for app updates on Android only /// Shows dialog if update is available Future _checkForUpdates() async { @@ -224,13 +238,17 @@ class _MeshCoreSarAppState extends State { GlobalCupertinoLocalizations.delegate, ], supportedLocales: LocalePreferences.supportedLocales, - home: HomeScreen( - onThemeChanged: _handleThemeChanged, - onLocaleChanged: _handleLocaleChanged, - currentTheme: _themeMode, - currentLocale: _locale, - shouldShowPermissionDialog: _shouldShowPermissionDialog, - ), + home: _wizardCompleted + ? HomeScreen( + onThemeChanged: _handleThemeChanged, + onLocaleChanged: _handleLocaleChanged, + currentTheme: _themeMode, + currentLocale: _locale, + shouldShowPermissionDialog: _shouldShowPermissionDialog, + ) + : WelcomeWizardScreen( + onCompleted: _handleWizardCompleted, + ), ); }, ); diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index e5ff609..456c6c8 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -14,6 +14,7 @@ import '../utils/sample_data_generator.dart'; import '../theme/app_theme.dart'; import '../l10n/app_localizations.dart'; import 'sar_template_management_screen.dart'; +import 'welcome_wizard_screen.dart'; class SettingsScreen extends StatefulWidget { final Function(AppThemeMode) onThemeChanged; @@ -476,6 +477,25 @@ class _SettingsScreenState extends State { ); }, ), + ListTile( + leading: const Icon(Icons.school), + title: Text(AppLocalizations.of(context)!.viewWelcomeTutorial), + trailing: const Icon(Icons.chevron_right), + onTap: () async { + // Show wizard without resetting state - just as a modal + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => WelcomeWizardScreen( + onCompleted: () { + // Just pop back to settings when done + Navigator.of(context).pop(); + }, + ), + ), + ); + }, + ), const Divider(), // Permissions Section diff --git a/lib/screens/welcome_wizard_screen.dart b/lib/screens/welcome_wizard_screen.dart new file mode 100644 index 0000000..a61ea17 --- /dev/null +++ b/lib/screens/welcome_wizard_screen.dart @@ -0,0 +1,399 @@ +import 'package:flutter/material.dart'; +import '../l10n/app_localizations.dart'; +import '../services/wizard_preferences.dart'; + +/// Welcome wizard screen to introduce new users to the app +class WelcomeWizardScreen extends StatefulWidget { + final VoidCallback? onCompleted; + + const WelcomeWizardScreen({super.key, this.onCompleted}); + + @override + State createState() => _WelcomeWizardScreenState(); +} + +class _WelcomeWizardScreenState extends State { + final PageController _pageController = PageController(); + int _currentPage = 0; + static const int _totalPages = 6; + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + void _onPageChanged(int page) { + setState(() { + _currentPage = page; + }); + } + + void _nextPage() { + if (_currentPage < _totalPages - 1) { + _pageController.nextPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } else { + _completeWizard(); + } + } + + void _previousPage() { + if (_currentPage > 0) { + _pageController.previousPage( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + } + + Future _completeWizard() async { + await WizardPreferences.setWizardCompleted(true); + if (mounted) { + widget.onCompleted?.call(); + } + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + + return Scaffold( + body: SafeArea( + child: Column( + children: [ + // Top bar with skip button + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (_currentPage > 0) + TextButton.icon( + onPressed: _previousPage, + icon: const Icon(Icons.arrow_back), + label: Text(l10n.wizardBack), + ) + else + const SizedBox(width: 80), + if (_currentPage < _totalPages - 1) + TextButton( + onPressed: _completeWizard, + child: Text(l10n.wizardSkip), + ) + else + const SizedBox(width: 80), + ], + ), + ), + + // Page view with wizard content + Expanded( + child: PageView( + controller: _pageController, + onPageChanged: _onPageChanged, + children: [ + _buildWelcomePage(context, l10n, colorScheme), + _buildConnectingPage(context, l10n, colorScheme), + _buildSimpleModePage(context, l10n, colorScheme), + _buildChannelPage(context, l10n, colorScheme), + _buildContactsPage(context, l10n, colorScheme), + _buildMapPage(context, l10n, colorScheme), + ], + ), + ), + + // Page indicators + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + _totalPages, + (index) => Container( + margin: const EdgeInsets.symmetric(horizontal: 4.0), + width: _currentPage == index ? 12.0 : 8.0, + height: _currentPage == index ? 12.0 : 8.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _currentPage == index + ? colorScheme.primary + : colorScheme.outline.withValues(alpha: 0.3), + ), + ), + ), + ), + ), + + // Next/Get Started button + Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: _nextPage, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + _currentPage < _totalPages - 1 + ? l10n.wizardNext + : l10n.wizardGetStarted, + style: const TextStyle(fontSize: 16), + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildWelcomePage( + BuildContext context, + AppLocalizations l10n, + ColorScheme colorScheme, + ) { + return _buildPage( + icon: Icons.waving_hand, + iconColor: Colors.orange, + title: l10n.wizardWelcomeTitle, + description: l10n.wizardWelcomeDescription, + colorScheme: colorScheme, + ); + } + + Widget _buildConnectingPage( + BuildContext context, + AppLocalizations l10n, + ColorScheme colorScheme, + ) { + return _buildPage( + icon: Icons.bluetooth_searching, + iconColor: Colors.blue, + title: l10n.wizardConnectingTitle, + description: l10n.wizardConnectingDescription, + features: [ + _FeatureItem( + icon: Icons.radio, + text: l10n.wizardConnectingFeature1, + ), + _FeatureItem( + icon: Icons.link, + text: l10n.wizardConnectingFeature2, + ), + _FeatureItem( + icon: Icons.wifi_off, + text: l10n.wizardConnectingFeature3, + ), + ], + colorScheme: colorScheme, + ); + } + + Widget _buildSimpleModePage( + BuildContext context, + AppLocalizations l10n, + ColorScheme colorScheme, + ) { + return _buildPage( + icon: Icons.toggle_on, + iconColor: Colors.green, + title: l10n.wizardSimpleModeTitle, + description: l10n.wizardSimpleModeDescription, + features: [ + _FeatureItem( + icon: Icons.check_circle_outline, + text: l10n.wizardSimpleModeFeature1, + ), + _FeatureItem( + icon: Icons.settings, + text: l10n.wizardSimpleModeFeature2, + ), + ], + colorScheme: colorScheme, + ); + } + + Widget _buildChannelPage( + BuildContext context, + AppLocalizations l10n, + ColorScheme colorScheme, + ) { + return _buildPage( + icon: Icons.campaign, + iconColor: Colors.purple, + title: l10n.wizardChannelTitle, + description: l10n.wizardChannelDescription, + features: [ + _FeatureItem( + icon: Icons.public, + text: l10n.wizardChannelFeature1, + ), + _FeatureItem( + icon: Icons.groups, + text: l10n.wizardChannelFeature2, + ), + _FeatureItem( + icon: Icons.send, + text: l10n.wizardChannelFeature3, + ), + ], + colorScheme: colorScheme, + ); + } + + Widget _buildContactsPage( + BuildContext context, + AppLocalizations l10n, + ColorScheme colorScheme, + ) { + return _buildPage( + icon: Icons.people, + iconColor: Colors.teal, + title: l10n.wizardContactsTitle, + description: l10n.wizardContactsDescription, + features: [ + _FeatureItem( + icon: Icons.person_add, + text: l10n.wizardContactsFeature1, + ), + _FeatureItem( + icon: Icons.chat, + text: l10n.wizardContactsFeature2, + ), + _FeatureItem( + icon: Icons.battery_std, + text: l10n.wizardContactsFeature3, + ), + ], + colorScheme: colorScheme, + ); + } + + Widget _buildMapPage( + BuildContext context, + AppLocalizations l10n, + ColorScheme colorScheme, + ) { + return _buildPage( + icon: Icons.map, + iconColor: Colors.red, + title: l10n.wizardMapTitle, + description: l10n.wizardMapDescription, + features: [ + _FeatureItem( + icon: Icons.location_on, + text: l10n.wizardMapFeature1, + ), + _FeatureItem( + icon: Icons.person_pin_circle, + text: l10n.wizardMapFeature2, + ), + _FeatureItem( + icon: Icons.offline_pin, + text: l10n.wizardMapFeature3, + ), + _FeatureItem( + icon: Icons.draw, + text: l10n.wizardMapFeature4, + ), + ], + colorScheme: colorScheme, + ); + } + + Widget _buildPage({ + required IconData icon, + required Color iconColor, + required String title, + required String description, + List<_FeatureItem>? features, + required ColorScheme colorScheme, + }) { + return SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 20), + // Icon + Container( + padding: const EdgeInsets.all(24.0), + decoration: BoxDecoration( + color: iconColor.withValues(alpha: 0.1), + shape: BoxShape.circle, + ), + child: Icon( + icon, + size: 80, + color: iconColor, + ), + ), + const SizedBox(height: 32), + // Title + Text( + title, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + // Description + Text( + description, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: colorScheme.onSurface.withValues(alpha: 0.7), + height: 1.5, + ), + textAlign: TextAlign.center, + ), + if (features != null && features.isNotEmpty) ...[ + const SizedBox(height: 32), + // Features list + ...features.map((feature) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Icon( + feature.icon, + color: colorScheme.primary, + size: 24, + ), + const SizedBox(width: 16), + Expanded( + child: Text( + feature.text, + style: + Theme.of(context).textTheme.bodyMedium?.copyWith( + color: colorScheme.onSurface, + ), + ), + ), + ], + ), + )), + ], + const SizedBox(height: 20), + ], + ), + ); + } +} + +class _FeatureItem { + final IconData icon; + final String text; + + _FeatureItem({required this.icon, required this.text}); +} diff --git a/lib/services/wizard_preferences.dart b/lib/services/wizard_preferences.dart new file mode 100644 index 0000000..a5e1825 --- /dev/null +++ b/lib/services/wizard_preferences.dart @@ -0,0 +1,42 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +/// Service to manage welcome wizard preferences and state +class WizardPreferences { + static const String _wizardCompletedKey = 'wizard_completed'; + static const String _wizardVersionKey = 'wizard_version'; + static const int _currentWizardVersion = 1; + + /// Check if the welcome wizard has been completed + static Future isWizardCompleted() async { + final prefs = await SharedPreferences.getInstance(); + final completed = prefs.getBool(_wizardCompletedKey) ?? false; + final version = prefs.getInt(_wizardVersionKey) ?? 0; + + // Re-show wizard if version has changed (for major updates) + return completed && version >= _currentWizardVersion; + } + + /// Mark the welcome wizard as completed + static Future setWizardCompleted(bool completed) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_wizardCompletedKey, completed); + + if (completed) { + // Store current version when wizard is completed + await prefs.setInt(_wizardVersionKey, _currentWizardVersion); + } + } + + /// Get the wizard version last shown to the user + static Future getWizardVersion() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getInt(_wizardVersionKey) ?? 0; + } + + /// Reset wizard state (useful for testing or re-showing tutorial) + static Future resetWizard() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_wizardCompletedKey, false); + await prefs.setInt(_wizardVersionKey, 0); + } +}