make verify page use a form

This commit is contained in:
Henry Hiles 2026-06-05 17:23:09 -04:00
commit 91dde7b684
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
3 changed files with 40 additions and 52 deletions

View file

@ -0,0 +1,2 @@
String? requiredValidator(String? value) =>
value == null || value.isEmpty ? "This field is required" : null;

View file

@ -3,6 +3,7 @@ import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_controller.dart";
import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/appbar.dart";
import "package:nexus/helpers/required_validator_helper.dart";
class LoginPage extends HookConsumerWidget { class LoginPage extends HookConsumerWidget {
final Uri homeserver; final Uri homeserver;
@ -44,9 +45,6 @@ class LoginPage extends HookConsumerWidget {
} }
} }
String? requiredValidator(String? value) =>
value == null || value.isEmpty ? "This field is required" : null;
return Scaffold( return Scaffold(
appBar: Appbar( appBar: Appbar(
leading: IconButton( leading: IconButton(
@ -65,7 +63,7 @@ class LoginPage extends HookConsumerWidget {
TextFormField( TextFormField(
autofocus: true, autofocus: true,
textInputAction: .next, textInputAction: .next,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: .onUserInteraction,
validator: requiredValidator, validator: requiredValidator,
decoration: .new(label: Text("Username")), decoration: .new(label: Text("Username")),
controller: username, controller: username,
@ -78,7 +76,7 @@ class LoginPage extends HookConsumerWidget {
errorText: inputError.value, errorText: inputError.value,
errorMaxLines: 5, errorMaxLines: 5,
), ),
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: .onUserInteraction,
validator: requiredValidator, validator: requiredValidator,
controller: password, controller: password,
obscureText: true, obscureText: true,

View file

@ -3,6 +3,7 @@ import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_controller.dart";
import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/appbar.dart";
import "package:nexus/helpers/required_validator_helper.dart";
class VerifyPage extends HookConsumerWidget { class VerifyPage extends HookConsumerWidget {
const VerifyPage({super.key}); const VerifyPage({super.key});
@ -11,12 +12,16 @@ class VerifyPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final passphraseController = useTextEditingController(); final passphraseController = useTextEditingController();
final isLoading = useState(false); final isLoading = useState(false);
final inputError = useState<String?>(null);
final formKey = useRef(GlobalKey<FormState>());
return Scaffold( return Scaffold(
appBar: Appbar(), appBar: Appbar(),
body: AlertDialog( body: AlertDialog(
title: Text("Verify"), title: Text("Verify"),
content: Column( content: Form(
key: formKey.value,
child: Column(
mainAxisSize: .min, mainAxisSize: .min,
crossAxisAlignment: .start, crossAxisAlignment: .start,
children: [ children: [
@ -24,55 +29,38 @@ class VerifyPage extends HookConsumerWidget {
"Enter your recovery key or passphrase below to unlock encrypted events.\nYour passphrase is usually not the same as your password.", "Enter your recovery key or passphrase below to unlock encrypted events.\nYour passphrase is usually not the same as your password.",
), ),
SizedBox(height: 12), SizedBox(height: 12),
TextField( TextFormField(
autofocus: true, autofocus: true,
controller: passphraseController, controller: passphraseController,
textInputAction: .done,
autovalidateMode: .onUserInteraction,
validator: requiredValidator,
obscureText: true, obscureText: true,
decoration: .new(label: Text("Recovery Key or Passphrase")), decoration: .new(
label: Text("Recovery Key or Passphrase"),
errorText: inputError.value,
),
), ),
], ],
), ),
),
actions: [ actions: [
TextButton( TextButton(
onPressed: isLoading.value onPressed: isLoading.value
? null ? null
: () async { : () async {
final scaffoldMessenger = ScaffoldMessenger.of(context);
final snackbar = scaffoldMessenger.showSnackBar(
.new(
content: Text(
"Attempting to verify with recovery key...",
),
duration: .new(days: 999),
),
);
isLoading.value = true; isLoading.value = true;
final error = await ref try {
if (formKey.value.currentState?.validate() != true) {
return;
}
inputError.value = await ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)
.verify(passphraseController.text); .verify(passphraseController.text);
} finally {
snackbar.close();
if (error != null) {
isLoading.value = false; isLoading.value = false;
if (context.mounted) {
scaffoldMessenger.showSnackBar(
.new(
backgroundColor: Theme.of(
context,
).colorScheme.errorContainer,
content: Text(
"Verification failed. Is your passphrase correct?\nError: $error",
style: .new(
color: Theme.of(
context,
).colorScheme.onErrorContainer,
),
),
),
);
}
} }
}, },
child: Text("Verify"), child: Text("Verify"),