mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-03 22:07:23 +01:00 
			
		
		
		
	refactor: MVC sign up view
This commit is contained in:
		
							parent
							
								
									76199418b2
								
							
						
					
					
						commit
						db19b37f72
					
				@ -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';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								lib/controllers/sign_up_controller.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/controllers/sign_up_controller.dart
									
									
									
									
									
										Normal file
									
								
							@ -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<SignUp> {
 | 
			
		||||
  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);
 | 
			
		||||
}
 | 
			
		||||
@ -51,11 +51,13 @@ void main() async {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class FluffyChatApp extends StatelessWidget {
 | 
			
		||||
  final Widget test;
 | 
			
		||||
  final Widget testWidget;
 | 
			
		||||
  final Client testClient;
 | 
			
		||||
  static final GlobalKey<AdaptivePageLayoutState> _apl =
 | 
			
		||||
      GlobalKey<AdaptivePageLayoutState>();
 | 
			
		||||
 | 
			
		||||
  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,
 | 
			
		||||
 | 
			
		||||
@ -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<SignUp> {
 | 
			
		||||
  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<SignUp> {
 | 
			
		||||
      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<SignUp> {
 | 
			
		||||
          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<SignUp> {
 | 
			
		||||
          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<SignUp> {
 | 
			
		||||
            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(),
 | 
			
		||||
@ -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<Matrix> with WidgetsBindingObserver {
 | 
			
		||||
 | 
			
		||||
  BackgroundPush _backgroundPush;
 | 
			
		||||
 | 
			
		||||
  bool get testMode => widget.testClient != null;
 | 
			
		||||
 | 
			
		||||
  Map<String, dynamic> get shareContent => _shareContent;
 | 
			
		||||
  set shareContent(Map<String, dynamic> content) {
 | 
			
		||||
    _shareContent = content;
 | 
			
		||||
@ -256,7 +261,7 @@ class MatrixState extends State<Matrix> 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);
 | 
			
		||||
 | 
			
		||||
@ -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));
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								test/sign_up_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								test/sign_up_test.dart
									
									
									
									
									
										Normal file
									
								
							@ -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()));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user