From bedffba2876cf8337195282ff347f4fe14d9bc6b Mon Sep 17 00:00:00 2001 From: miakh <2659269+miakh@users.noreply.github.com> Date: Fri, 2 Aug 2024 23:39:48 +0200 Subject: [PATCH] install page --- assets/translations/cs.json | 3 + assets/translations/de.json | 3 + assets/translations/en.json | 3 + assets/translations/pl.json | 3 + assets/translations/sk.json | 3 + assets/translations/uk.json | 3 + ios/Runner.xcodeproj/project.pbxproj | 6 +- lib/RouterService.dart | 6 + lib/appConfig.dart | 5 + lib/pages/InstallPage.dart | 196 +++++++++++++++++++++++++++ lib/services/js/js_stub.dart | 8 ++ lib/services/js/js_web.dart | 12 ++ 12 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 lib/pages/InstallPage.dart diff --git a/assets/translations/cs.json b/assets/translations/cs.json index f59296ef..402873b3 100644 --- a/assets/translations/cs.json +++ b/assets/translations/cs.json @@ -225,5 +225,8 @@ "Install App": "Instalovat aplikaci", "This platform or browser does not support the PWA install prompt or the app is already installed.": "Tato platforma nebo prohlížeč nepodporuje instalaci PWA nebo je aplikace již nainstalována.", "The app is already installed.": "Aplikace je již nainstalována.", + "Download App": "Stáhnout aplikaci", + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.": "Instalace se nezdařila. Zkuste prosím otevřít tento odkaz v prohlížeči Chrome, Edge nebo v jiném.", + "Copied to clipboard": "Zkopírováno do schránky", "_": "_" } \ No newline at end of file diff --git a/assets/translations/de.json b/assets/translations/de.json index 07f5d2b5..177c3103 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -225,5 +225,8 @@ "Install App": "App installieren", "This platform or browser does not support the PWA install prompt or the app is already installed.": "Diese Plattform oder der Browser unterstützt die Installation von PWA nicht oder die App ist bereits installiert.", "The app is already installed.": "Die App ist bereits installiert.", + "Download App": "App herunterladen", + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.": "Installation fehlgeschlagen. Bitte versuchen Sie, diesen Link in Chrome, Edge oder einem anderen zu öffnen.", + "Copied to clipboard": "In die Zwischenablage kopiert", "_": "_" } diff --git a/assets/translations/en.json b/assets/translations/en.json index f6525d95..a8bab211 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -225,5 +225,8 @@ "Install App": "Install App", "This platform or browser does not support the PWA install prompt or the app is already installed.": "This platform or browser does not support the PWA install prompt or the app is already installed.", "The app is already installed.": "The app is already installed.", + "Download App": "Download App", + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.": "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.", + "Copied to clipboard": "Copied to clipboard", "_":"_" } \ No newline at end of file diff --git a/assets/translations/pl.json b/assets/translations/pl.json index a4a0302f..9e70cc78 100644 --- a/assets/translations/pl.json +++ b/assets/translations/pl.json @@ -224,5 +224,8 @@ "Install App": "Zainstaluj aplikację", "This platform or browser does not support the PWA install prompt or the app is already installed.": "Ta platforma lub przeglądarka nie obsługuje instalacji PWA lub aplikacja jest już zainstalowana.", "The app is already installed.": "Aplikacja jest już zainstalowana.", + "Download App": "Pobierz aplikację", + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.": "Instalacja nie powiodła się. Spróbuj otworzyć ten link w przeglądarce Chrome, Edge lub innej.", + "Copied to clipboard": "Skopiowane do schowka", "_": "_" } \ No newline at end of file diff --git a/assets/translations/sk.json b/assets/translations/sk.json index 69cb7b01..6b248bc0 100644 --- a/assets/translations/sk.json +++ b/assets/translations/sk.json @@ -225,5 +225,8 @@ "Install App": "Nainštalovať aplikáciu", "This platform or browser does not support the PWA install prompt or the app is already installed.": "Táto platforma alebo prehliadač nepodporuje inštaláciu PWA alebo je aplikácia už nainštalovaná.", "The app is already installed.": "Aplikácia je už nainštalovaná.", + "Download App": "Stiahnite si aplikáciu", + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.": "Inštalácia zlyhala. Skúste prosím otvoriť tento odkaz v prehliadači Chrome, Edge alebo v inom.", + "Copied to clipboard": "Skopírované do schránky", "_": "_" } \ No newline at end of file diff --git a/assets/translations/uk.json b/assets/translations/uk.json index fb571c3a..4b517aef 100644 --- a/assets/translations/uk.json +++ b/assets/translations/uk.json @@ -225,5 +225,8 @@ "Install App": "Встановити додаток", "This platform or browser does not support the PWA install prompt or the app is already installed.": "Ця платформа або браузер не підтримує встановлення PWA або додаток уже встановлено.", "The app is already installed.": "Додаток уже встановлено.", + "Download App": "Завантажити додаток", + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.": "Не вдалося встановити. Спробуйте відкрити це посилання в браузері Chrome, Edge або іншому.", + "Copied to clipboard": "Скопійовано до буфера обміну", "_": "_" } diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index d55bfe12..23514651 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -670,7 +670,7 @@ }; 78D9A5DA2B292F2E00AFFF1A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 41860034A371AF7B12D79E87 /* Pods-OneSignalNotificationServiceExtension.debug.xcconfig */; + baseConfigurationReference = 9066AA37269E4A2725D57345 /* Pods-OneSignalNotificationServiceExtension.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -707,7 +707,7 @@ }; 78D9A5DB2B292F2E00AFFF1A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 123F1F10B8600F1A8A29F7E1 /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */; + baseConfigurationReference = 8E149D62039179741BFC2CB7 /* Pods-OneSignalNotificationServiceExtension.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -744,7 +744,7 @@ }; 78D9A5DC2B292F2E00AFFF1A /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 11F34DEAD2F757956490C165 /* Pods-OneSignalNotificationServiceExtension.profile.xcconfig */; + baseConfigurationReference = FE2D5DC25231EEB036DD5B29 /* Pods-OneSignalNotificationServiceExtension.profile.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/lib/RouterService.dart b/lib/RouterService.dart index 61e9944a..561b170e 100644 --- a/lib/RouterService.dart +++ b/lib/RouterService.dart @@ -8,6 +8,7 @@ import 'package:fstapp/pages/EventPage.dart'; import 'package:fstapp/pages/HomePage.dart'; import 'package:fstapp/pages/HtmlEditorPage.dart'; import 'package:fstapp/pages/InfoPage.dart'; +import 'package:fstapp/pages/InstallPage.dart'; import 'package:fstapp/pages/LoginPage.dart'; import 'package:fstapp/pages/MapPage.dart'; import 'package:fstapp/pages/NewsFormPage.dart'; @@ -80,6 +81,7 @@ class RouterService{ static Uri getCurrentUri(){ return Uri.base; + //GoRouterState.of(context).uri.toString() } static final router = GoRouter( @@ -113,6 +115,10 @@ class RouterService{ path: "/${SettingsPage.ROUTE}", builder: (context, state) => const SettingsPage(), ), + GoRoute( + path: "/${InstallPage.ROUTE}", + builder: (context, state) => const InstallPage(), + ), GoRoute( path: "/${CheckPage.ROUTE}/:id", builder: (context, state) { diff --git a/lib/appConfig.dart b/lib/appConfig.dart index 7b041a31..6daf7577 100644 --- a/lib/appConfig.dart +++ b/lib/appConfig.dart @@ -17,6 +17,11 @@ class AppConfig { static const String oneSignalAppId = '73f77f22-961a-4ded-9647-e33a7ac14f90'; static const String defaultLink = "conference2024"; + static const String appStoreLink = "https://apps.apple.com/us/app/festapp/id6474078383"; + static const String playStoreLink = ""; + static const String desktopAppLink = ""; + + static const bool isEventTimeUtc = true; static const int daySplitHour = 4; diff --git a/lib/pages/InstallPage.dart b/lib/pages/InstallPage.dart new file mode 100644 index 00000000..5264a982 --- /dev/null +++ b/lib/pages/InstallPage.dart @@ -0,0 +1,196 @@ +import 'package:flutter/material.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:fstapp/RouterService.dart'; +import 'package:fstapp/appConfig.dart'; +import 'package:fstapp/dataServices/DataService.dart'; +import 'package:fstapp/widgets/ButtonsHelper.dart'; +import 'package:fstapp/styles/Styles.dart'; +import 'package:pwa_install/pwa_install.dart'; +import 'package:flutter/services.dart'; +import '../services/js/js_interop.dart'; + +class InstallPage extends StatefulWidget { + static final JSInterop jsInterop = JSInterop(); + static const ROUTE = "install"; + const InstallPage({super.key}); + + @override + _InstallPageState createState() => _InstallPageState(); +} + +class _InstallPageState extends State { + bool _isAppInstalled = false; + bool _isPromptEnabled = false; + bool _installFailed = false; + String platform = ""; + final TextEditingController _urlController = TextEditingController(); + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + platform = InstallPage.jsInterop.getOSInsideWeb(); + loadSettings(); + setState(() {}); + } + + Future loadSettings() async { + bool isAppInstalled = DataService.isPwaInstalledOrNative(); + + setState(() { + _isAppInstalled = isAppInstalled; + _isPromptEnabled = true; + }); + } + + Future handleInstallButtonPress() async { + try { + PWAInstall().promptInstall_(); + } catch (e) { + setState(() { + _installFailed = true; + _urlController.text = RouterService.getCurrentUri().toString(); + _urlController.selection = TextSelection(baseOffset: 0, extentOffset: _urlController.text.length); + }); + } + await loadSettings(); + } + + bool get _canInstallPWA => !_isAppInstalled && !_installFailed && _isPromptEnabled; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Install App").tr(), + leading: BackButton( + onPressed: () => RouterService.goBackOrInitial(context), + ), + ), + body: Align( + alignment: Alignment.topCenter, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: appMaxWidth), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildInstallSection( + context, + "Install for Apple", + Icons.apple, + AppConfig.appStoreLink, + platform == "ios", + isApple: true, + ), + buildInstallSection( + context, + "Install for Android", + Icons.android, + AppConfig.playStoreLink, + platform == "android", + ), + buildInstallSection( + context, + "Install for PC/Mac", + Icons.desktop_windows, + AppConfig.desktopAppLink, + platform == "web", + ), + ], + ), + ), + ), + ), + ); + } + + Widget buildInstallSection( + BuildContext context, + String title, + IconData icon, + String link, + bool initiallyExpanded, { + bool isApple = false, + String? notice, + }) { + return ExpansionTile( + initiallyExpanded: initiallyExpanded, + title: Row( + children: [ + Icon(icon), + const SizedBox(width: 10), + Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ), + children: [ + if (notice != null) ...[ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + notice, + style: TextStyle(color: Colors.grey[800]), + ).tr(), + ), + const SizedBox(height: 10), + ], + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + children: [ + ButtonsHelper.bigButton( + label: isApple ? "Download App".tr() : "Install App".tr(), + onPressed: isApple ? () => InstallPage.jsInterop.openLinkInNewTab(link) : _canInstallPWA ? handleInstallButtonPress : null, + color: isApple || _canInstallPWA ? AppConfig.color1 : Colors.grey, + textColor: Colors.white, + ), + if (_isAppInstalled) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: const Text( + "The app is already installed.", + style: TextStyle(fontSize: 16, color: AppConfig.color1), + textAlign: TextAlign.center, + ).tr(), + ), + if (!isApple && _installFailed) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Column( + children: [ + const Text( + "Installation failed. Please try opening this link in Chrome, Edge, or a different browser.", + style: TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ).tr(), + const SizedBox(height: 8.0), + Row( + children: [ + Expanded( + child: TextFormField( + controller: _urlController, + readOnly: true, + ), + ), + IconButton( + icon: Icon(Icons.copy), + onPressed: () { + Clipboard.setData(ClipboardData(text: _urlController.text)); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Copied to clipboard')), + ); + }, + ), + ], + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 16), + ], + ); + } +} diff --git a/lib/services/js/js_stub.dart b/lib/services/js/js_stub.dart index 1f8d0840..d13461d6 100644 --- a/lib/services/js/js_stub.dart +++ b/lib/services/js/js_stub.dart @@ -15,4 +15,12 @@ class JSInterop { Future callFutureMethod(String method, List args) { throw UnsupportedError('JS interop is not supported on this platform'); } + + String getOSInsideWeb() { + throw UnsupportedError('JS interop is not supported on this platform'); + } + + void openLinkInNewTab(String link) { + throw UnsupportedError('JS interop is not supported on this platform'); + } } diff --git a/lib/services/js/js_web.dart b/lib/services/js/js_web.dart index c12352b6..417b00a6 100644 --- a/lib/services/js/js_web.dart +++ b/lib/services/js/js_web.dart @@ -1,6 +1,7 @@ // js_web.dart import 'dart:async'; import 'dart:js' as js; +import 'dart:html' as html; class JSInterop { void callMethod(String method, List args) async { @@ -34,4 +35,15 @@ class JSInterop { return completer.future; } + + String getOSInsideWeb() { + final userAgent = html.window.navigator.userAgent.toString().toLowerCase(); + if (userAgent.contains("iphone") || userAgent.contains("ipad")) return "ios"; + if (userAgent.contains("android")) return "android"; + return "web"; + } + + void openLinkInNewTab(String link) { + html.window.open(link, "_blank"); + } }