[client] Password reset plumbing

This commit is contained in:
Abhishek Shroff
2025-05-25 11:13:10 +05:30
parent de04b3289a
commit d8a5c0c3ab
3 changed files with 77 additions and 13 deletions

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:phylum/ui/common/logo_row.dart';
import 'package:phylum/ui/login/instance_url_fragment.dart';
import 'package:phylum/ui/login/password_login_fragment.dart';
import 'package:phylum/util/dialogs.dart';
import 'package:uri/uri.dart';
import 'login_request.dart';
@@ -20,7 +22,9 @@ class _LoginPageState extends State<LoginPage> {
@override
void initState() {
super.initState();
_instanceUrl = widget.fixedInstanceUrl;
if (widget.fixedInstanceUrl != null) {
_instanceUrl = _cleanUrl(widget.fixedInstanceUrl!);
}
}
@override
@@ -53,8 +57,42 @@ class _LoginPageState extends State<LoginPage> {
Widget _buildBody(BuildContext context) {
final instanceUrl = _instanceUrl;
if (instanceUrl == null) {
return InstanceUrlFragment(onSubmitted: (url) => setState(() => _instanceUrl = url));
return InstanceUrlFragment(onSubmitted: (url) {
setState(() => _instanceUrl = _cleanUrl(url));
});
}
return PasswordLoginFragment(onSubmitted: (email, password) => performLogin(context, instanceUrl, email, password));
return PasswordLoginFragment(
onSubmitted: (email, password) => performLogin(context, instanceUrl, email, password),
onBackPressed: widget.fixedInstanceUrl == null ? () => setState(() => _instanceUrl = null) : null,
onForgotPassword: (email) async {
if (email.isEmpty) {
showAlertDialog(
context,
message: 'Please enter email address to reset password',
barrierDismissible: true,
);
return;
}
final confirm = await showAlertDialog(
context,
title: 'Reset Password',
message: 'This will request a password reset for $email. Do you want to continue?',
negativeText: 'No',
positiveText: 'Yes',
barrierDismissible: true,
) ??
false;
if (!confirm || !context.mounted) return;
},
);
}
Uri _cleanUrl(Uri url) {
if (url.path.endsWith('/')) {
final builder = UriBuilder.fromUri(url);
builder.path = url.pathSegments.where((s) => s.isNotEmpty).join('/');
return builder.build();
}
return url;
}
}

View File

@@ -11,8 +11,6 @@ import 'package:uri/uri.dart';
void performLogin(BuildContext context, Uri instanceUri, String email, String password) async {
final builder = UriBuilder.fromUri(instanceUri);
builder.path = builder.path.split('/').where((s) => s.isNotEmpty).join('/');
final cleanServerUrl = builder.build();
showProgressDialog(
context,
@@ -40,7 +38,7 @@ void performLogin(BuildContext context, Uri instanceUri, String email, String pa
);
} else {
final accountManager = context.read<AccountManager<PhylumAccount>>();
final account = await PhylumAccount.createFromLoginResponse(cleanServerUrl, json.cast<String, dynamic>());
final account = await PhylumAccount.createFromLoginResponse(instanceUri, json.cast<String, dynamic>());
accountManager.addAccount(account);
}
}

View File

@@ -2,8 +2,15 @@ import 'package:flutter/material.dart';
class PasswordLoginFragment extends StatefulWidget {
final Function(String, String) onSubmitted;
final Function()? onBackPressed;
final Function(String) onForgotPassword;
const PasswordLoginFragment({super.key, required this.onSubmitted});
const PasswordLoginFragment({
super.key,
required this.onSubmitted,
required this.onBackPressed,
required this.onForgotPassword,
});
@override
State<PasswordLoginFragment> createState() => _PasswordLoginFragmentState();
@@ -16,6 +23,7 @@ class _PasswordLoginFragmentState extends State<PasswordLoginFragment> {
@override
Widget build(BuildContext context) {
return Column(
spacing: 12.0,
children: [
TextField(
decoration: const InputDecoration(
@@ -31,13 +39,33 @@ class _PasswordLoginFragmentState extends State<PasswordLoginFragment> {
obscureText: true,
keyboardType: TextInputType.visiblePassword,
onChanged: (value) => setState(() => _password = value),
onSubmitted: _email.isNotEmpty ? (value) => widget.onSubmitted(_email, _password) : null,
onSubmitted: _email.isEmpty ? null : (value) => widget.onSubmitted(_email, _password),
),
TextButton(
onPressed: () => widget.onForgotPassword(_email),
child: Text(
'Forgot Password',
style: TextStyle(color: Theme.of(context).colorScheme.primary),
)),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
spacing: 12.0,
children: [
if (widget.onBackPressed != null)
TextButton(
onPressed: widget.onBackPressed,
child: Text(
'Back',
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
),
ElevatedButton(
onPressed: _email.isEmpty || _password.isEmpty ? null : () => widget.onSubmitted(_email, _password),
child: Text('Login'),
),
],
),
TextButton(onPressed: () {}, child: Text('Forgot Password')),
ElevatedButton(
onPressed: _email.isEmpty || _password.isEmpty ? null : () => widget.onSubmitted(_email, _password),
child: Text('Login'),
)
],
);
}