feat: added submit on enter to login/signup pages and other text fields (#1336)

This commit is contained in:
ggurdin 2024-12-31 13:15:42 -05:00 committed by GitHub
parent 59ca59f465
commit 6f06cfd911
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 77 additions and 413 deletions

View file

@ -979,6 +979,9 @@ class ChatController extends State<ChatPageWithRoom>
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
textFields: [DialogTextField(hintText: L10n.of(context).reason)],
// #Pangea
autoSubmit: true,
// Pangea#
);
if (reason == null || reason.single.isEmpty) return;
// #Pangea
@ -1062,6 +1065,9 @@ class ChatController extends State<ChatPageWithRoom>
],
okLabel: L10n.of(context).remove,
cancelLabel: L10n.of(context).cancel,
// #Pangea
autoSubmit: true,
// Pangea#
)
: <String>[];
if (reasonInput == null) {
@ -1164,9 +1170,10 @@ class ChatController extends State<ChatPageWithRoom>
// #Pangea
if (selectedEvents.isEmpty) {
ErrorHandler.logError(
e: "No selected events in send again action",
s: StackTrace.current,
data: {"roomId": roomId});
e: "No selected events in send again action",
s: StackTrace.current,
data: {"roomId": roomId},
);
clearSelectedEvents();
return;
}

View file

@ -92,11 +92,11 @@ class ChatDetailsController extends State<ChatDetails> {
],
);
if (input == null) return;
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setDescription(input.single),
);
// #Pangea
// final success = await showFutureLoadingDialog(
// context: context,
// future: () => room.setDescription(input.single),
// );
// if (success.error == null) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(

View file

@ -1,4 +1,5 @@
import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -13,59 +14,33 @@ class LoginView extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
// #Pangea
// final homeserver = Matrix.of(context)
// .getLoginClient()
// .homeserver
// .toString()
// .replaceFirst('https://', '');
// final title = L10n.of(context).logInTo(homeserver);
// final titleParts = title.split(homeserver);
// Pangea#
final homeserver = Matrix.of(context)
.getLoginClient()
.homeserver
.toString()
.replaceFirst('https://', '');
final title = L10n.of(context).logInTo(homeserver);
final titleParts = title.split(homeserver);
return LoginScaffold(
// #Pangea
// enforceMobileMode: Matrix.of(context).client.isLogged(),
// Pangea#
enforceMobileMode: Matrix.of(context).client.isLogged(),
appBar: AppBar(
// #Pangea
// leading: controller.loading ? null : const BackButton(),
leading: controller.loading
? null
: Padding(
padding: const EdgeInsets.only(left: 10),
child: ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.all<Color>(
Theme.of(context).colorScheme.surface.withOpacity(0.75),
),
shape: WidgetStateProperty.all<OutlinedBorder>(
const CircleBorder(),
),
),
child: const Icon(Icons.arrow_back),
),
),
// Pangea#
leading: controller.loading ? null : const Center(child: BackButton()),
automaticallyImplyLeading: !controller.loading,
titleSpacing: !controller.loading ? 0 : null,
// #Pangea
// title: Text.rich(
// TextSpan(
// children: [
// TextSpan(text: titleParts.first),
// TextSpan(
// text: homeserver,
// style: const TextStyle(fontWeight: FontWeight.bold),
// ),
// TextSpan(text: titleParts.last),
// ],
// ),
// style: const TextStyle(fontSize: 18),
// ),
// Pangea#
title: Text.rich(
TextSpan(
children: [
TextSpan(text: titleParts.first),
TextSpan(
text: homeserver,
style: const TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: titleParts.last),
],
),
style: const TextStyle(fontSize: 18),
),
),
body: Builder(
builder: (context) {
@ -73,23 +48,18 @@ class LoginView extends StatelessWidget {
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 8),
children: <Widget>[
// #Pangea
const SizedBox(height: 80),
// Hero(
// tag: 'info-logo',
// child: Image.asset('assets/banner_transparent.png'),
// ),
// const SizedBox(height: 16),
// Pangea#
Hero(
tag: 'info-logo',
child: Image.asset('assets/banner_transparent.png'),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextField(
readOnly: controller.loading,
autocorrect: false,
// #Pangea
// autofocus: true,
// onChanged: controller.checkWellKnownWithCoolDown,
// Pangea#
autofocus: true,
onChanged: controller.checkWellKnownWithCoolDown,
controller: controller.usernameController,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.emailAddress,
@ -98,15 +68,8 @@ class LoginView extends StatelessWidget {
decoration: InputDecoration(
prefixIcon: const Icon(Icons.account_box_outlined),
errorText: controller.usernameError,
// #Pangea
errorStyle: TextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 14,
),
hintText: L10n.of(context).emailOrUsername,
// errorStyle,: const TextStyle(color: Colors.orange),
// hintText: '@username:domain',
// Pangea#
errorStyle: const TextStyle(color: Colors.orange),
hintText: '@username:domain',
labelText: L10n.of(context).emailOrUsername,
),
),
@ -126,35 +89,16 @@ class LoginView extends StatelessWidget {
decoration: InputDecoration(
prefixIcon: const Icon(Icons.lock_outlined),
errorText: controller.passwordError,
// #Pangea
errorStyle: TextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 14,
),
suffixIcon: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: controller.toggleShowPassword,
child: Icon(
controller.showPassword
? Icons.visibility_off_outlined
: Icons.visibility_outlined,
// color: Colors.black,
),
errorStyle: const TextStyle(color: Colors.orange),
suffixIcon: IconButton(
onPressed: controller.toggleShowPassword,
icon: Icon(
controller.showPassword
? Icons.visibility_off_outlined
: Icons.visibility_outlined,
color: Colors.black,
),
),
// errorStyle: const TextStyle(color: Colors.orange),
// suffixIcon: IconButton(
// onPressed: controller.toggleShowPassword,
// icon: Icon(
// controller.showPassword
// ? Icons.visibility_off_outlined
// : Icons.visibility_outlined,
// color: Colors.black,
// ),
// ),
// Pangea#
hintText: '******',
labelText: L10n.of(context).password,
),
@ -163,59 +107,21 @@ class LoginView extends StatelessWidget {
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
// #Pangea
child: ElevatedButton(
onPressed:
controller.loading ? null : () => controller.login(),
style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
foregroundColor: theme.colorScheme.onPrimary,
),
onPressed: controller.loading ? null : controller.login,
child: controller.loading
? const LinearProgressIndicator()
: Text(L10n.of(context).login),
),
// child: ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: theme.colorScheme.primary,
// foregroundColor: theme.colorScheme.onPrimary,
// ),
// onPressed: controller.loading ? null : controller.login,
// child: controller.loading
// ? const LinearProgressIndicator()
// : Text(L10n.of(context).login),
// ),
// Pangea#
),
// #Pangea
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
const Expanded(
child: Divider(
thickness: 1,
color: Colors.white,
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
L10n.of(context).or,
style: const TextStyle(fontSize: 18),
),
),
const Expanded(
child: Divider(
thickness: 1,
color: Colors.white,
),
),
],
),
),
// Pangea#
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
// #Pangea
child: ElevatedButton(
child: TextButton(
onPressed: controller.loading
? () {}
: controller.passwordForgotten,
@ -224,16 +130,6 @@ class LoginView extends StatelessWidget {
),
child: Text(L10n.of(context).passwordForgotten),
),
// child: TextButton(
// onPressed: controller.loading
// ? () {}
// : controller.passwordForgotten,
// style: TextButton.styleFrom(
// foregroundColor: theme.colorScheme.error,
// ),
// child: Text(L10n.of(context).passwordForgotten),
// ),
// Pangea#
),
const SizedBox(height: 16),
],

