Browse Source

Implemented remembering login data with radio button (#126)

Alex 3 years ago
parent
commit
da9eb61532

+ 4 - 4
mobile/lib/constants/hive_box.dart

@@ -3,9 +3,9 @@ const String userInfoBox = "immichBoxUserInfo"; // Box
 const String accessTokenKey = "immichBoxAccessTokenKey"; // Key 1
 const String accessTokenKey = "immichBoxAccessTokenKey"; // Key 1
 const String deviceIdKey = 'immichBoxDeviceIdKey'; // Key 2
 const String deviceIdKey = 'immichBoxDeviceIdKey'; // Key 2
 
 
-// SERVER ENDPOINT
+// Server endpoint
 const String serverEndpointKey = 'immichBoxServerEndpoint';
 const String serverEndpointKey = 'immichBoxServerEndpoint';
 
 
-// KEY
-const String hiveAllAsssetKey = "allAssets";
-const String hiveBackupProgressKey = "backupProgressAssets";
+// Login Info
+const String hiveLoginInfoBox = "immichLoginInfoBox";
+const String savedLoginInfoKey = "immichSavedLoginInfoKey";

+ 3 - 0
mobile/lib/main.dart

@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hive_flutter/hive_flutter.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/immich_colors.dart';
 import 'package:immich_mobile/constants/immich_colors.dart';
+import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/routing/tab_navigation_observer.dart';
 import 'package:immich_mobile/routing/tab_navigation_observer.dart';
@@ -15,7 +16,9 @@ import 'constants/hive_box.dart';
 
 
 void main() async {
 void main() async {
   await Hive.initFlutter();
   await Hive.initFlutter();
+  Hive.registerAdapter(HiveSavedLoginInfoAdapter());
   await Hive.openBox(userInfoBox);
   await Hive.openBox(userInfoBox);
+  await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox);
 
 
   SystemChrome.setSystemUIOverlayStyle(
   SystemChrome.setSystemUIOverlayStyle(
     const SystemUiOverlayStyle(
     const SystemUiOverlayStyle(

+ 20 - 0
mobile/lib/modules/login/models/hive_saved_login_info.model.dart

@@ -0,0 +1,20 @@
+import 'package:hive/hive.dart';
+
+part 'hive_saved_login_info.model.g.dart';
+
+@HiveType(typeId: 0)
+class HiveSavedLoginInfo {
+  @HiveField(0)
+  String email;
+
+  @HiveField(1)
+  String password;
+
+  @HiveField(2)
+  String serverUrl;
+
+  @HiveField(3)
+  bool isSaveLogin;
+
+  HiveSavedLoginInfo({required this.email, required this.password, required this.serverUrl, required this.isSaveLogin});
+}

+ 50 - 0
mobile/lib/modules/login/models/hive_saved_login_info.model.g.dart

@@ -0,0 +1,50 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'hive_saved_login_info.model.dart';
+
+// **************************************************************************
+// TypeAdapterGenerator
+// **************************************************************************
+
+class HiveSavedLoginInfoAdapter extends TypeAdapter<HiveSavedLoginInfo> {
+  @override
+  final int typeId = 0;
+
+  @override
+  HiveSavedLoginInfo read(BinaryReader reader) {
+    final numOfFields = reader.readByte();
+    final fields = <int, dynamic>{
+      for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
+    };
+    return HiveSavedLoginInfo(
+      email: fields[0] as String,
+      password: fields[1] as String,
+      serverUrl: fields[2] as String,
+      isSaveLogin: fields[3] as bool,
+    );
+  }
+
+  @override
+  void write(BinaryWriter writer, HiveSavedLoginInfo obj) {
+    writer
+      ..writeByte(4)
+      ..writeByte(0)
+      ..write(obj.email)
+      ..writeByte(1)
+      ..write(obj.password)
+      ..writeByte(2)
+      ..write(obj.serverUrl)
+      ..writeByte(3)
+      ..write(obj.isSaveLogin);
+  }
+
+  @override
+  int get hashCode => typeId.hashCode;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      other is HiveSavedLoginInfoAdapter &&
+          runtimeType == other.runtimeType &&
+          typeId == other.typeId;
+}

+ 16 - 1
mobile/lib/modules/login/providers/authentication.provider.dart

@@ -4,6 +4,7 @@ import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
+import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
 import 'package:immich_mobile/modules/login/models/login_response.model.dart';
 import 'package:immich_mobile/modules/login/models/login_response.model.dart';
 import 'package:immich_mobile/shared/services/backup.service.dart';
 import 'package:immich_mobile/shared/services/backup.service.dart';
 import 'package:immich_mobile/shared/services/device_info.service.dart';
 import 'package:immich_mobile/shared/services/device_info.service.dart';
@@ -36,7 +37,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
   final BackupService _backupService = BackupService();
   final BackupService _backupService = BackupService();
   final NetworkService _networkService = NetworkService();
   final NetworkService _networkService = NetworkService();
 
 
-  Future<bool> login(String email, String password, String serverEndpoint) async {
+  Future<bool> login(String email, String password, String serverEndpoint, bool isSavedLoginInfo) async {
     // Store server endpoint to Hive and test endpoint
     // Store server endpoint to Hive and test endpoint
     if (serverEndpoint[serverEndpoint.length - 1] == "/") {
     if (serverEndpoint[serverEndpoint.length - 1] == "/") {
       var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1);
       var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1);
@@ -76,6 +77,20 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
         userId: payload.userId,
         userId: payload.userId,
         userEmail: payload.userEmail,
         userEmail: payload.userEmail,
       );
       );
+
+      if (isSavedLoginInfo) {
+        // Save login info to local storage
+        Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).put(
+          savedLoginInfoKey,
+          HiveSavedLoginInfo(
+              email: email,
+              password: password,
+              isSaveLogin: true,
+              serverUrl: Hive.box(userInfoBox).get(serverEndpointKey)),
+        );
+      } else {
+        Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).delete(savedLoginInfoKey);
+      }
     } catch (e) {
     } catch (e) {
       return false;
       return false;
     }
     }

+ 59 - 15
mobile/lib/modules/login/ui/login_form.dart

@@ -1,7 +1,10 @@
 import 'package:auto_route/auto_route.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hive/hive.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/constants/hive_box.dart';
+import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/shared/providers/asset.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 import 'package:immich_mobile/shared/providers/backup.provider.dart';
 import 'package:immich_mobile/shared/providers/backup.provider.dart';
@@ -12,22 +15,36 @@ class LoginForm extends HookConsumerWidget {
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
-    final usernameController = useTextEditingController(text: 'testuser@email.com');
-    final passwordController = useTextEditingController(text: 'password');
-    final serverEndpointController = useTextEditingController(text: 'http://192.168.1.216:2283');
+    final usernameController = useTextEditingController.fromValue(TextEditingValue.empty);
+    final passwordController = useTextEditingController.fromValue(TextEditingValue.empty);
+    final serverEndpointController = useTextEditingController(text: 'http://your-server-ip:2283');
+    final isSaveLoginInfo = useState<bool>(false);
+
+    useEffect(() {
+      var loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox).get(savedLoginInfoKey);
+
+      if (loginInfo != null) {
+        usernameController.text = loginInfo.email;
+        passwordController.text = loginInfo.password;
+        serverEndpointController.text = loginInfo.serverUrl;
+        isSaveLoginInfo.value = loginInfo.isSaveLogin;
+      }
+
+      return null;
+    }, []);
 
 
     return Center(
     return Center(
       child: ConstrainedBox(
       child: ConstrainedBox(
         constraints: const BoxConstraints(maxWidth: 300),
         constraints: const BoxConstraints(maxWidth: 300),
         child: SingleChildScrollView(
         child: SingleChildScrollView(
           child: Wrap(
           child: Wrap(
-            spacing: 32,
-            runSpacing: 32,
+            spacing: 16,
+            runSpacing: 16,
             alignment: WrapAlignment.center,
             alignment: WrapAlignment.center,
             children: [
             children: [
               const Image(
               const Image(
                 image: AssetImage('assets/immich-logo-no-outline.png'),
                 image: AssetImage('assets/immich-logo-no-outline.png'),
-                width: 128,
+                width: 100,
                 filterQuality: FilterQuality.high,
                 filterQuality: FilterQuality.high,
               ),
               ),
               Text(
               Text(
@@ -42,10 +59,29 @@ class LoginForm extends HookConsumerWidget {
               EmailInput(controller: usernameController),
               EmailInput(controller: usernameController),
               PasswordInput(controller: passwordController),
               PasswordInput(controller: passwordController),
               ServerEndpointInput(controller: serverEndpointController),
               ServerEndpointInput(controller: serverEndpointController),
+              CheckboxListTile(
+                activeColor: Theme.of(context).primaryColor,
+                contentPadding: const EdgeInsets.symmetric(horizontal: 8),
+                dense: true,
+                side: const BorderSide(color: Colors.grey, width: 1.5),
+                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
+                enableFeedback: true,
+                title: const Text(
+                  "Save login",
+                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey),
+                ),
+                value: isSaveLoginInfo.value,
+                onChanged: (switchValue) {
+                  if (switchValue != null) {
+                    isSaveLoginInfo.value = switchValue;
+                  }
+                },
+              ),
               LoginButton(
               LoginButton(
                 emailController: usernameController,
                 emailController: usernameController,
                 passwordController: passwordController,
                 passwordController: passwordController,
                 serverEndpointController: serverEndpointController,
                 serverEndpointController: serverEndpointController,
+                isSavedLoginInfo: isSaveLoginInfo.value,
               ),
               ),
             ],
             ],
           ),
           ),
@@ -104,29 +140,34 @@ class LoginButton extends ConsumerWidget {
   final TextEditingController emailController;
   final TextEditingController emailController;
   final TextEditingController passwordController;
   final TextEditingController passwordController;
   final TextEditingController serverEndpointController;
   final TextEditingController serverEndpointController;
+  final bool isSavedLoginInfo;
 
 
-  const LoginButton(
-      {Key? key,
-      required this.emailController,
-      required this.passwordController,
-      required this.serverEndpointController})
-      : super(key: key);
+  const LoginButton({
+    Key? key,
+    required this.emailController,
+    required this.passwordController,
+    required this.serverEndpointController,
+    required this.isSavedLoginInfo,
+  }) : super(key: key);
 
 
   @override
   @override
   Widget build(BuildContext context, WidgetRef ref) {
   Widget build(BuildContext context, WidgetRef ref) {
     return ElevatedButton(
     return ElevatedButton(
+        style: ButtonStyle(
+          visualDensity: VisualDensity.standard,
+          padding: MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.symmetric(vertical: 10, horizontal: 25)),
+        ),
         onPressed: () async {
         onPressed: () async {
           // This will remove current cache asset state of previous user login.
           // This will remove current cache asset state of previous user login.
           ref.watch(assetProvider.notifier).clearAllAsset();
           ref.watch(assetProvider.notifier).clearAllAsset();
 
 
           var isAuthenticated = await ref
           var isAuthenticated = await ref
               .read(authenticationProvider.notifier)
               .read(authenticationProvider.notifier)
-              .login(emailController.text, passwordController.text, serverEndpointController.text);
+              .login(emailController.text, passwordController.text, serverEndpointController.text, isSavedLoginInfo);
 
 
           if (isAuthenticated) {
           if (isAuthenticated) {
             // Resume backup (if enable) then navigate
             // Resume backup (if enable) then navigate
             ref.watch(backupProvider.notifier).resumeBackup();
             ref.watch(backupProvider.notifier).resumeBackup();
-            // AutoRouter.of(context).pushNamed("/home-page");
             AutoRouter.of(context).pushNamed("/tab-controller-page");
             AutoRouter.of(context).pushNamed("/tab-controller-page");
           } else {
           } else {
             ImmichToast.show(
             ImmichToast.show(
@@ -136,6 +177,9 @@ class LoginButton extends ConsumerWidget {
             );
             );
           }
           }
         },
         },
-        child: const Text("Login"));
+        child: const Text(
+          "Login",
+          style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+        ));
   }
   }
 }
 }