import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:lemmy_api_client/v3.dart'; import 'package:provider/provider.dart'; import '../../hooks/delayed_loading.dart'; import '../../hooks/stores.dart'; import '../../l10n/l10n.dart'; import '../../stores/accounts_store.dart'; import '../../stores/config_store.dart'; import '../../url_launcher.dart'; import '../../widgets/cached_network_image.dart'; import '../../widgets/fullscreenable_image.dart'; import '../../widgets/radio_picker.dart'; import 'add_instance_page.dart'; /// A modal where an account can be added for a given instance class AddAccountPage extends HookWidget { final String instanceHost; const AddAccountPage({required this.instanceHost}); @override Widget build(BuildContext context) { final theme = Theme.of(context); final usernameController = useListenable(useTextEditingController()); final passwordController = useListenable(useTextEditingController()); final passwordFocusNode = useFocusNode(); final accountsStore = useAccountsStore(); final loading = useDelayedLoading(); final selectedInstance = useState(instanceHost); final icon = useState(null); useEffect(() { LemmyApiV3(selectedInstance.value) .run(const GetSite()) .then((site) => icon.value = site.siteView?.site.icon); return null; }, [selectedInstance.value]); handleOnAdd() async { try { final isFirstAccount = accountsStore.hasNoAccount; loading.start(); await accountsStore.addAccount( selectedInstance.value, usernameController.text, passwordController.text, ); // if first account try to import settings if (isFirstAccount) { try { await context.read().importLemmyUserSettings( accountsStore .userDataFor( selectedInstance.value, usernameController.text)! .jwt); // ignore: empty_catches } catch (e) {} } Navigator.of(context).pop(); } on VerifyEmailException { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Verification email sent'), )); } on RegistrationApplicationSentException { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Registration application sent'), )); } on Exception catch (err) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(err.toString()), )); } loading.cancel(); } final handleSubmit = usernameController.text.isEmpty || passwordController.text.isEmpty ? null : loading.pending ? () {} : handleOnAdd; return Scaffold( appBar: AppBar( leading: const CloseButton(), title: const Text('Add account'), ), body: AutofillGroup( child: ListView( padding: const EdgeInsets.all(15), children: [ if (icon.value == null) const SizedBox(height: 150) else SizedBox( height: 150, child: FullscreenableImage( url: icon.value!, child: CachedNetworkImage( imageUrl: icon.value!, errorBuilder: (_, ___) => const SizedBox.shrink(), ), ), ), RadioPicker( title: 'select instance', values: accountsStore.instances.toList(), groupValue: selectedInstance.value, onChanged: (value) => selectedInstance.value = value, buttonBuilder: (context, displayValue, onPressed) => TextButton( onPressed: onPressed, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(displayValue), const Icon(Icons.arrow_drop_down), ], ), ), trailing: ListTile( leading: const Padding( padding: EdgeInsets.all(8), child: Icon(Icons.add), ), title: const Text('Add instance'), onTap: () async { final value = await Navigator.of(context).push(AddInstancePage.route()); Navigator.of(context).pop(value); }, ), ), TextField( autofocus: true, controller: usernameController, autofillHints: const [ AutofillHints.email, AutofillHints.username ], onSubmitted: (_) => passwordFocusNode.requestFocus(), decoration: InputDecoration( labelText: L10n.of(context).email_or_username), ), const SizedBox(height: 5), TextField( controller: passwordController, obscureText: true, focusNode: passwordFocusNode, onSubmitted: (_) => handleSubmit?.call(), autofillHints: const [AutofillHints.password], keyboardType: TextInputType.visiblePassword, decoration: InputDecoration(labelText: L10n.of(context).password), ), ElevatedButton( onPressed: handleSubmit, child: !loading.loading ? const Text('Sign in') : SizedBox( width: 20, height: 20, child: CircularProgressIndicator.adaptive( valueColor: AlwaysStoppedAnimation(theme.canvasColor), ), ), ), TextButton( onPressed: () { launchLink( // TODO: extract to LemmyUrls or something link: 'https://${selectedInstance.value}/login', context: context, ); }, child: const Text('Register'), ), ], ), ), ); } static Route route(String instanceHost) => MaterialPageRoute( builder: (context) => AddAccountPage(instanceHost: instanceHost), fullscreenDialog: true, ); }