diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..c3926c9 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,38 @@ +name: Deploy to Cloudflare Workers + +on: + push: + tags: + - '*' + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + # Match local development version which provides Dart 3.11.0 + flutter-version: '3.41.2' + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Get dependencies + run: flutter pub get + + - name: Build Web + run: bun run build + + - name: Deploy to Cloudflare + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: deploy diff --git a/.gitignore b/.gitignore index 157c7ec..a312737 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ keystore.properties # IDE .vscode/launch.json .vscode/settings.json + +# Cloudflare Wrangler +.wrangler \ No newline at end of file diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index ef19f02..cf147bd 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -717,17 +717,13 @@ class MeshCoreConnector extends ChangeNotifier { _scanSubscription = FlutterBluePlus.scanResults.listen((results) { _scanResults.clear(); - for (var result in results) { - if (result.device.platformName.startsWith("MeshCore-") || - result.advertisementData.advName.startsWith("MeshCore-") || - result.advertisementData.advName.startsWith("Whisper-")) { - _scanResults.add(result); - } - } + _scanResults.addAll(results); notifyListeners(); }); await FlutterBluePlus.startScan( + withKeywords: ["MeshCore-", "Whisper-"], + webOptionalServices: [Guid(MeshCoreUuids.service)], timeout: timeout, androidScanMode: AndroidScanMode.lowLatency, ); diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index 975e067..2dbcf5e 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -1606,6 +1606,8 @@ "scanner_bluetoothOff": "Bluetooth е изключен.", "scanner_enableBluetooth": "Активирайте Bluetooth", "scanner_bluetoothOffMessage": "Моля, активирайте Bluetooth, за да сканирате за устройства.", + "scanner_chromeRequired": "Изисква се браузър Chrome", + "scanner_chromeRequiredMessage": "Това уеб приложение изисква Google Chrome или браузър, базиран на Chromium, за поддръжка на Bluetooth.", "snrIndicator_lastSeen": "Последно видян", "snrIndicator_nearByRepeaters": "Близки повтарящи се устройства", "chat_ShowAllPaths": "Покажи всички пътища", diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 74b6c05..07190a9 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1635,6 +1635,8 @@ "pathTrace_clearTooltip": "Pfad löschen", "map_pathTraceCancelled": "Pfadverfolgung abgebrochen.", "scanner_bluetoothOffMessage": "Bitte aktivieren Sie Bluetooth, um nach Geräten zu suchen.", + "scanner_chromeRequired": "Chrome Browser erforderlich", + "scanner_chromeRequiredMessage": "Diese Webanwendung erfordert Google Chrome oder einen Chromium-basierten Browser für die Bluetooth-Unterstützung.", "scanner_bluetoothOff": "Bluetooth ist deaktiviert.", "scanner_enableBluetooth": "Bluetooth aktivieren", "snrIndicator_lastSeen": "Zuletzt gesehen", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 175c346..f0b0587 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -72,6 +72,8 @@ "scanner_scan": "Scan", "scanner_bluetoothOff": "Bluetooth is off", "scanner_bluetoothOffMessage": "Please turn on Bluetooth to scan for devices", + "scanner_chromeRequired": "Chrome Browser Required", + "scanner_chromeRequiredMessage": "This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.", "scanner_enableBluetooth": "Enable Bluetooth", "device_quickSwitch": "Quick switch", "device_meshcore": "MeshCore", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 74339ff..47765c6 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1632,6 +1632,8 @@ "map_removeLast": "Eliminar último", "map_pathTraceCancelled": "Rastreo de ruta cancelado.", "scanner_bluetoothOffMessage": "Por favor, active el Bluetooth para escanear dispositivos.", + "scanner_chromeRequired": "Navegador Chrome requerido", + "scanner_chromeRequiredMessage": "Esta aplicación web requiere Google Chrome o un navegador basado en Chromium para el soporte de Bluetooth.", "scanner_bluetoothOff": "Bluetooth está desactivado.", "scanner_enableBluetooth": "Habilitar Bluetooth", "snrIndicator_nearByRepeaters": "Repetidores cercanos", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 0697aee..b742dc9 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1604,6 +1604,8 @@ "map_removeLast": "Supprimer le dernier", "map_runTrace": "Exécuter la traçage de chemin", "scanner_bluetoothOffMessage": "Veuillez activer le Bluetooth pour rechercher des appareils.", + "scanner_chromeRequired": "Navigateur Chrome requis", + "scanner_chromeRequiredMessage": "Cette application web nécessite Google Chrome ou un navigateur basé sur Chromium pour le support Bluetooth.", "scanner_bluetoothOff": "Le Bluetooth est désactivé.", "scanner_enableBluetooth": "Activer le Bluetooth", "snrIndicator_lastSeen": "Dernière fois vu", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 4798d26..82adad8 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1605,6 +1605,8 @@ "map_tapToAdd": "Tocca i nodi per aggiungerli al percorso.", "scanner_bluetoothOff": "Il Bluetooth è disattivato.", "scanner_bluetoothOffMessage": "Si prega di attivare il Bluetooth per effettuare la scansione dei dispositivi.", + "scanner_chromeRequired": "Browser Chrome richiesto", + "scanner_chromeRequiredMessage": "Questa applicazione web richiede Google Chrome o un browser basato su Chromium per il supporto Bluetooth.", "scanner_enableBluetooth": "Abilita il Bluetooth", "snrIndicator_nearByRepeaters": "Ripetitori vicini", "snrIndicator_lastSeen": "Ultimo accesso", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index ff2c726..c48994c 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -388,6 +388,18 @@ abstract class AppLocalizations { /// **'Please turn on Bluetooth to scan for devices'** String get scanner_bluetoothOffMessage; + /// No description provided for @scanner_chromeRequired. + /// + /// In en, this message translates to: + /// **'Chrome Browser Required'** + String get scanner_chromeRequired; + + /// No description provided for @scanner_chromeRequiredMessage. + /// + /// In en, this message translates to: + /// **'This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.'** + String get scanner_chromeRequiredMessage; + /// No description provided for @scanner_enableBluetooth. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index da7ddc9..c168b7c 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -150,6 +150,13 @@ class AppLocalizationsBg extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Моля, активирайте Bluetooth, за да сканирате за устройства.'; + @override + String get scanner_chromeRequired => 'Изисква се браузър Chrome'; + + @override + String get scanner_chromeRequiredMessage => + 'Това уеб приложение изисква Google Chrome или браузър, базиран на Chromium, за поддръжка на Bluetooth.'; + @override String get scanner_enableBluetooth => 'Активирайте Bluetooth'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 228ffe7..c7eb927 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -150,6 +150,13 @@ class AppLocalizationsDe extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Bitte aktivieren Sie Bluetooth, um nach Geräten zu suchen.'; + @override + String get scanner_chromeRequired => 'Chrome Browser erforderlich'; + + @override + String get scanner_chromeRequiredMessage => + 'Diese Webanwendung erfordert Google Chrome oder einen Chromium-basierten Browser für die Bluetooth-Unterstützung.'; + @override String get scanner_enableBluetooth => 'Bluetooth aktivieren'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 70b3393..4458062 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -149,6 +149,13 @@ class AppLocalizationsEn extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Please turn on Bluetooth to scan for devices'; + @override + String get scanner_chromeRequired => 'Chrome Browser Required'; + + @override + String get scanner_chromeRequiredMessage => + 'This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.'; + @override String get scanner_enableBluetooth => 'Enable Bluetooth'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 876666b..ce4b615 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -150,6 +150,13 @@ class AppLocalizationsEs extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Por favor, active el Bluetooth para escanear dispositivos.'; + @override + String get scanner_chromeRequired => 'Navegador Chrome requerido'; + + @override + String get scanner_chromeRequiredMessage => + 'Esta aplicación web requiere Google Chrome o un navegador basado en Chromium para el soporte de Bluetooth.'; + @override String get scanner_enableBluetooth => 'Habilitar Bluetooth'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 0c11eac..6118444 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -150,6 +150,13 @@ class AppLocalizationsFr extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Veuillez activer le Bluetooth pour rechercher des appareils.'; + @override + String get scanner_chromeRequired => 'Navigateur Chrome requis'; + + @override + String get scanner_chromeRequiredMessage => + 'Cette application web nécessite Google Chrome ou un navigateur basé sur Chromium pour le support Bluetooth.'; + @override String get scanner_enableBluetooth => 'Activer le Bluetooth'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 8a8fe71..20873b9 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -150,6 +150,13 @@ class AppLocalizationsIt extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Si prega di attivare il Bluetooth per effettuare la scansione dei dispositivi.'; + @override + String get scanner_chromeRequired => 'Browser Chrome richiesto'; + + @override + String get scanner_chromeRequiredMessage => + 'Questa applicazione web richiede Google Chrome o un browser basato su Chromium per il supporto Bluetooth.'; + @override String get scanner_enableBluetooth => 'Abilita il Bluetooth'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 8b4eee5..323981d 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -149,6 +149,13 @@ class AppLocalizationsNl extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Zorg ervoor dat Bluetooth is ingeschakeld om naar apparaten te zoeken.'; + @override + String get scanner_chromeRequired => 'Chrome-browser vereist'; + + @override + String get scanner_chromeRequiredMessage => + 'Deze webapplicatie vereist Google Chrome of een op Chromium gebaseerde browser voor Bluetooth-ondersteuning.'; + @override String get scanner_enableBluetooth => 'Activeer Bluetooth'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index cff6010..e033359 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -150,6 +150,13 @@ class AppLocalizationsPl extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Prosimy włączyć Bluetooth, aby przeskanować urządzenia.'; + @override + String get scanner_chromeRequired => 'Wymagana przeglądarka Chrome'; + + @override + String get scanner_chromeRequiredMessage => + 'Ta aplikacja internetowa wymaga przeglądarki Google Chrome lub opartej na Chromium do obsługi Bluetooth.'; + @override String get scanner_enableBluetooth => 'Włącz Bluetooth'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 831d47a..dd19452 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -150,6 +150,13 @@ class AppLocalizationsPt extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Por favor, ative o Bluetooth para escanear por dispositivos.'; + @override + String get scanner_chromeRequired => 'Navegador Chrome necessário'; + + @override + String get scanner_chromeRequiredMessage => + 'Esta aplicação web requer o Google Chrome ou um navegador baseado no Chromium para suporte de Bluetooth.'; + @override String get scanner_enableBluetooth => 'Ative o Bluetooth'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 5c73e3e..5f5591d 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -149,6 +149,13 @@ class AppLocalizationsRu extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Пожалуйста, включите Bluetooth, чтобы найти устройства.'; + @override + String get scanner_chromeRequired => 'Требуется браузер Chrome'; + + @override + String get scanner_chromeRequiredMessage => + 'Для поддержки Bluetooth в этом веб-приложении требуется Google Chrome или браузер на базе Chromium.'; + @override String get scanner_enableBluetooth => 'Включите Bluetooth'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index b4f28fb..82e4d06 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -150,6 +150,13 @@ class AppLocalizationsSk extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Prosím, zapnite Bluetooth, aby ste mohli skenovať pre zariadenia.'; + @override + String get scanner_chromeRequired => 'Vyžaduje sa prehliadač Chrome'; + + @override + String get scanner_chromeRequiredMessage => + 'Táto webová aplikácia vyžaduje Google Chrome alebo prehliadač založený na Chromium pre podporu Bluetooth.'; + @override String get scanner_enableBluetooth => 'Povolte Bluetooth'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index e015e45..9b1bfc9 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -150,6 +150,13 @@ class AppLocalizationsSl extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Prosimo, vklopite Bluetooth, da lahko poiščete naprave.'; + @override + String get scanner_chromeRequired => 'Zahtevan brskalnik Chrome'; + + @override + String get scanner_chromeRequiredMessage => + 'Ta spletna aplikacija za podporo Bluetooth zahteva Google Chrome ali brskalnik na osnovi Chromiuma.'; + @override String get scanner_enableBluetooth => 'Omogočite Bluetooth'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 3a25c3c..ddfcf41 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -149,6 +149,13 @@ class AppLocalizationsSv extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Vänligen aktivera Bluetooth för att söka efter enheter.'; + @override + String get scanner_chromeRequired => 'Chrome-webbläsare krävs'; + + @override + String get scanner_chromeRequiredMessage => + 'Denna webbapplikation kräver Google Chrome oder en Chromium-baserader webbläsare för Bluetooth-stöd.'; + @override String get scanner_enableBluetooth => 'Aktivera Bluetooth'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index cd820cf..b44a7cb 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -150,6 +150,13 @@ class AppLocalizationsUk extends AppLocalizations { String get scanner_bluetoothOffMessage => 'Будь ласка, увімкніть Bluetooth, щоб сканувати пристрої.'; + @override + String get scanner_chromeRequired => 'Потрібен браузер Chrome'; + + @override + String get scanner_chromeRequiredMessage => + 'Для підтримки Bluetooth у цьому веб-додатку потрібен Google Chrome або браузер на базі Chromium.'; + @override String get scanner_enableBluetooth => 'Увімкніть Bluetooth'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index b63f714..b3d85ed 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -148,6 +148,13 @@ class AppLocalizationsZh extends AppLocalizations { @override String get scanner_bluetoothOffMessage => '请开启蓝牙以搜索设备'; + @override + String get scanner_chromeRequired => '需要 Chrome 浏览器'; + + @override + String get scanner_chromeRequiredMessage => + '此 Web 应用程序需要 Google Chrome 或基于 Chromium 的浏览器以支持蓝牙。'; + @override String get scanner_enableBluetooth => '启用蓝牙'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 69ebecc..35cb375 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1605,6 +1605,8 @@ "map_runTrace": "Padeshulp traceren", "scanner_enableBluetooth": "Activeer Bluetooth", "scanner_bluetoothOffMessage": "Zorg ervoor dat Bluetooth is ingeschakeld om naar apparaten te zoeken.", + "scanner_chromeRequired": "Chrome-browser vereist", + "scanner_chromeRequiredMessage": "Deze webapplicatie vereist Google Chrome of een op Chromium gebaseerde browser voor Bluetooth-ondersteuning.", "scanner_bluetoothOff": "Bluetooth is uitgeschakeld", "snrIndicator_lastSeen": "Laatst gezien", "snrIndicator_nearByRepeaters": "Nabije herhalingseenheden", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 75e1d34..23c4cbc 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1604,6 +1604,8 @@ "map_removeLast": "Usuń ostatni", "map_tapToAdd": "Kliknij na węzły, aby dodać je do ścieżki.", "scanner_bluetoothOffMessage": "Prosimy włączyć Bluetooth, aby przeskanować urządzenia.", + "scanner_chromeRequired": "Wymagana przeglądarka Chrome", + "scanner_chromeRequiredMessage": "Ta aplikacja internetowa wymaga przeglądarki Google Chrome lub opartej na Chromium do obsługi Bluetooth.", "scanner_bluetoothOff": "Bluetooth jest wyłączony", "scanner_enableBluetooth": "Włącz Bluetooth", "snrIndicator_lastSeen": "Ostatnio widziany", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index f6ada19..05792c6 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -1606,6 +1606,8 @@ "scanner_enableBluetooth": "Ative o Bluetooth", "scanner_bluetoothOff": "Bluetooth está desativado", "scanner_bluetoothOffMessage": "Por favor, ative o Bluetooth para escanear por dispositivos.", + "scanner_chromeRequired": "Navegador Chrome necessário", + "scanner_chromeRequiredMessage": "Esta aplicação web requer o Google Chrome ou um navegador baseado no Chromium para suporte de Bluetooth.", "snrIndicator_nearByRepeaters": "Repetidores Próximos", "snrIndicator_lastSeen": "Visto pela última vez", "chat_ShowAllPaths": "Mostrar todos os caminhos", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 9aef298..9460f44 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -846,6 +846,8 @@ "scanner_enableBluetooth": "Включите Bluetooth", "scanner_bluetoothOff": "Bluetooth выключен", "scanner_bluetoothOffMessage": "Пожалуйста, включите Bluetooth, чтобы найти устройства.", + "scanner_chromeRequired": "Требуется браузер Chrome", + "scanner_chromeRequiredMessage": "Для поддержки Bluetooth в этом веб-приложении требуется Google Chrome или браузер на базе Chromium.", "snrIndicator_nearByRepeaters": "Ближайшие ретрансляторы", "snrIndicator_lastSeen": "Последний раз видели", "chat_ShowAllPaths": "Показать все пути", diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 672b7d7..5bc00c6 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1604,6 +1604,8 @@ "map_runTrace": "Spustiť trasovaním cesty", "map_pathTraceCancelled": "Zrušenie stopáže cesty bolo zrušené.", "scanner_bluetoothOffMessage": "Prosím, zapnite Bluetooth, aby ste mohli skenovať pre zariadenia.", + "scanner_chromeRequired": "Vyžaduje sa prehliadač Chrome", + "scanner_chromeRequiredMessage": "Táto webová aplikácia vyžaduje Google Chrome alebo prehliadač založený na Chromium pre podporu Bluetooth.", "scanner_bluetoothOff": "Bluetooth je vypnutý", "scanner_enableBluetooth": "Povolte Bluetooth", "snrIndicator_lastSeen": "Naposledy videný", diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 09359a1..226a715 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -1605,6 +1605,8 @@ "map_pathTraceCancelled": "Spremljanje poti je prekinjeno.", "scanner_enableBluetooth": "Omogočite Bluetooth", "scanner_bluetoothOffMessage": "Prosimo, vklopite Bluetooth, da lahko poiščete naprave.", + "scanner_chromeRequired": "Zahtevan brskalnik Chrome", + "scanner_chromeRequiredMessage": "Ta spletna aplikacija za podporo Bluetooth zahteva Google Chrome ali brskalnik na osnovi Chromiuma.", "scanner_bluetoothOff": "Bluetooth je izklopljen", "snrIndicator_lastSeen": "Zadnjič videno", "snrIndicator_nearByRepeaters": "Bližnji ponovitelji", diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index a923cc9..bfccfff 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -1605,6 +1605,8 @@ "map_removeLast": "Ta bort sista", "scanner_enableBluetooth": "Aktivera Bluetooth", "scanner_bluetoothOffMessage": "Vänligen aktivera Bluetooth för att söka efter enheter.", + "scanner_chromeRequired": "Chrome-webbläsare krävs", + "scanner_chromeRequiredMessage": "Denna webbapplikation kräver Google Chrome oder en Chromium-baserader webbläsare för Bluetooth-stöd.", "scanner_bluetoothOff": "Bluetooth är avstängt", "snrIndicator_lastSeen": "Senast sedd", "snrIndicator_nearByRepeaters": "Närliggande uppreparstationer", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index c86f11c..6063bc8 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1605,6 +1605,8 @@ "map_pathTraceCancelled": "Відмінується трасування шляху", "scanner_enableBluetooth": "Увімкніть Bluetooth", "scanner_bluetoothOffMessage": "Будь ласка, увімкніть Bluetooth, щоб сканувати пристрої.", + "scanner_chromeRequired": "Потрібен браузер Chrome", + "scanner_chromeRequiredMessage": "Для підтримки Bluetooth у цьому веб-додатку потрібен Google Chrome або браузер на базі Chromium.", "scanner_bluetoothOff": "Bluetooth вимкнено", "snrIndicator_lastSeen": "Останній раз бачили", "snrIndicator_nearByRepeaters": "Ближні ретранслятори", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 63c02a5..db4953e 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1609,6 +1609,8 @@ "map_removeLast": "移除最后一个", "map_runTrace": "运行路径追踪", "scanner_bluetoothOffMessage": "请开启蓝牙以搜索设备", + "scanner_chromeRequired": "需要 Chrome 浏览器", + "scanner_chromeRequiredMessage": "此 Web 应用程序需要 Google Chrome 或基于 Chromium 的浏览器以支持蓝牙。", "scanner_bluetoothOff": "蓝牙已关闭", "scanner_enableBluetooth": "启用蓝牙", "snrIndicator_lastSeen": "最近访问", diff --git a/lib/main.dart b/lib/main.dart index 5a11188..9e53e21 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,9 @@ import 'package:flutter/foundation.dart'; import 'l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'screens/chrome_required_screen.dart'; +import 'utils/platform_info.dart'; + import 'connector/meshcore_connector.dart'; import 'screens/scanner_screen.dart'; import 'services/storage_service.dart'; @@ -187,7 +190,9 @@ class MeshCoreApp extends StatelessWidget { NotificationService().setLocale(locale); return child ?? const SizedBox.shrink(); }, - home: const ScannerScreen(), + home: (PlatformInfo.isWeb && !PlatformInfo.isChrome) + ? const ChromeRequiredScreen() + : const ScannerScreen(), ); }, ), diff --git a/lib/screens/chrome_required_screen.dart b/lib/screens/chrome_required_screen.dart new file mode 100644 index 0000000..1827aeb --- /dev/null +++ b/lib/screens/chrome_required_screen.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import '../l10n/l10n.dart'; + +class ChromeRequiredScreen extends StatelessWidget { + const ChromeRequiredScreen({super.key}); + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + final theme = Theme.of(context); + final isDark = theme.brightness == Brightness.dark; + + return Scaffold( + body: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 32), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: isDark + ? [const Color(0xFF1A1A1A), const Color(0xFF0D0D0D)] + : [const Color(0xFFF5F7FA), const Color(0xFFE4E7EB)], + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: Colors.orange.withValues(alpha: 0.1), + shape: BoxShape.circle, + ), + child: const Icon( + Icons.browser_not_supported_rounded, + size: 80, + color: Colors.orange, + ), + ), + const SizedBox(height: 32), + Text( + l10n.scanner_chromeRequired, + textAlign: TextAlign.center, + style: theme.textTheme.headlineMedium?.copyWith( + fontWeight: FontWeight.bold, + color: isDark ? Colors.white : Colors.black87, + ), + ), + const SizedBox(height: 16), + Text( + l10n.scanner_chromeRequiredMessage, + textAlign: TextAlign.center, + style: theme.textTheme.bodyLarge?.copyWith( + color: isDark ? Colors.white70 : Colors.black54, + height: 1.5, + ), + ), + const SizedBox(height: 48), + // We can't really "fix" it for them other than telling them to use Chrome + // but we can provide a nice visual. + Container( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + decoration: BoxDecoration( + color: Colors.blue.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(30), + border: Border.all(color: Colors.blue.withValues(alpha: 0.3)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.info_outline, size: 20, color: Colors.blue), + const SizedBox(width: 12), + Text( + "Web Bluetooth requires a Chromium browser", + style: theme.textTheme.bodyMedium?.copyWith( + color: Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/scanner_screen.dart b/lib/screens/scanner_screen.dart index af9d75e..4017408 100644 --- a/lib/screens/scanner_screen.dart +++ b/lib/screens/scanner_screen.dart @@ -1,7 +1,6 @@ import 'dart:async'; -import 'dart:io' show Platform; - import 'package:flutter/material.dart'; +import '../utils/platform_info.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:provider/provider.dart'; @@ -46,17 +45,22 @@ class _ScannerScreenState extends State { connector.addListener(_connectionListener); - _bluetoothStateSubscription = FlutterBluePlus.adapterState.listen((state) { - if (mounted) { - setState(() { - _bluetoothState = state; - }); - // Cancel scan if Bluetooth turns off while scanning - if (state != BluetoothAdapterState.on) { - unawaited(connector.stopScan()); + _bluetoothStateSubscription = FlutterBluePlus.adapterState.listen( + (state) { + if (mounted) { + setState(() { + _bluetoothState = state; + }); + // Cancel scan if Bluetooth turns off while scanning + if (state != BluetoothAdapterState.on) { + unawaited(connector.stopScan()); + } } - } - }); + }, + onError: (Object e) { + debugPrint("Scanner adapterState stream error: $e"); + }, + ); } @override @@ -108,7 +112,11 @@ class _ScannerScreenState extends State { if (isScanning) { connector.stopScan(); } else { - connector.startScan(); + unawaited( + connector.startScan().catchError((e) { + debugPrint("Scanner screen startScan error: $e"); + }), + ); } }, icon: isScanning @@ -265,7 +273,7 @@ class _ScannerScreenState extends State { ], ), ), - if (Platform.isAndroid) + if (PlatformInfo.isAndroid) TextButton( onPressed: () => FlutterBluePlus.turnOn(), child: Text(context.l10n.scanner_enableBluetooth), diff --git a/lib/services/background_service.dart b/lib/services/background_service.dart index 0edd393..6202b3b 100644 --- a/lib/services/background_service.dart +++ b/lib/services/background_service.dart @@ -1,12 +1,11 @@ -import 'dart:io'; - +import '../utils/platform_info.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; class BackgroundService { bool _initialized = false; Future initialize() async { - if (!Platform.isAndroid || _initialized) return; + if (!PlatformInfo.isAndroid || _initialized) return; FlutterForegroundTask.init( androidNotificationOptions: AndroidNotificationOptions( channelId: 'meshcore_background', @@ -29,7 +28,7 @@ class BackgroundService { } Future start() async { - if (!Platform.isAndroid) return; + if (!PlatformInfo.isAndroid) return; if (!_initialized) { await initialize(); } @@ -43,7 +42,7 @@ class BackgroundService { } Future stop() async { - if (!Platform.isAndroid) return; + if (!PlatformInfo.isAndroid) return; final running = await FlutterForegroundTask.isRunningService; if (!running) return; await FlutterForegroundTask.stopService(); diff --git a/lib/utils/browser_detection.dart b/lib/utils/browser_detection.dart new file mode 100644 index 0000000..b8dca93 --- /dev/null +++ b/lib/utils/browser_detection.dart @@ -0,0 +1,2 @@ +export 'browser_detection_stub.dart' + if (dart.library.js_interop) 'browser_detection_web.dart'; diff --git a/lib/utils/browser_detection_stub.dart b/lib/utils/browser_detection_stub.dart new file mode 100644 index 0000000..f872d9a --- /dev/null +++ b/lib/utils/browser_detection_stub.dart @@ -0,0 +1,3 @@ +class BrowserDetection { + static bool get isChrome => false; +} diff --git a/lib/utils/browser_detection_web.dart b/lib/utils/browser_detection_web.dart new file mode 100644 index 0000000..bbb9c76 --- /dev/null +++ b/lib/utils/browser_detection_web.dart @@ -0,0 +1,9 @@ +import 'package:web/web.dart' as web; + +class BrowserDetection { + static bool get isChrome { + final userAgent = web.window.navigator.userAgent.toLowerCase(); + final isChrome = userAgent.contains('chrome'); + return isChrome; + } +} diff --git a/lib/utils/gpx_export.dart b/lib/utils/gpx_export.dart index 595479c..b0165bd 100644 --- a/lib/utils/gpx_export.dart +++ b/lib/utils/gpx_export.dart @@ -4,6 +4,7 @@ import 'package:meshcore_open/connector/meshcore_connector.dart'; import 'package:meshcore_open/connector/meshcore_protocol.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; +import '../utils/platform_info.dart'; import 'package:share_plus/share_plus.dart'; @@ -109,6 +110,10 @@ class GpxExport { String shareText, String subject, ) async { + if (PlatformInfo.isWeb) { + debugPrint("GPX export is not supported on Web."); + return gpxExportNotAvailable; + } if (_contacts.isEmpty) { debugPrint("No repeaters to export – nothing to share."); return gpxExportNoContacts; diff --git a/lib/utils/platform_info.dart b/lib/utils/platform_info.dart new file mode 100644 index 0000000..dc8e27e --- /dev/null +++ b/lib/utils/platform_info.dart @@ -0,0 +1,36 @@ +import 'package:flutter/foundation.dart'; +import 'dart:io' show Platform; +import 'browser_detection.dart'; + +/// Utility class to safely check the current platform across web and native. +/// +/// Using `Platform` from `dart:io` directly on Web causes a crash. +/// This class handles the `kIsWeb` check first to avoid those crashes. +class PlatformInfo { + /// Whether the app is running in a web browser. + static bool get isWeb => kIsWeb; + + /// Whether the app is running in the Chrome browser (only relevant if [isWeb] is true). + static bool get isChrome => isWeb && BrowserDetection.isChrome; + + /// Whether the app is running on Android. + static bool get isAndroid => !kIsWeb && Platform.isAndroid; + + /// Whether the app is running on iOS. + static bool get isIOS => !kIsWeb && Platform.isIOS; + + /// Whether the app is running on macOS. + static bool get isMacOS => !kIsWeb && Platform.isMacOS; + + /// Whether the app is running on Windows. + static bool get isWindows => !kIsWeb && Platform.isWindows; + + /// Whether the app is running on Linux. + static bool get isLinux => !kIsWeb && Platform.isLinux; + + /// Whether the app is running on a mobile platform (Android or iOS). + static bool get isMobile => isAndroid || isIOS; + + /// Whether the app is running on a desktop platform (macOS, Windows, or Linux). + static bool get isDesktop => isMacOS || isWindows || isLinux; +} diff --git a/lib/widgets/repeater_login_dialog.dart b/lib/widgets/repeater_login_dialog.dart index b550cc2..ec0af66 100644 --- a/lib/widgets/repeater_login_dialog.dart +++ b/lib/widgets/repeater_login_dialog.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import '../utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; @@ -326,8 +327,7 @@ class _RepeaterLoginDialogState extends State { }, onSubmitted: (_) => _handleLogin(), autofocus: - !(defaultTargetPlatform == TargetPlatform.android || - defaultTargetPlatform == TargetPlatform.iOS) && + !PlatformInfo.isMobile && _passwordController.text.isEmpty, ), const SizedBox(height: 12), diff --git a/lib/widgets/room_login_dialog.dart b/lib/widgets/room_login_dialog.dart index cba7bec..7324f44 100644 --- a/lib/widgets/room_login_dialog.dart +++ b/lib/widgets/room_login_dialog.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import '../utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; @@ -274,8 +275,7 @@ class _RoomLoginDialogState extends State { ), onSubmitted: (_) => _handleLogin(), autofocus: - !(defaultTargetPlatform == TargetPlatform.android || - defaultTargetPlatform == TargetPlatform.iOS) && + !PlatformInfo.isMobile && _passwordController.text.isEmpty, ), const SizedBox(height: 12), diff --git a/package.json b/package.json new file mode 100644 index 0000000..1684721 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "meshcore-open", + "scripts": { + "build": "dart run build_pipe:build", + "deploy": "bun x wrangler deploy" + } +} diff --git a/pubspec.yaml b/pubspec.yaml index fb68b00..f85530f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -60,6 +60,7 @@ dependencies: gpx: ^2.3.0 path_provider: ^2.1.5 share_plus: ^12.0.1 + build_pipe: ^0.3.1 material_symbols_icons: ^4.2906.0 web: ^1.1.1 flutter_svg: ^2.0.10+1 @@ -128,3 +129,16 @@ flutter_launcher_icons: # # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package + +build_pipe: + workflows: + default: + clean_flutter: true # Optional: Cleans old build artifacts before running + generate_log: true # Optional: Outputs a build log file for debugging + platforms: + web: + build: + build_command: flutter build web --release --pwa-strategy=none + # Strongly recommended: disables the default service worker which often causes more cache headaches + add_version_query_param: true + # This is the key flag! It appends ?v= to bootstrap/JS files \ No newline at end of file diff --git a/wrangler.toml b/wrangler.toml new file mode 100644 index 0000000..f3c57d9 --- /dev/null +++ b/wrangler.toml @@ -0,0 +1,7 @@ +#:schema node_modules/wrangler/config-schema.json +name = "meshcore" +compatibility_date = "2025-10-08" + +[assets] +directory = "build/web" +