View file

@ -90,6 +90,11 @@ class NewGroupView extends StatelessWidget {
: L10n.of(context).chatName,
// Pangea#
),
// #Pangea
onSubmitted: (value) {
controller.loading ? null : controller.submitAction();
},
// Pangea#
),
),
const SizedBox(height: 16),

View file

@ -45,6 +45,9 @@ class SettingsController extends State<Settings> {
Matrix.of(context).client.userID!.localpart,
),
],
// #Pangea
autoSubmit: true,
// Pangea#
);
if (input == null) return;
final matrix = Matrix.of(context);

View file

@ -118,6 +118,7 @@ class FullWidthTextField extends StatelessWidget {
final String? Function(String?)? validator;
final TextEditingController? controller;
final String? errorText;
final Function(String)? onSubmitted;
const FullWidthTextField({
required this.hintText,
@ -128,6 +129,7 @@ class FullWidthTextField extends StatelessWidget {
this.validator,
this.controller,
this.errorText,
this.onSubmitted,
super.key,
});
@ -151,6 +153,7 @@ class FullWidthTextField extends StatelessWidget {
validator: validator,
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
controller: controller,
onFieldSubmitted: onSubmitted,
),
);
}

View file

@ -32,7 +32,10 @@ class PangeaLoginView extends StatelessWidget {
FullWidthTextField(
hintText: L10n.of(context).password,
obscureText: true,
textInputAction: TextInputAction.done,
textInputAction: TextInputAction.go,
onSubmitted: (_) {
controller.enabledSignIn ? controller.login() : null;
},
validator: (value) {
if (value == null || value.isEmpty) {
return L10n.of(context).pleaseEnterYourPassword;

View file

@ -43,6 +43,7 @@ class SignupWithEmailView extends StatelessWidget {
obscureText: true,
validator: controller.password1TextFieldValidator,
controller: controller.passwordController,
onSubmitted: controller.enableSignUp ? controller.signup : null,
),
TosCheckbox(
controller.isTnCChecked,

View file

@ -47,6 +47,7 @@ class SpaceCodeUtil {
textFields: [
DialogTextField(hintText: L10n.of(context).joinWithClassCodeHint),
],
autoSubmit: true,
);
if (spaceCode == null || spaceCode.single.isEmpty) return;
await pangeaController.classController.joinClasswithCode(

View file

@ -1,34 +0,0 @@
import 'package:flutter/material.dart';
class PInputTextField extends StatelessWidget {
TextEditingController controller;
Function(String) onSubmit;
String labelText;
String hintText;
PInputTextField({
super.key,
required this.controller,
required this.onSubmit,
required this.labelText,
required this.hintText,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
controller: controller,
autofocus: true,
autocorrect: false,
textInputAction: TextInputAction.go,
onSubmitted: onSubmit,
decoration: InputDecoration(
labelText: labelText,
prefixIcon: const Icon(Icons.people_outlined),
hintText: hintText,
),
),
);
}
}

View file

@ -1,221 +0,0 @@
import 'dart:typed_data';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/widgets/common/pangea_logo_svg.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart';
class SignupButtons extends StatefulWidget {
const SignupButtons({super.key});
@override
State<SignupButtons> createState() => SignupButtonsState();
}
class SignupButtonsState extends State<SignupButtons> {
final PangeaController pangeaController = MatrixState.pangeaController;
void pickAvatar() async {
final source = !PlatformInfos.isMobile
? ImageSource.gallery
: await showModalActionSheet<ImageSource>(
context: context,
title: L10n.of(context).changeYourAvatar,
actions: [
SheetAction(
key: ImageSource.camera,
label: L10n.of(context).openCamera,
isDefaultAction: true,
icon: Icons.camera_alt_outlined,
),
SheetAction(
key: ImageSource.gallery,
label: L10n.of(context).openGallery,
icon: Icons.photo_outlined,
),
],
);
if (source == null) return;
final picked = await ImagePicker().pickImage(
source: source,
imageQuality: 50,
maxWidth: 512,
maxHeight: 512,
);
setState(() {
Matrix.of(context).loginAvatar = picked;
});
}
final TextEditingController usernameController = TextEditingController();
String? signupError;
void signUp() async {
usernameController.text = usernameController.text.trim();
final localpart =
usernameController.text.toLowerCase().replaceAll(' ', '_');
if (localpart.isEmpty) {
setState(() {
signupError = L10n.of(context).pleaseChooseAUsername;
});
return;
}
setState(() {
signupError = null;
});
try {
try {
await Matrix.of(context).getLoginClient().register(username: localpart);
} on MatrixException catch (e) {
if (!e.requireAdditionalAuthentication) rethrow;
}
Matrix.of(context).loginUsername = usernameController.text;
context.go('/home/signup');
} catch (e, s) {
Logs().d('Sign up failed', e, s);
setState(() {
signupError = e.toLocalizedString(context);
});
}
}
@override
Widget build(BuildContext context) {
final avatar = Matrix.of(context).loginAvatar;
return Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: Center(
child: Stack(
children: [
Material(
borderRadius: BorderRadius.circular(64),
elevation:
Theme.of(context).appBarTheme.scrolledUnderElevation ??
10,
color: Colors.transparent,
shadowColor:
Theme.of(context).colorScheme.onBackground.withAlpha(64),
clipBehavior: Clip.hardEdge,
child: CircleAvatar(
radius: 64,
backgroundColor: Colors.white,
child: avatar == null
? const Icon(
Icons.person_outlined,
color: Colors.black,
size: 64,
)
: FutureBuilder<Uint8List>(
future: avatar.readAsBytes(),
builder: (context, snapshot) {
final bytes = snapshot.data;
if (bytes == null) {
return const CircularProgressIndicator
.adaptive();
}
return Image.memory(
bytes,
fit: BoxFit.cover,
width: 128,
height: 128,
);
},
),
),
),
Positioned(
bottom: 0,
right: 0,
child: FloatingActionButton(
mini: true,
onPressed: pickAvatar,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
child: const Icon(Icons.camera_alt_outlined),
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
controller: usernameController,
onSubmitted: (_) => signUp(),
decoration: InputDecoration(
prefixIcon: const Icon(Icons.account_box_outlined),
hintText: L10n.of(context).chooseAUsername,
errorText: signupError,
errorStyle: TextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color,
fontSize: 14,
),
fillColor:
Theme.of(context).colorScheme.background.withOpacity(0.75),
),
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Hero(
tag: 'signUpButton',
child: ElevatedButton(
onPressed: signUp,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const PangeaLogoSvg(width: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Text(L10n.of(context).signUp),
),
],
),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
const Expanded(
child: Divider(
thickness: 1,
color: Colors.white,
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
L10n.of(context).or,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
),
),
const Expanded(
child: Divider(
thickness: 1,
color: Colors.white,
),
),
],
),
),
],
);
}
}