diff --git a/lib/config/routes.dart b/lib/config/routes.dart index cabd5871..be64c045 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,6 +1,7 @@ import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/controllers/homeserver_picker_controller.dart'; +import 'package:fluffychat/controllers/sign_up_controller.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; import 'package:fluffychat/views/archive.dart'; import 'package:fluffychat/views/chat.dart'; @@ -24,7 +25,6 @@ import 'package:fluffychat/views/settings_ignore_list.dart'; import 'package:fluffychat/views/settings_multiple_emotes.dart'; import 'package:fluffychat/views/settings_notifications.dart'; import 'package:fluffychat/views/settings_style.dart'; -import 'package:fluffychat/views/sign_up.dart'; import 'package:fluffychat/views/sign_up_password.dart'; import 'package:flutter/material.dart'; diff --git a/lib/controllers/sign_up_controller.dart b/lib/controllers/sign_up_controller.dart new file mode 100644 index 00000000..58d969ec --- /dev/null +++ b/lib/controllers/sign_up_controller.dart @@ -0,0 +1,71 @@ +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:file_picker_cross/file_picker_cross.dart'; +import 'package:fluffychat/views/sign_up_view.dart'; + +import 'package:fluffychat/views/widgets/matrix.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class SignUp extends StatefulWidget { + @override + SignUpController createState() => SignUpController(); +} + +class SignUpController extends State { + final TextEditingController usernameController = TextEditingController(); + String usernameError; + bool loading = false; + MatrixFile avatar; + + void setAvatarAction() async { + var file = + await FilePickerCross.importFromStorage(type: FileTypeCross.image); + if (file != null) { + setState( + () => avatar = MatrixFile( + bytes: file.toUint8List(), + name: file.fileName, + ), + ); + } + } + + void resetAvatarAction() => setState(() => avatar = null); + + void signUpAction([_]) async { + var matrix = Matrix.of(context); + if (usernameController.text.isEmpty) { + setState(() => usernameError = L10n.of(context).pleaseChooseAUsername); + } else { + setState(() => usernameError = null); + } + + if (usernameController.text.isEmpty) { + return; + } + setState(() => loading = true); + + final preferredUsername = + usernameController.text.toLowerCase().trim().replaceAll(' ', '-'); + + try { + await matrix.client.usernameAvailable(preferredUsername); + } on MatrixException catch (exception) { + setState(() => usernameError = exception.errorMessage); + return setState(() => loading = false); + } catch (exception) { + setState(() => usernameError = exception.toString()); + return setState(() => loading = false); + } + setState(() => loading = false); + await AdaptivePageLayout.of(context).pushNamed( + '/signup/password/${Uri.encodeComponent(preferredUsername)}/${Uri.encodeComponent(usernameController.text)}', + arguments: avatar, + ); + } + + @override + Widget build(BuildContext context) => SignUpView(this); +} diff --git a/lib/main.dart b/lib/main.dart index 7754d295..0c5ed943 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -51,11 +51,13 @@ void main() async { } class FluffyChatApp extends StatelessWidget { - final Widget test; + final Widget testWidget; + final Client testClient; static final GlobalKey _apl = GlobalKey(); - const FluffyChatApp({Key key, this.test}) : super(key: key); + const FluffyChatApp({Key key, this.testWidget, this.testClient}) + : super(key: key); @override Widget build(BuildContext context) { return AdaptiveTheme( @@ -75,13 +77,14 @@ class FluffyChatApp extends StatelessWidget { builder: (context) => Matrix( context: context, apl: _apl, + testClient: testClient, child: Builder( builder: (context) => AdaptivePageLayout( key: _apl, safeAreaOnColumnView: false, - onGenerateRoute: test == null + onGenerateRoute: testWidget == null ? FluffyRoutes(context).onGenerateRoute - : (_) => ViewData(mainView: (_) => test), + : (_) => ViewData(mainView: (_) => testWidget), dividerColor: Theme.of(context).dividerColor, columnWidth: FluffyThemes.columnWidth, dividerWidth: 1.0, diff --git a/lib/views/sign_up.dart b/lib/views/sign_up_view.dart similarity index 54% rename from lib/views/sign_up.dart rename to lib/views/sign_up_view.dart index ee44d481..d62a3672 100644 --- a/lib/views/sign_up.dart +++ b/lib/views/sign_up_view.dart @@ -1,6 +1,5 @@ import 'package:adaptive_page_layout/adaptive_page_layout.dart'; -import 'package:famedlysdk/famedlysdk.dart'; -import 'package:file_picker_cross/file_picker_cross.dart'; +import 'package:fluffychat/controllers/sign_up_controller.dart'; import 'package:fluffychat/views/widgets/fluffy_banner.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; @@ -9,61 +8,13 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -class SignUp extends StatefulWidget { - @override - _SignUpState createState() => _SignUpState(); -} +class SignUpView extends StatelessWidget { + final SignUpController controller; -class _SignUpState extends State { - final TextEditingController usernameController = TextEditingController(); - String usernameError; - bool loading = false; - MatrixFile avatar; - - void setAvatarAction() async { - var file = - await FilePickerCross.importFromStorage(type: FileTypeCross.image); - if (file != null) { - setState( - () => avatar = MatrixFile( - bytes: file.toUint8List(), - name: file.fileName, - ), - ); - } - } - - void signUpAction(BuildContext context) async { - var matrix = Matrix.of(context); - if (usernameController.text.isEmpty) { - setState(() => usernameError = L10n.of(context).pleaseChooseAUsername); - } else { - setState(() => usernameError = null); - } - - if (usernameController.text.isEmpty) { - return; - } - setState(() => loading = true); - - final preferredUsername = - usernameController.text.toLowerCase().trim().replaceAll(' ', '-'); - - try { - await matrix.client.usernameAvailable(preferredUsername); - } on MatrixException catch (exception) { - setState(() => usernameError = exception.errorMessage); - return setState(() => loading = false); - } catch (exception) { - setState(() => usernameError = exception.toString()); - return setState(() => loading = false); - } - setState(() => loading = false); - await AdaptivePageLayout.of(context).pushNamed( - '/signup/password/${Uri.encodeComponent(preferredUsername)}/${Uri.encodeComponent(usernameController.text)}', - arguments: avatar, - ); - } + const SignUpView( + this.controller, { + Key key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -71,7 +22,7 @@ class _SignUpState extends State { child: Scaffold( appBar: AppBar( elevation: 0, - leading: loading ? Container() : BackButton(), + leading: controller.loading ? Container() : BackButton(), title: Text( Matrix.of(context) .client @@ -89,15 +40,16 @@ class _SignUpState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: TextField( - readOnly: loading, + readOnly: controller.loading, autocorrect: false, - controller: usernameController, - onSubmitted: (s) => signUpAction(context), - autofillHints: loading ? null : [AutofillHints.newUsername], + controller: controller.usernameController, + onSubmitted: controller.signUpAction, + autofillHints: + controller.loading ? null : [AutofillHints.newUsername], decoration: InputDecoration( prefixIcon: Icon(Icons.account_circle_outlined), hintText: L10n.of(context).username, - errorText: usernameError, + errorText: controller.usernameError, labelText: L10n.of(context).chooseAUsername, ), ), @@ -105,30 +57,31 @@ class _SignUpState extends State { SizedBox(height: 8), ListTile( leading: CircleAvatar( - backgroundImage: - avatar == null ? null : MemoryImage(avatar.bytes), - backgroundColor: avatar == null + backgroundImage: controller.avatar == null + ? null + : MemoryImage(controller.avatar.bytes), + backgroundColor: controller.avatar == null ? Theme.of(context).brightness == Brightness.dark ? Color(0xff121212) : Colors.white : Theme.of(context).secondaryHeaderColor, - child: avatar == null + child: controller.avatar == null ? Icon(Icons.camera_alt_outlined, color: Theme.of(context).primaryColor) : null, ), - trailing: avatar == null + trailing: controller.avatar == null ? null : Icon( Icons.close, color: Colors.red, ), - title: Text(avatar == null + title: Text(controller.avatar == null ? L10n.of(context).setAProfilePicture : L10n.of(context).discardPicture), - onTap: avatar == null - ? setAvatarAction - : () => setState(() => avatar = null), + onTap: controller.avatar == null + ? controller.setAvatarAction + : controller.resetAvatarAction, ), SizedBox(height: 16), Hero( @@ -136,8 +89,8 @@ class _SignUpState extends State { child: Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: ElevatedButton( - onPressed: loading ? null : () => signUpAction(context), - child: loading + onPressed: controller.loading ? null : controller.signUpAction, + child: controller.loading ? LinearProgressIndicator() : Text( L10n.of(context).signUp.toUpperCase(), diff --git a/lib/views/widgets/matrix.dart b/lib/views/widgets/matrix.dart index 46a8ecb6..ece07682 100644 --- a/lib/views/widgets/matrix.dart +++ b/lib/views/widgets/matrix.dart @@ -43,10 +43,13 @@ class Matrix extends StatefulWidget { final BuildContext context; + final Client testClient; + Matrix({ this.child, @required this.apl, @required this.context, + this.testClient, Key key, }) : super(key: key); @@ -66,6 +69,8 @@ class MatrixState extends State with WidgetsBindingObserver { BackgroundPush _backgroundPush; + bool get testMode => widget.testClient != null; + Map get shareContent => _shareContent; set shareContent(Map content) { _shareContent = content; @@ -256,7 +261,7 @@ class MatrixState extends State with WidgetsBindingObserver { }); }); } - client = FluffyClient(); + client = widget.testClient ?? FluffyClient(); LoadingDialog.defaultTitle = L10n.of(context).loadingPleaseWait; LoadingDialog.defaultBackLabel = L10n.of(context).close; LoadingDialog.defaultOnError = (Object e) => e.toLocalizedString(context); diff --git a/test/homeserver_picker_test.dart b/test/homeserver_picker_test.dart index d998af59..d13dcb31 100644 --- a/test/homeserver_picker_test.dart +++ b/test/homeserver_picker_test.dart @@ -5,7 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test if the widget can be created', (WidgetTester tester) async { - await tester.pumpWidget(FluffyChatApp(test: HomeserverPicker())); + await tester.pumpWidget(FluffyChatApp(testWidget: HomeserverPicker())); await tester.tap(find.byType(TextField)); await tester.tap(find.byType(ElevatedButton)); diff --git a/test/sign_up_test.dart b/test/sign_up_test.dart new file mode 100644 index 00000000..db426f10 --- /dev/null +++ b/test/sign_up_test.dart @@ -0,0 +1,9 @@ +import 'package:fluffychat/controllers/sign_up_controller.dart'; +import 'package:fluffychat/main.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Test if the widget can be created', (WidgetTester tester) async { + await tester.pumpWidget(FluffyChatApp(testWidget: SignUp())); + }); +}