lemmur-app-android/lib/pages/settings/add_account_page.dart

195 lines
6.5 KiB
Dart

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<String?>(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<ConfigStore>().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<String>(
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<Color>(theme.canvasColor),
),
),
),
TextButton(
onPressed: () {
launchLink(
// TODO: extract to LemmyUrls or something
link: 'https://${selectedInstance.value}/login',
context: context,
);
},
child: const Text('Register'),
),
],
),
),
);
}
static Route<String> route(String instanceHost) => MaterialPageRoute(
builder: (context) => AddAccountPage(instanceHost: instanceHost),
fullscreenDialog: true,
);
}