浏览代码

Update landing page to provide an option to update the app's endpoint

vishnukvmd 1 年之前
父节点
当前提交
ee33a3229f
共有 2 个文件被更改,包括 133 次插入3 次删除
  1. 42 3
      mobile/lib/ui/home/landing_page_widget.dart
  2. 91 0
      mobile/lib/ui/settings/developer_settings_page.dart

+ 42 - 3
mobile/lib/ui/home/landing_page_widget.dart

@@ -19,7 +19,10 @@ import 'package:photos/ui/components/buttons/button_widget.dart';
 import 'package:photos/ui/components/dialog_widget.dart';
 import 'package:photos/ui/components/models/button_type.dart';
 import 'package:photos/ui/payment/subscription.dart';
+import "package:photos/ui/settings/developer_settings_page.dart";
+import "package:photos/ui/settings/developer_settings_widget.dart";
 import "package:photos/ui/settings/language_picker.dart";
+import "package:photos/utils/dialog_util.dart";
 import "package:photos/utils/navigation_util.dart";
 
 class LandingPageWidget extends StatefulWidget {
@@ -30,7 +33,10 @@ class LandingPageWidget extends StatefulWidget {
 }
 
 class _LandingPageWidgetState extends State<LandingPageWidget> {
+  static const kDeveloperModeTapCountThreshold = 7;
+
   double _featureIndex = 0;
+  int _developerModeTapCount = 0;
 
   @override
   void initState() {
@@ -40,7 +46,35 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
 
   @override
   Widget build(BuildContext context) {
-    return Scaffold(body: _getBody(), resizeToAvoidBottomInset: false);
+    return Scaffold(
+      body: GestureDetector(
+        onTap: () async {
+          _developerModeTapCount++;
+          if (_developerModeTapCount >= kDeveloperModeTapCountThreshold) {
+            _developerModeTapCount = 0;
+            final result = await showChoiceDialog(
+              context,
+              title: S.of(context).developerSettings,
+              firstButtonLabel: S.of(context).yes,
+              body: S.of(context).developerSettingsWarning,
+              isDismissible: false,
+            );
+            if (result?.action == ButtonAction.first) {
+              await Navigator.of(context).push(
+                MaterialPageRoute(
+                  builder: (BuildContext context) {
+                    return const DeveloperSettingsPage();
+                  },
+                ),
+              );
+              setState(() {});
+            }
+          }
+        },
+        child: _getBody(),
+      ),
+      resizeToAvoidBottomInset: false,
+    );
   }
 
   Widget _getBody() {
@@ -131,6 +165,7 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
                 ),
               ),
             ),
+            const DeveloperSettingsWidget(),
             const Padding(
               padding: EdgeInsets.all(20),
             ),
@@ -195,7 +230,9 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
       // No key
       if (Configuration.instance.getKeyAttributes() == null) {
         // Never had a key
-        page =  const PasswordEntryPage(mode: PasswordEntryMode.set,);
+        page = const PasswordEntryPage(
+          mode: PasswordEntryMode.set,
+        );
       } else if (Configuration.instance.getKey() == null) {
         // Yet to decrypt the key
         page = const PasswordReentryPage();
@@ -223,7 +260,9 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
       // No key
       if (Configuration.instance.getKeyAttributes() == null) {
         // Never had a key
-        page =  const PasswordEntryPage(mode: PasswordEntryMode.set,);
+        page = const PasswordEntryPage(
+          mode: PasswordEntryMode.set,
+        );
       } else if (Configuration.instance.getKey() == null) {
         // Yet to decrypt the key
         page = const PasswordReentryPage();

+ 91 - 0
mobile/lib/ui/settings/developer_settings_page.dart

@@ -0,0 +1,91 @@
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+import "package:photos/core/configuration.dart";
+import "package:photos/core/network/network.dart";
+import "package:photos/generated/l10n.dart";
+import "package:photos/ui/common/gradient_button.dart";
+import "package:photos/utils/dialog_util.dart";
+import "package:photos/utils/toast_util.dart";
+
+class DeveloperSettingsPage extends StatefulWidget {
+  const DeveloperSettingsPage({super.key});
+
+  @override
+  State<DeveloperSettingsPage> createState() => _DeveloperSettingsPageState();
+}
+
+class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
+  final _logger = Logger('DeveloperSettingsPage');
+  final _urlController = TextEditingController();
+
+  @override
+  void dispose() {
+    _urlController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    _logger.info(
+      "Current endpoint is: ${Configuration.instance.getHttpEndpoint()}",
+    );
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(S.of(context).developerSettings),
+      ),
+      body: Padding(
+        padding: const EdgeInsets.all(16.0),
+        child: Column(
+          children: [
+            TextField(
+              controller: _urlController,
+              decoration: InputDecoration(
+                labelText: S.of(context).serverEndpoint,
+                hintText: Configuration.instance.getHttpEndpoint(),
+              ),
+              autofocus: true,
+            ),
+            const SizedBox(height: 40),
+            GradientButton(
+              onTap: () async {
+                final url = _urlController.text;
+                _logger.info("Entered endpoint: $url");
+                try {
+                  final uri = Uri.parse(url);
+                  if ((uri.scheme == "http" || uri.scheme == "https")) {
+                    await _ping(url);
+                    await Configuration.instance.setHttpEndpoint(url);
+                    showToast(context, S.of(context).endpointUpdatedMessage);
+                    Navigator.of(context).pop();
+                  } else {
+                    throw const FormatException();
+                  }
+                } catch (e) {
+                  // ignore: unawaited_futures
+                  showErrorDialog(
+                    context,
+                    S.of(context).invalidEndpoint,
+                    S.of(context).invalidEndpointMessage,
+                  );
+                }
+              },
+              text: S.of(context).save,
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Future<void> _ping(String endpoint) async {
+    try {
+      final response =
+          await NetworkClient.instance.getDio().get('$endpoint/ping');
+      if (response.data['message'] != 'pong') {
+        throw Exception('Invalid response');
+      }
+    } catch (e) {
+      throw Exception('Error occurred: $e');
+    }
+  }
+}