|
@@ -32,48 +32,78 @@ class LoginForm extends HookConsumerWidget {
|
|
|
final serverEndpointController =
|
|
|
useTextEditingController.fromValue(TextEditingValue.empty);
|
|
|
final apiService = ref.watch(apiServiceProvider);
|
|
|
+ final emailFocusNode = useFocusNode();
|
|
|
+ final passwordFocusNode = useFocusNode();
|
|
|
final serverEndpointFocusNode = useFocusNode();
|
|
|
final isLoading = useState<bool>(false);
|
|
|
+ final isLoadingServer = useState<bool>(false);
|
|
|
final isOauthEnable = useState<bool>(false);
|
|
|
final oAuthButtonLabel = useState<String>('OAuth');
|
|
|
final logoAnimationController = useAnimationController(
|
|
|
duration: const Duration(seconds: 60),
|
|
|
)..repeat();
|
|
|
|
|
|
- getServeLoginConfig() async {
|
|
|
- if (!serverEndpointFocusNode.hasFocus) {
|
|
|
- var serverUrl = serverEndpointController.text.trim();
|
|
|
+ final ValueNotifier<String?> serverEndpoint = useState<String?>(null);
|
|
|
|
|
|
- try {
|
|
|
- if (serverUrl.isNotEmpty) {
|
|
|
- isLoading.value = true;
|
|
|
- final serverEndpoint =
|
|
|
- await apiService.resolveAndSetEndpoint(serverUrl.toString());
|
|
|
+ /// Fetch the server login credential and enables oAuth login if necessary
|
|
|
+ /// Returns true if successful, false otherwise
|
|
|
+ Future<bool> getServerLoginCredential() async {
|
|
|
+ final serverUrl = serverEndpointController.text.trim();
|
|
|
|
|
|
- var loginConfig = await apiService.oAuthApi.generateConfig(
|
|
|
- OAuthConfigDto(redirectUri: serverEndpoint),
|
|
|
- );
|
|
|
+ // Guard empty URL
|
|
|
+ if (serverUrl.isEmpty) {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: "login_form_server_empty".tr(),
|
|
|
+ toastType: ToastType.error,
|
|
|
+ );
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- if (loginConfig != null) {
|
|
|
- isOauthEnable.value = loginConfig.enabled;
|
|
|
- oAuthButtonLabel.value = loginConfig.buttonText ?? 'OAuth';
|
|
|
- } else {
|
|
|
- isOauthEnable.value = false;
|
|
|
- }
|
|
|
+ try {
|
|
|
+ isLoadingServer.value = true;
|
|
|
+ final endpoint =
|
|
|
+ await apiService.resolveAndSetEndpoint(serverUrl);
|
|
|
|
|
|
- isLoading.value = false;
|
|
|
- }
|
|
|
- } catch (_) {
|
|
|
- isLoading.value = false;
|
|
|
+ final loginConfig = await apiService.oAuthApi.generateConfig(
|
|
|
+ OAuthConfigDto(redirectUri: serverUrl),
|
|
|
+ );
|
|
|
+
|
|
|
+ if (loginConfig != null) {
|
|
|
+ isOauthEnable.value = loginConfig.enabled;
|
|
|
+ oAuthButtonLabel.value = loginConfig.buttonText ?? 'OAuth';
|
|
|
+ } else {
|
|
|
isOauthEnable.value = false;
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ serverEndpoint.value = endpoint;
|
|
|
+ } on ApiException catch (e) {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: e.message ?? 'login_form_api_exception'.tr(),
|
|
|
+ toastType: ToastType.error,
|
|
|
+ );
|
|
|
+ isOauthEnable.value = false;
|
|
|
+ isLoadingServer.value = false;
|
|
|
+ return false;
|
|
|
+ } catch (e) {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: 'login_form_server_error'.tr(),
|
|
|
+ toastType: ToastType.error,
|
|
|
+ );
|
|
|
+ isOauthEnable.value = false;
|
|
|
+ isLoadingServer.value = false;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ isLoadingServer.value = false;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
useEffect(
|
|
|
() {
|
|
|
- serverEndpointFocusNode.addListener(getServeLoginConfig);
|
|
|
-
|
|
|
var loginInfo = Hive.box<HiveSavedLoginInfo>(hiveLoginInfoBox)
|
|
|
.get(savedLoginInfoKey);
|
|
|
|
|
@@ -83,7 +113,6 @@ class LoginForm extends HookConsumerWidget {
|
|
|
serverEndpointController.text = loginInfo.serverUrl;
|
|
|
}
|
|
|
|
|
|
- getServeLoginConfig();
|
|
|
return null;
|
|
|
},
|
|
|
[],
|
|
@@ -95,86 +124,260 @@ class LoginForm extends HookConsumerWidget {
|
|
|
serverEndpointController.text = 'http://10.1.15.216:2283/api';
|
|
|
}
|
|
|
|
|
|
- return Center(
|
|
|
- child: ConstrainedBox(
|
|
|
+ login() async {
|
|
|
+ // Start loading
|
|
|
+ isLoading.value = true;
|
|
|
+
|
|
|
+ // This will remove current cache asset state of previous user login.
|
|
|
+ ref.read(assetProvider.notifier).clearAllAsset();
|
|
|
+
|
|
|
+ try {
|
|
|
+ final isAuthenticated =
|
|
|
+ await ref.read(authenticationProvider.notifier).login(
|
|
|
+ usernameController.text,
|
|
|
+ passwordController.text,
|
|
|
+ serverEndpointController.text.trim(),
|
|
|
+ );
|
|
|
+ if (isAuthenticated) {
|
|
|
+ // Resume backup (if enable) then navigate
|
|
|
+ if (ref.read(authenticationProvider).shouldChangePassword &&
|
|
|
+ !ref.read(authenticationProvider).isAdmin) {
|
|
|
+ AutoRouter.of(context).push(const ChangePasswordRoute());
|
|
|
+ } else {
|
|
|
+ final hasPermission = await ref
|
|
|
+ .read(galleryPermissionNotifier.notifier)
|
|
|
+ .hasPermission;
|
|
|
+ if (hasPermission) {
|
|
|
+ // Don't resume the backup until we have gallery permission
|
|
|
+ ref.read(backupProvider.notifier).resumeBackup();
|
|
|
+ }
|
|
|
+ AutoRouter.of(context).replace(const TabControllerRoute());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: "login_form_failed_login".tr(),
|
|
|
+ toastType: ToastType.error,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ // Make sure we stop loading
|
|
|
+ isLoading.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ oAuthLogin() async {
|
|
|
+ var oAuthService = ref.watch(oAuthServiceProvider);
|
|
|
+ ref.watch(assetProvider.notifier).clearAllAsset();
|
|
|
+ OAuthConfigResponseDto? oAuthServerConfig;
|
|
|
+
|
|
|
+ try {
|
|
|
+ oAuthServerConfig = await oAuthService
|
|
|
+ .getOAuthServerConfig(serverEndpointController.text);
|
|
|
+
|
|
|
+ isLoading.value = true;
|
|
|
+ } catch (e) {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: "login_form_failed_get_oauth_server_config".tr(),
|
|
|
+ toastType: ToastType.error,
|
|
|
+ );
|
|
|
+ isLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (oAuthServerConfig != null && oAuthServerConfig.enabled) {
|
|
|
+ var loginResponseDto =
|
|
|
+ await oAuthService.oAuthLogin(oAuthServerConfig.url!);
|
|
|
+
|
|
|
+ if (loginResponseDto != null) {
|
|
|
+ var isSuccess = await ref
|
|
|
+ .watch(authenticationProvider.notifier)
|
|
|
+ .setSuccessLoginInfo(
|
|
|
+ accessToken: loginResponseDto.accessToken,
|
|
|
+ serverUrl: serverEndpointController.text,
|
|
|
+ );
|
|
|
+
|
|
|
+ if (isSuccess) {
|
|
|
+ isLoading.value = false;
|
|
|
+ final permission = ref.watch(galleryPermissionNotifier);
|
|
|
+ if (permission.isGranted || permission.isLimited) {
|
|
|
+ ref.watch(backupProvider.notifier).resumeBackup();
|
|
|
+ }
|
|
|
+ AutoRouter.of(context).replace(
|
|
|
+ const TabControllerRoute(),
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: "login_form_failed_login".tr(),
|
|
|
+ toastType: ToastType.error,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ isLoading.value = false;
|
|
|
+ } else {
|
|
|
+ ImmichToast.show(
|
|
|
+ context: context,
|
|
|
+ msg: "login_form_failed_get_oauth_server_disable".tr(),
|
|
|
+ toastType: ToastType.info,
|
|
|
+ );
|
|
|
+ isLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ buildSelectServer() {
|
|
|
+ return ConstrainedBox(
|
|
|
+ key: const ValueKey('server'),
|
|
|
constraints: const BoxConstraints(maxWidth: 300),
|
|
|
- child: SingleChildScrollView(
|
|
|
- child: AutofillGroup(
|
|
|
- child: Wrap(
|
|
|
- spacing: 16,
|
|
|
- runSpacing: 16,
|
|
|
- alignment: WrapAlignment.center,
|
|
|
- children: [
|
|
|
- GestureDetector(
|
|
|
- onDoubleTap: () => populateTestLoginInfo(),
|
|
|
- child: RotationTransition(
|
|
|
- turns: logoAnimationController,
|
|
|
- child: const ImmichLogo(
|
|
|
- heroTag: 'logo',
|
|
|
- ),
|
|
|
- ),
|
|
|
- ),
|
|
|
- const ImmichTitleText(),
|
|
|
- EmailInput(controller: usernameController),
|
|
|
- PasswordInput(controller: passwordController),
|
|
|
- ServerEndpointInput(
|
|
|
- controller: serverEndpointController,
|
|
|
- focusNode: serverEndpointFocusNode,
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
+ children: [
|
|
|
+ ServerEndpointInput(
|
|
|
+ controller: serverEndpointController,
|
|
|
+ focusNode: serverEndpointFocusNode,
|
|
|
+ onSubmit: getServerLoginCredential,
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 18),
|
|
|
+ ElevatedButton.icon(
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ padding: const EdgeInsets.symmetric(vertical: 12),
|
|
|
+ ),
|
|
|
+ onPressed: isLoadingServer.value ? null : getServerLoginCredential,
|
|
|
+ icon: const Icon(Icons.arrow_forward_rounded),
|
|
|
+ label: const Text(
|
|
|
+ 'Next',
|
|
|
+ style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
|
|
+ ).tr(),
|
|
|
+ ),
|
|
|
+ if (isLoadingServer.value)
|
|
|
+ const Padding(
|
|
|
+ padding: EdgeInsets.only(top: 18.0),
|
|
|
+ child: Center(
|
|
|
+ child: CircularProgressIndicator(),
|
|
|
),
|
|
|
- if (isLoading.value)
|
|
|
- const SizedBox(
|
|
|
- width: 24,
|
|
|
- height: 24,
|
|
|
- child: CircularProgressIndicator(
|
|
|
- strokeWidth: 2,
|
|
|
- ),
|
|
|
- ),
|
|
|
- if (!isLoading.value)
|
|
|
- Column(
|
|
|
- crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
- mainAxisAlignment: MainAxisAlignment.center,
|
|
|
- children: [
|
|
|
- const SizedBox(height: 18),
|
|
|
- LoginButton(
|
|
|
- emailController: usernameController,
|
|
|
- passwordController: passwordController,
|
|
|
- serverEndpointController: serverEndpointController,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ buildLogin() {
|
|
|
+ return ConstrainedBox(
|
|
|
+ key: const ValueKey('login'),
|
|
|
+ constraints: const BoxConstraints(maxWidth: 300),
|
|
|
+ child: AutofillGroup(
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ serverEndpointController.text,
|
|
|
+ style: Theme.of(context).textTheme.displaySmall,
|
|
|
+ textAlign: TextAlign.center,
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 18),
|
|
|
+ EmailInput(
|
|
|
+ controller: usernameController,
|
|
|
+ focusNode: emailFocusNode,
|
|
|
+ onSubmit: passwordFocusNode.requestFocus,
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ PasswordInput(
|
|
|
+ controller: passwordController,
|
|
|
+ focusNode: passwordFocusNode,
|
|
|
+ onSubmit: login,
|
|
|
+ ),
|
|
|
+ AnimatedSwitcher(
|
|
|
+ duration: const Duration(milliseconds: 500),
|
|
|
+ child: isLoading.value
|
|
|
+ ? const SizedBox(
|
|
|
+ width: 24,
|
|
|
+ height: 24,
|
|
|
+ child: CircularProgressIndicator(
|
|
|
+ strokeWidth: 2,
|
|
|
),
|
|
|
- if (isOauthEnable.value) ...[
|
|
|
- Padding(
|
|
|
- padding: const EdgeInsets.symmetric(
|
|
|
- horizontal: 16.0,
|
|
|
+ )
|
|
|
+ : Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ const SizedBox(height: 18),
|
|
|
+ LoginButton(onPressed: login),
|
|
|
+ if (isOauthEnable.value) ...[
|
|
|
+ Padding(
|
|
|
+ padding: const EdgeInsets.symmetric(
|
|
|
+ horizontal: 16.0,
|
|
|
+ ),
|
|
|
+ child: Divider(
|
|
|
+ color:
|
|
|
+ Brightness.dark == Theme.of(context).brightness
|
|
|
+ ? Colors.white
|
|
|
+ : Colors.black,
|
|
|
+ ),
|
|
|
),
|
|
|
- child: Divider(
|
|
|
- color:
|
|
|
- Brightness.dark == Theme.of(context).brightness
|
|
|
- ? Colors.white
|
|
|
- : Colors.black,
|
|
|
+ OAuthLoginButton(
|
|
|
+ serverEndpointController: serverEndpointController,
|
|
|
+ buttonLabel: oAuthButtonLabel.value,
|
|
|
+ isLoading: isLoading,
|
|
|
+ onPressed: oAuthLogin,
|
|
|
),
|
|
|
- ),
|
|
|
- OAuthLoginButton(
|
|
|
- serverEndpointController: serverEndpointController,
|
|
|
- buttonLabel: oAuthButtonLabel.value,
|
|
|
- isLoading: isLoading,
|
|
|
- onLoginSuccess: () {
|
|
|
- isLoading.value = false;
|
|
|
- final permission = ref.watch(galleryPermissionNotifier);
|
|
|
- if (permission.isGranted || permission.isLimited) {
|
|
|
- ref.watch(backupProvider.notifier).resumeBackup();
|
|
|
- }
|
|
|
- AutoRouter.of(context).replace(
|
|
|
- const TabControllerRoute(),
|
|
|
- );
|
|
|
- },
|
|
|
- ),
|
|
|
+ ],
|
|
|
],
|
|
|
- ],
|
|
|
- )
|
|
|
- ],
|
|
|
- ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 12),
|
|
|
+ TextButton.icon(
|
|
|
+ icon: const Icon(Icons.arrow_back),
|
|
|
+ onPressed: () => serverEndpoint.value = null,
|
|
|
+ label: const Text('Back'),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
),
|
|
|
),
|
|
|
- ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ final child = serverEndpoint.value == null
|
|
|
+ ? buildSelectServer()
|
|
|
+ : buildLogin();
|
|
|
+
|
|
|
+ return LayoutBuilder(
|
|
|
+ builder: (context, constraints) {
|
|
|
+ return SingleChildScrollView(
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ SizedBox(
|
|
|
+ height: constraints.maxHeight / 5,
|
|
|
+ ),
|
|
|
+ Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.end,
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onDoubleTap: () => populateTestLoginInfo(),
|
|
|
+ child: RotationTransition(
|
|
|
+ turns: logoAnimationController,
|
|
|
+ child: const ImmichLogo(
|
|
|
+ heroTag: 'logo',
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const ImmichTitleText(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 18),
|
|
|
+ AnimatedSwitcher(
|
|
|
+ duration: const Duration(milliseconds: 500),
|
|
|
+ child: child,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
);
|
|
|
}
|
|
|
}
|
|
@@ -182,10 +385,13 @@ class LoginForm extends HookConsumerWidget {
|
|
|
class ServerEndpointInput extends StatelessWidget {
|
|
|
final TextEditingController controller;
|
|
|
final FocusNode focusNode;
|
|
|
+ final Function()? onSubmit;
|
|
|
+
|
|
|
const ServerEndpointInput({
|
|
|
Key? key,
|
|
|
required this.controller,
|
|
|
required this.focusNode,
|
|
|
+ this.onSubmit,
|
|
|
}) : super(key: key);
|
|
|
|
|
|
String? _validateInput(String? url) {
|
|
@@ -218,14 +424,23 @@ class ServerEndpointInput extends StatelessWidget {
|
|
|
autofillHints: const [AutofillHints.url],
|
|
|
keyboardType: TextInputType.url,
|
|
|
autocorrect: false,
|
|
|
+ onFieldSubmitted: (_) => onSubmit?.call(),
|
|
|
+ textInputAction: TextInputAction.go,
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class EmailInput extends StatelessWidget {
|
|
|
final TextEditingController controller;
|
|
|
+ final FocusNode? focusNode;
|
|
|
+ final Function()? onSubmit;
|
|
|
|
|
|
- const EmailInput({Key? key, required this.controller}) : super(key: key);
|
|
|
+ const EmailInput({
|
|
|
+ Key? key,
|
|
|
+ required this.controller,
|
|
|
+ this.focusNode,
|
|
|
+ this.onSubmit,
|
|
|
+ }) : super(key: key);
|
|
|
|
|
|
String? _validateInput(String? email) {
|
|
|
if (email == null || email == '') return null;
|
|
@@ -240,6 +455,7 @@ class EmailInput extends StatelessWidget {
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
return TextFormField(
|
|
|
+ autofocus: true,
|
|
|
controller: controller,
|
|
|
decoration: InputDecoration(
|
|
|
labelText: 'login_form_label_email'.tr(),
|
|
@@ -250,14 +466,24 @@ class EmailInput extends StatelessWidget {
|
|
|
autovalidateMode: AutovalidateMode.always,
|
|
|
autofillHints: const [AutofillHints.email],
|
|
|
keyboardType: TextInputType.emailAddress,
|
|
|
+ onFieldSubmitted: (_) => onSubmit?.call(),
|
|
|
+ focusNode: focusNode,
|
|
|
+ textInputAction: TextInputAction.next,
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class PasswordInput extends StatelessWidget {
|
|
|
final TextEditingController controller;
|
|
|
+ final FocusNode? focusNode;
|
|
|
+ final Function()? onSubmit;
|
|
|
|
|
|
- const PasswordInput({Key? key, required this.controller}) : super(key: key);
|
|
|
+ const PasswordInput({
|
|
|
+ Key? key,
|
|
|
+ required this.controller,
|
|
|
+ this.focusNode,
|
|
|
+ this.onSubmit,
|
|
|
+ }) : super(key: key);
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
@@ -271,20 +497,19 @@ class PasswordInput extends StatelessWidget {
|
|
|
),
|
|
|
autofillHints: const [AutofillHints.password],
|
|
|
keyboardType: TextInputType.text,
|
|
|
+ onFieldSubmitted: (_) => onSubmit?.call(),
|
|
|
+ focusNode: focusNode,
|
|
|
+ textInputAction: TextInputAction.go,
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class LoginButton extends ConsumerWidget {
|
|
|
- final TextEditingController emailController;
|
|
|
- final TextEditingController passwordController;
|
|
|
- final TextEditingController serverEndpointController;
|
|
|
+ final Function() onPressed;
|
|
|
|
|
|
const LoginButton({
|
|
|
Key? key,
|
|
|
- required this.emailController,
|
|
|
- required this.passwordController,
|
|
|
- required this.serverEndpointController,
|
|
|
+ required this.onPressed,
|
|
|
}) : super(key: key);
|
|
|
|
|
|
@override
|
|
@@ -293,40 +518,7 @@ class LoginButton extends ConsumerWidget {
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
|
),
|
|
|
- onPressed: () async {
|
|
|
- // This will remove current cache asset state of previous user login.
|
|
|
- ref.read(assetProvider.notifier).clearAllAsset();
|
|
|
-
|
|
|
- var isAuthenticated =
|
|
|
- await ref.read(authenticationProvider.notifier).login(
|
|
|
- emailController.text,
|
|
|
- passwordController.text,
|
|
|
- serverEndpointController.text,
|
|
|
- );
|
|
|
-
|
|
|
- if (isAuthenticated) {
|
|
|
- // Resume backup (if enable) then navigate
|
|
|
- if (ref.read(authenticationProvider).shouldChangePassword &&
|
|
|
- !ref.read(authenticationProvider).isAdmin) {
|
|
|
- AutoRouter.of(context).push(const ChangePasswordRoute());
|
|
|
- } else {
|
|
|
- final hasPermission = await ref
|
|
|
- .read(galleryPermissionNotifier.notifier)
|
|
|
- .hasPermission;
|
|
|
- if (hasPermission) {
|
|
|
- // Don't resume the backup until we have gallery permission
|
|
|
- ref.read(backupProvider.notifier).resumeBackup();
|
|
|
- }
|
|
|
- AutoRouter.of(context).replace(const TabControllerRoute());
|
|
|
- }
|
|
|
- } else {
|
|
|
- ImmichToast.show(
|
|
|
- context: context,
|
|
|
- msg: "login_form_failed_login".tr(),
|
|
|
- toastType: ToastType.error,
|
|
|
- );
|
|
|
- }
|
|
|
- },
|
|
|
+ onPressed: onPressed,
|
|
|
icon: const Icon(Icons.login_rounded),
|
|
|
label: const Text(
|
|
|
"login_form_button_text",
|
|
@@ -339,82 +531,26 @@ class LoginButton extends ConsumerWidget {
|
|
|
class OAuthLoginButton extends ConsumerWidget {
|
|
|
final TextEditingController serverEndpointController;
|
|
|
final ValueNotifier<bool> isLoading;
|
|
|
- final VoidCallback onLoginSuccess;
|
|
|
final String buttonLabel;
|
|
|
+ final Function() onPressed;
|
|
|
|
|
|
const OAuthLoginButton({
|
|
|
Key? key,
|
|
|
required this.serverEndpointController,
|
|
|
required this.isLoading,
|
|
|
- required this.onLoginSuccess,
|
|
|
required this.buttonLabel,
|
|
|
+ required this.onPressed,
|
|
|
}) : super(key: key);
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
|
- var oAuthService = ref.watch(oAuthServiceProvider);
|
|
|
-
|
|
|
- void performOAuthLogin() async {
|
|
|
- ref.watch(assetProvider.notifier).clearAllAsset();
|
|
|
- OAuthConfigResponseDto? oAuthServerConfig;
|
|
|
-
|
|
|
- try {
|
|
|
- oAuthServerConfig = await oAuthService
|
|
|
- .getOAuthServerConfig(serverEndpointController.text);
|
|
|
-
|
|
|
- isLoading.value = true;
|
|
|
- } catch (e) {
|
|
|
- ImmichToast.show(
|
|
|
- context: context,
|
|
|
- msg: "login_form_failed_get_oauth_server_config".tr(),
|
|
|
- toastType: ToastType.error,
|
|
|
- );
|
|
|
- isLoading.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (oAuthServerConfig != null && oAuthServerConfig.enabled) {
|
|
|
- var loginResponseDto =
|
|
|
- await oAuthService.oAuthLogin(oAuthServerConfig.url!);
|
|
|
-
|
|
|
- if (loginResponseDto != null) {
|
|
|
- var isSuccess = await ref
|
|
|
- .watch(authenticationProvider.notifier)
|
|
|
- .setSuccessLoginInfo(
|
|
|
- accessToken: loginResponseDto.accessToken,
|
|
|
- serverUrl: serverEndpointController.text,
|
|
|
- );
|
|
|
-
|
|
|
- if (isSuccess) {
|
|
|
- isLoading.value = false;
|
|
|
- onLoginSuccess();
|
|
|
- } else {
|
|
|
- ImmichToast.show(
|
|
|
- context: context,
|
|
|
- msg: "login_form_failed_login".tr(),
|
|
|
- toastType: ToastType.error,
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- isLoading.value = false;
|
|
|
- } else {
|
|
|
- ImmichToast.show(
|
|
|
- context: context,
|
|
|
- msg: "login_form_failed_get_oauth_server_disable".tr(),
|
|
|
- toastType: ToastType.info,
|
|
|
- );
|
|
|
- isLoading.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
return ElevatedButton.icon(
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
backgroundColor: Theme.of(context).primaryColor.withAlpha(230),
|
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
|
),
|
|
|
- onPressed: performOAuthLogin,
|
|
|
+ onPressed: onPressed,
|
|
|
icon: const Icon(Icons.pin_rounded),
|
|
|
label: Text(
|
|
|
buttonLabel,
|