Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented remembering login data #126

Merged
merged 1 commit into from
Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions mobile/lib/constants/hive_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 3 additions & 0 deletions mobile/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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(
Expand Down
20 changes: 20 additions & 0 deletions mobile/lib/modules/login/models/hive_saved_login_info.model.dart
Original file line number Diff line number Diff line change
@@ -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 changes: 50 additions & 0 deletions mobile/lib/modules/login/models/hive_saved_login_info.model.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion mobile/lib/modules/login/providers/authentication.provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
74 changes: 59 additions & 15 deletions mobile/lib/modules/login/ui/login_form.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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(
Expand All @@ -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,
),
],
),
Expand Down Expand Up @@ -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(
Expand All @@ -136,6 +177,9 @@ class LoginButton extends ConsumerWidget {
);
}
},
child: const Text("Login"));
child: const Text(
"Login",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
));
}
}