forked from Cutlery/immich
		
	Implemented remembering login data with radio button (#126)
This commit is contained in:
		
							parent
							
								
									c1ccf026f0
								
							
						
					
					
						commit
						da9eb61532
					
				| @ -3,9 +3,9 @@ const String userInfoBox = "immichBoxUserInfo"; // Box | ||||
| const String accessTokenKey = "immichBoxAccessTokenKey"; // Key 1 | ||||
| const String deviceIdKey = 'immichBoxDeviceIdKey'; // Key 2 | ||||
| 
 | ||||
| // SERVER ENDPOINT | ||||
| // Server endpoint | ||||
| const String serverEndpointKey = 'immichBoxServerEndpoint'; | ||||
| 
 | ||||
| // KEY | ||||
| const String hiveAllAsssetKey = "allAssets"; | ||||
| const String hiveBackupProgressKey = "backupProgressAssets"; | ||||
| // Login Info | ||||
| const String hiveLoginInfoBox = "immichLoginInfoBox"; | ||||
| const String savedLoginInfoKey = "immichSavedLoginInfoKey"; | ||||
|  | ||||
| @ -3,6 +3,7 @@ import 'package:flutter/services.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.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/routing/router.dart'; | ||||
| import 'package:immich_mobile/routing/tab_navigation_observer.dart'; | ||||
| @ -15,7 +16,9 @@ import 'constants/hive_box.dart'; | ||||
| 
 | ||||
| void main() async { | ||||
|   await Hive.initFlutter(); | ||||
|   Hive.registerAdapter(HiveSavedLoginInfoAdapter()); | ||||
|   await Hive.openBox(userInfoBox); | ||||
|   await Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox); | ||||
| 
 | ||||
|   SystemChrome.setSystemUIOverlayStyle( | ||||
|     const SystemUiOverlayStyle( | ||||
|  | ||||
| @ -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}); | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -4,6 +4,7 @@ import 'package:hive/hive.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.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/hive_saved_login_info.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/device_info.service.dart'; | ||||
| @ -36,7 +37,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> { | ||||
|   final BackupService _backupService = BackupService(); | ||||
|   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 | ||||
|     if (serverEndpoint[serverEndpoint.length - 1] == "/") { | ||||
|       var validUrl = serverEndpoint.substring(0, serverEndpoint.length - 1); | ||||
| @ -76,6 +77,20 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> { | ||||
|         userId: payload.userId, | ||||
|         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) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
| @ -1,7 +1,10 @@ | ||||
| import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:hive/hive.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/modules/login/providers/authentication.provider.dart'; | ||||
| import 'package:immich_mobile/shared/providers/backup.provider.dart'; | ||||
| @ -12,22 +15,36 @@ class LoginForm extends HookConsumerWidget { | ||||
| 
 | ||||
|   @override | ||||
|   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( | ||||
|       child: ConstrainedBox( | ||||
|         constraints: const BoxConstraints(maxWidth: 300), | ||||
|         child: SingleChildScrollView( | ||||
|           child: Wrap( | ||||
|             spacing: 32, | ||||
|             runSpacing: 32, | ||||
|             spacing: 16, | ||||
|             runSpacing: 16, | ||||
|             alignment: WrapAlignment.center, | ||||
|             children: [ | ||||
|               const Image( | ||||
|                 image: AssetImage('assets/immich-logo-no-outline.png'), | ||||
|                 width: 128, | ||||
|                 width: 100, | ||||
|                 filterQuality: FilterQuality.high, | ||||
|               ), | ||||
|               Text( | ||||
| @ -42,10 +59,29 @@ class LoginForm extends HookConsumerWidget { | ||||
|               EmailInput(controller: usernameController), | ||||
|               PasswordInput(controller: passwordController), | ||||
|               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( | ||||
|                 emailController: usernameController, | ||||
|                 passwordController: passwordController, | ||||
|                 serverEndpointController: serverEndpointController, | ||||
|                 isSavedLoginInfo: isSaveLoginInfo.value, | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
| @ -104,29 +140,34 @@ class LoginButton extends ConsumerWidget { | ||||
|   final TextEditingController emailController; | ||||
|   final TextEditingController passwordController; | ||||
|   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 | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     return ElevatedButton( | ||||
|         style: ButtonStyle( | ||||
|           visualDensity: VisualDensity.standard, | ||||
|           padding: MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.symmetric(vertical: 10, horizontal: 25)), | ||||
|         ), | ||||
|         onPressed: () async { | ||||
|           // This will remove current cache asset state of previous user login. | ||||
|           ref.watch(assetProvider.notifier).clearAllAsset(); | ||||
| 
 | ||||
|           var isAuthenticated = await ref | ||||
|               .read(authenticationProvider.notifier) | ||||
|               .login(emailController.text, passwordController.text, serverEndpointController.text); | ||||
|               .login(emailController.text, passwordController.text, serverEndpointController.text, isSavedLoginInfo); | ||||
| 
 | ||||
|           if (isAuthenticated) { | ||||
|             // Resume backup (if enable) then navigate | ||||
|             ref.watch(backupProvider.notifier).resumeBackup(); | ||||
|             // AutoRouter.of(context).pushNamed("/home-page"); | ||||
|             AutoRouter.of(context).pushNamed("/tab-controller-page"); | ||||
|           } else { | ||||
|             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), | ||||
|         )); | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user