318 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:email_validator/email_validator.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:go_router/go_router.dart';
 | 
						|
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
						|
import 'package:gap/gap.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:island/pods/network.dart';
 | 
						|
import 'package:island/screens/account/me/profile_update.dart';
 | 
						|
import 'package:island/widgets/alert.dart';
 | 
						|
import 'package:island/widgets/app_scaffold.dart';
 | 
						|
import 'package:material_symbols_icons/symbols.dart';
 | 
						|
import 'package:styled_widget/styled_widget.dart';
 | 
						|
import 'package:url_launcher/url_launcher_string.dart';
 | 
						|
 | 
						|
import 'captcha.dart';
 | 
						|
 | 
						|
class CreateAccountScreen extends HookConsumerWidget {
 | 
						|
  const CreateAccountScreen({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final formKey = useMemoized(GlobalKey<FormState>.new, const []);
 | 
						|
 | 
						|
    final emailController = useTextEditingController();
 | 
						|
    final usernameController = useTextEditingController();
 | 
						|
    final nicknameController = useTextEditingController();
 | 
						|
    final passwordController = useTextEditingController();
 | 
						|
 | 
						|
    void showPostCreateModal() {
 | 
						|
      showModalBottomSheet(
 | 
						|
        isScrollControlled: true,
 | 
						|
        context: context,
 | 
						|
        builder: (context) => _PostCreateModal(),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    void performAction() async {
 | 
						|
      if (!formKey.currentState!.validate()) return;
 | 
						|
 | 
						|
      final captchaTk = await Navigator.of(
 | 
						|
        context,
 | 
						|
      ).push(MaterialPageRoute(builder: (context) => CaptchaScreen()));
 | 
						|
      if (captchaTk == null) return;
 | 
						|
 | 
						|
      if (!context.mounted) return;
 | 
						|
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final client = ref.watch(apiClientProvider);
 | 
						|
        await client.post(
 | 
						|
          '/pass/accounts',
 | 
						|
          data: {
 | 
						|
            'name': usernameController.text,
 | 
						|
            'nick': nicknameController.text,
 | 
						|
            'email': emailController.text,
 | 
						|
            'password': passwordController.text,
 | 
						|
            'language':
 | 
						|
                kServerSupportedLanguages[EasyLocalization.of(
 | 
						|
                  context,
 | 
						|
                )!.currentLocale.toString()] ??
 | 
						|
                'en-us',
 | 
						|
            'captcha_token': captchaTk,
 | 
						|
          },
 | 
						|
        );
 | 
						|
        if (!context.mounted) return;
 | 
						|
        hideLoadingModal(context);
 | 
						|
        showPostCreateModal();
 | 
						|
      } catch (err) {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
        showErrorAlert(err);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return AppScaffold(
 | 
						|
      isNoBackground: false,
 | 
						|
      appBar: AppBar(
 | 
						|
        leading: const PageBackButton(),
 | 
						|
        title: Text('createAccount').tr(),
 | 
						|
      ),
 | 
						|
      body:
 | 
						|
          StyledWidget(
 | 
						|
            Container(
 | 
						|
              constraints: const BoxConstraints(maxWidth: 380),
 | 
						|
              child: SingleChildScrollView(
 | 
						|
                child: Column(
 | 
						|
                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
                  children: [
 | 
						|
                    Align(
 | 
						|
                      alignment: Alignment.centerLeft,
 | 
						|
                      child: CircleAvatar(
 | 
						|
                        radius: 26,
 | 
						|
                        child: const Icon(Symbols.person_add, size: 28),
 | 
						|
                      ).padding(bottom: 8),
 | 
						|
                    ),
 | 
						|
                    Text(
 | 
						|
                      'createAccount',
 | 
						|
                      style: const TextStyle(
 | 
						|
                        fontSize: 28,
 | 
						|
                        fontWeight: FontWeight.w900,
 | 
						|
                      ),
 | 
						|
                    ).tr().padding(left: 4, bottom: 16),
 | 
						|
                    Form(
 | 
						|
                      key: formKey,
 | 
						|
                      autovalidateMode: AutovalidateMode.onUserInteraction,
 | 
						|
                      child: Column(
 | 
						|
                        children: [
 | 
						|
                          TextFormField(
 | 
						|
                            controller: usernameController,
 | 
						|
                            validator: (value) {
 | 
						|
                              if (value == null || value.isEmpty) {
 | 
						|
                                return 'fieldCannotBeEmpty'.tr();
 | 
						|
                              }
 | 
						|
                              return null;
 | 
						|
                            },
 | 
						|
                            autocorrect: false,
 | 
						|
                            enableSuggestions: false,
 | 
						|
                            autofillHints: const [AutofillHints.username],
 | 
						|
                            decoration: InputDecoration(
 | 
						|
                              isDense: true,
 | 
						|
                              border: const UnderlineInputBorder(),
 | 
						|
                              labelText: 'username'.tr(),
 | 
						|
                              helperText: 'usernameCannotChangeHint'.tr(),
 | 
						|
                            ),
 | 
						|
                            onTapOutside:
 | 
						|
                                (_) =>
 | 
						|
                                    FocusManager.instance.primaryFocus
 | 
						|
                                        ?.unfocus(),
 | 
						|
                          ),
 | 
						|
                          const Gap(12),
 | 
						|
                          TextFormField(
 | 
						|
                            controller: nicknameController,
 | 
						|
                            validator: (value) {
 | 
						|
                              if (value == null || value.isEmpty) {
 | 
						|
                                return 'fieldCannotBeEmpty'.tr();
 | 
						|
                              }
 | 
						|
                              return null;
 | 
						|
                            },
 | 
						|
                            autocorrect: false,
 | 
						|
                            autofillHints: const [AutofillHints.nickname],
 | 
						|
                            decoration: InputDecoration(
 | 
						|
                              isDense: true,
 | 
						|
                              border: const UnderlineInputBorder(),
 | 
						|
                              labelText: 'nickname'.tr(),
 | 
						|
                            ),
 | 
						|
                            onTapOutside:
 | 
						|
                                (_) =>
 | 
						|
                                    FocusManager.instance.primaryFocus
 | 
						|
                                        ?.unfocus(),
 | 
						|
                          ),
 | 
						|
                          const Gap(12),
 | 
						|
                          TextFormField(
 | 
						|
                            controller: emailController,
 | 
						|
                            validator: (value) {
 | 
						|
                              if (value == null || value.isEmpty) {
 | 
						|
                                return 'fieldCannotBeEmpty'.tr();
 | 
						|
                              }
 | 
						|
                              if (!EmailValidator.validate(value)) {
 | 
						|
                                return 'fieldEmailAddressMustBeValid'.tr();
 | 
						|
                              }
 | 
						|
                              return null;
 | 
						|
                            },
 | 
						|
                            autocorrect: false,
 | 
						|
                            enableSuggestions: false,
 | 
						|
                            autofillHints: const [AutofillHints.email],
 | 
						|
                            decoration: InputDecoration(
 | 
						|
                              isDense: true,
 | 
						|
                              border: const UnderlineInputBorder(),
 | 
						|
                              labelText: 'email'.tr(),
 | 
						|
                            ),
 | 
						|
                            onTapOutside:
 | 
						|
                                (_) =>
 | 
						|
                                    FocusManager.instance.primaryFocus
 | 
						|
                                        ?.unfocus(),
 | 
						|
                          ),
 | 
						|
                          const Gap(12),
 | 
						|
                          TextFormField(
 | 
						|
                            controller: passwordController,
 | 
						|
                            validator: (value) {
 | 
						|
                              if (value == null || value.isEmpty) {
 | 
						|
                                return 'fieldCannotBeEmpty'.tr();
 | 
						|
                              }
 | 
						|
                              return null;
 | 
						|
                            },
 | 
						|
                            obscureText: true,
 | 
						|
                            autocorrect: false,
 | 
						|
                            enableSuggestions: false,
 | 
						|
                            autofillHints: const [AutofillHints.password],
 | 
						|
                            decoration: InputDecoration(
 | 
						|
                              isDense: true,
 | 
						|
                              border: const UnderlineInputBorder(),
 | 
						|
                              labelText: 'password'.tr(),
 | 
						|
                            ),
 | 
						|
                            onTapOutside:
 | 
						|
                                (_) =>
 | 
						|
                                    FocusManager.instance.primaryFocus
 | 
						|
                                        ?.unfocus(),
 | 
						|
                          ),
 | 
						|
                        ],
 | 
						|
                      ).padding(horizontal: 7),
 | 
						|
                    ),
 | 
						|
                    const Gap(16),
 | 
						|
                    Align(
 | 
						|
                      alignment: Alignment.centerRight,
 | 
						|
                      child: StyledWidget(
 | 
						|
                        Container(
 | 
						|
                          constraints: const BoxConstraints(maxWidth: 290),
 | 
						|
                          child: Column(
 | 
						|
                            crossAxisAlignment: CrossAxisAlignment.end,
 | 
						|
                            children: [
 | 
						|
                              Text(
 | 
						|
                                'termAcceptNextWithAgree'.tr(),
 | 
						|
                                textAlign: TextAlign.end,
 | 
						|
                                style: Theme.of(
 | 
						|
                                  context,
 | 
						|
                                ).textTheme.bodySmall!.copyWith(
 | 
						|
                                  color: Theme.of(context).colorScheme.onSurface
 | 
						|
                                      .withAlpha((255 * 0.75).round()),
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                              Material(
 | 
						|
                                color: Colors.transparent,
 | 
						|
                                child: InkWell(
 | 
						|
                                  child: Row(
 | 
						|
                                    mainAxisSize: MainAxisSize.min,
 | 
						|
                                    children: [
 | 
						|
                                      Text('termAcceptLink').tr(),
 | 
						|
                                      const Gap(4),
 | 
						|
                                      const Icon(Symbols.launch, size: 14),
 | 
						|
                                    ],
 | 
						|
                                  ),
 | 
						|
                                  onTap: () {
 | 
						|
                                    launchUrlString(
 | 
						|
                                      'https://solsynth.dev/terms',
 | 
						|
                                    );
 | 
						|
                                  },
 | 
						|
                                ),
 | 
						|
                              ),
 | 
						|
                            ],
 | 
						|
                          ),
 | 
						|
                        ),
 | 
						|
                      ).padding(horizontal: 16),
 | 
						|
                    ),
 | 
						|
                    Align(
 | 
						|
                      alignment: Alignment.centerRight,
 | 
						|
                      child: TextButton(
 | 
						|
                        onPressed: () {
 | 
						|
                          performAction();
 | 
						|
                        },
 | 
						|
                        child: Row(
 | 
						|
                          mainAxisSize: MainAxisSize.min,
 | 
						|
                          children: [
 | 
						|
                            Text("next").tr(),
 | 
						|
                            const Icon(Symbols.chevron_right),
 | 
						|
                          ],
 | 
						|
                        ),
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ).padding(all: 24).center(),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class _PostCreateModal extends HookConsumerWidget {
 | 
						|
  const _PostCreateModal();
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    return Center(
 | 
						|
      child: ConstrainedBox(
 | 
						|
        constraints: const BoxConstraints(maxWidth: 280),
 | 
						|
        child: Column(
 | 
						|
          crossAxisAlignment: CrossAxisAlignment.center,
 | 
						|
          mainAxisAlignment: MainAxisAlignment.center,
 | 
						|
          children: [
 | 
						|
            Text('🎉').fontSize(32),
 | 
						|
            Text(
 | 
						|
              'postCreateAccountTitle'.tr(),
 | 
						|
              textAlign: TextAlign.center,
 | 
						|
            ).fontSize(17),
 | 
						|
            const Gap(18),
 | 
						|
            Text('postCreateAccountNext').tr().fontSize(19).bold(),
 | 
						|
            const Gap(4),
 | 
						|
            Row(
 | 
						|
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
              spacing: 6,
 | 
						|
              children: [
 | 
						|
                Text('\u2022'),
 | 
						|
                Expanded(child: Text('postCreateAccountNext1').tr()),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
            Row(
 | 
						|
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
              spacing: 6,
 | 
						|
              children: [
 | 
						|
                Text('\u2022'),
 | 
						|
                Expanded(child: Text('postCreateAccountNext2').tr()),
 | 
						|
              ],
 | 
						|
            ),
 | 
						|
            const Gap(6),
 | 
						|
            TextButton(
 | 
						|
              onPressed: () {
 | 
						|
                Navigator.pop(context);
 | 
						|
                context.pushReplacementNamed('login');
 | 
						|
              },
 | 
						|
              child: Text('login'.tr()),
 | 
						|
            ),
 | 
						|
          ],
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |