mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
improve login ux (#2004)
removed animated switchers to resolve issue with flutter/issues/120874
This commit is contained in:
parent
3a1d5de742
commit
08ed71e51e
2 changed files with 134 additions and 132 deletions
|
@ -242,5 +242,6 @@
|
||||||
"permission_onboarding_go_to_settings": "Go to settings",
|
"permission_onboarding_go_to_settings": "Go to settings",
|
||||||
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
||||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
"permission_onboarding_continue_anyway": "Continue anyway",
|
||||||
"permission_onboarding_log_out": "Log out"
|
"permission_onboarding_log_out": "Log out",
|
||||||
|
"login_form_next_button": "Next"
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,13 +57,13 @@ class LoginForm extends HookConsumerWidget {
|
||||||
msg: "login_form_server_empty".tr(),
|
msg: "login_form_server_empty".tr(),
|
||||||
toastType: ToastType.error,
|
toastType: ToastType.error,
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isLoadingServer.value = true;
|
isLoadingServer.value = true;
|
||||||
final endpoint =
|
final endpoint =
|
||||||
await apiService.resolveAndSetEndpoint(serverUrl);
|
await apiService.resolveAndSetEndpoint(serverUrl);
|
||||||
|
|
||||||
final loginConfig = await apiService.oAuthApi.generateConfig(
|
final loginConfig = await apiService.oAuthApi.generateConfig(
|
||||||
|
@ -96,7 +96,7 @@ class LoginForm extends HookConsumerWidget {
|
||||||
isOauthEnable.value = false;
|
isOauthEnable.value = false;
|
||||||
isLoadingServer.value = false;
|
isLoadingServer.value = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoadingServer.value = false;
|
isLoadingServer.value = false;
|
||||||
return true;
|
return true;
|
||||||
|
@ -166,7 +166,6 @@ class LoginForm extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
oAuthLogin() async {
|
oAuthLogin() async {
|
||||||
var oAuthService = ref.watch(oAuthServiceProvider);
|
var oAuthService = ref.watch(oAuthServiceProvider);
|
||||||
ref.watch(assetProvider.notifier).clearAllAsset();
|
ref.watch(assetProvider.notifier).clearAllAsset();
|
||||||
|
@ -230,151 +229,153 @@ class LoginForm extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildSelectServer() {
|
buildSelectServer() {
|
||||||
return ConstrainedBox(
|
return Column(
|
||||||
key: const ValueKey('server'),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
constraints: const BoxConstraints(maxWidth: 300),
|
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(
|
||||||
|
'login_form_next_button',
|
||||||
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
|
if (isLoadingServer.value)
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 18.0),
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildLogin() {
|
||||||
|
return AutofillGroup(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
ServerEndpointInput(
|
Text(
|
||||||
controller: serverEndpointController,
|
serverEndpointController.text,
|
||||||
focusNode: serverEndpointFocusNode,
|
style: Theme.of(context).textTheme.displaySmall,
|
||||||
onSubmit: getServerLoginCredential,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
const SizedBox(height: 18),
|
||||||
ElevatedButton.icon(
|
EmailInput(
|
||||||
style: ElevatedButton.styleFrom(
|
controller: usernameController,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
focusNode: emailFocusNode,
|
||||||
),
|
onSubmit: passwordFocusNode.requestFocus,
|
||||||
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 SizedBox(height: 8),
|
||||||
const Padding(
|
PasswordInput(
|
||||||
padding: EdgeInsets.only(top: 18.0),
|
controller: passwordController,
|
||||||
child: Center(
|
focusNode: passwordFocusNode,
|
||||||
child: CircularProgressIndicator(),
|
onSubmit: login,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Note: This used to have an AnimatedSwitcher, but was removed
|
||||||
|
// because of https://github.com/flutter/flutter/issues/120874
|
||||||
|
isLoading.value
|
||||||
|
? const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 18.0),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: FittedBox(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
OAuthLoginButton(
|
||||||
|
serverEndpointController: serverEndpointController,
|
||||||
|
buttonLabel: oAuthButtonLabel.value,
|
||||||
|
isLoading: isLoading,
|
||||||
|
onPressed: oAuthLogin,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
TextButton.icon(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () => serverEndpoint.value = null,
|
||||||
|
label: const Text('Back'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
final serverSelectionOrLogin = serverEndpoint.value == null
|
||||||
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,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: 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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
OAuthLoginButton(
|
|
||||||
serverEndpointController: serverEndpointController,
|
|
||||||
buttonLabel: oAuthButtonLabel.value,
|
|
||||||
isLoading: isLoading,
|
|
||||||
onPressed: oAuthLogin,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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()
|
? buildSelectServer()
|
||||||
: buildLogin();
|
: buildLogin();
|
||||||
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Center(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
child: Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
constraints: const BoxConstraints(maxWidth: 300),
|
||||||
children: [
|
child: Column(
|
||||||
SizedBox(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
height: constraints.maxHeight / 5,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
SizedBox(
|
||||||
onDoubleTap: () => populateTestLoginInfo(),
|
height: constraints.maxHeight / 5,
|
||||||
child: RotationTransition(
|
|
||||||
turns: logoAnimationController,
|
|
||||||
child: const ImmichLogo(
|
|
||||||
heroTag: 'logo',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const ImmichTitleText(),
|
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),
|
||||||
|
|
||||||
|
// Note: This used to have an AnimatedSwitcher, but was removed
|
||||||
|
// because of https://github.com/flutter/flutter/issues/120874
|
||||||
|
serverSelectionOrLogin,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 18),
|
),
|
||||||
AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -386,7 +387,7 @@ class ServerEndpointInput extends StatelessWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final FocusNode focusNode;
|
final FocusNode focusNode;
|
||||||
final Function()? onSubmit;
|
final Function()? onSubmit;
|
||||||
|
|
||||||
const ServerEndpointInput({
|
const ServerEndpointInput({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
|
@ -436,7 +437,7 @@ class EmailInput extends StatelessWidget {
|
||||||
final Function()? onSubmit;
|
final Function()? onSubmit;
|
||||||
|
|
||||||
const EmailInput({
|
const EmailInput({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.focusNode,
|
this.focusNode,
|
||||||
this.onSubmit,
|
this.onSubmit,
|
||||||
|
@ -480,8 +481,8 @@ class PasswordInput extends StatelessWidget {
|
||||||
|
|
||||||
const PasswordInput({
|
const PasswordInput({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.focusNode,
|
this.focusNode,
|
||||||
this.onSubmit,
|
this.onSubmit,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue