Merge pull request #27 from krawieck/add-accounts

This commit is contained in:
Filip Krawczyk 2020-09-08 16:47:39 +02:00 committed by GitHub
commit 0f59735b15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 197 additions and 8 deletions

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
@ -77,12 +78,15 @@ class _AppearanceConfig extends StatelessWidget {
}
}
class _AccountsConfig extends StatelessWidget {
class _AccountsConfig extends HookWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
backgroundColor: theme.scaffoldBackgroundColor,
shadowColor: Colors.transparent,
@ -90,6 +94,16 @@ class _AccountsConfig extends StatelessWidget {
title: Text('Accounts', style: theme.textTheme.headline6),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (_) =>
_AccountsConfigAddInstanceDialog(scaffoldKey: _scaffoldKey),
);
},
child: Icon(Icons.add),
),
body: Observer(
builder: (ctx) {
var accountsStore = ctx.watch<AccountsStore>();
@ -116,9 +130,21 @@ class _AccountsConfig extends StatelessWidget {
onTap: () {}, // TODO: go to managing account
),
],
Divider(),
ListTile(
leading: Icon(Icons.add),
title: Text('Add account'),
onTap: () {
showDialog(
context: context,
builder: (_) => _AccountsConfigAddAccountDialog(
scaffoldKey: _scaffoldKey,
instanceUrl: entry.key,
),
);
},
),
]
]..removeLast(), // removes trailing Divider
],
);
},
),
@ -126,6 +152,135 @@ class _AccountsConfig extends StatelessWidget {
}
}
class _AccountsConfigAddInstanceDialog extends HookWidget {
final GlobalKey<ScaffoldState> scaffoldKey;
const _AccountsConfigAddInstanceDialog({@required this.scaffoldKey})
: assert(scaffoldKey != null);
@override
Widget build(BuildContext context) {
var instanceController = useTextEditingController();
useValueListenable(instanceController);
var loading = useState(false);
handleOnAdd() async {
try {
loading.value = true;
await context
.read<AccountsStore>()
.addInstance(instanceController.text);
scaffoldKey.currentState.hideCurrentSnackBar();
} on Exception catch (err) {
scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(err.toString()),
));
}
loading.value = false;
Navigator.of(context).pop();
}
return AlertDialog(
title: Text('Add instance'),
content: TextField(
autofocus: true,
controller: instanceController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Instance url',
),
),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: !loading.value ? Text('Add') : CircularProgressIndicator(),
onPressed: instanceController.text.isEmpty ? null : handleOnAdd,
),
],
);
}
}
class _AccountsConfigAddAccountDialog extends HookWidget {
final GlobalKey<ScaffoldState> scaffoldKey;
final String instanceUrl;
const _AccountsConfigAddAccountDialog(
{@required this.scaffoldKey, @required this.instanceUrl})
: assert(scaffoldKey != null),
assert(instanceUrl != null);
@override
Widget build(BuildContext context) {
var usernameController = useTextEditingController();
var passwordController = useTextEditingController();
useValueListenable(usernameController);
useValueListenable(passwordController);
var loading = useState(false);
handleOnAdd() async {
try {
loading.value = true;
await context.read<AccountsStore>().addAccount(
instanceUrl,
usernameController.text,
passwordController.text,
);
} on Exception catch (err) {
scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(err.toString()),
));
}
loading.value = false;
Navigator.of(context).pop();
}
return AlertDialog(
title: Text('Add account'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
autofocus: true,
controller: usernameController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username or email',
),
),
const SizedBox(height: 5),
TextField(
controller: passwordController,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
),
),
],
),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: !loading.value ? Text('Add') : CircularProgressIndicator(),
onPressed:
usernameController.text.isEmpty || passwordController.text.isEmpty
? null
: handleOnAdd,
),
],
);
}
}
class _SectionHeading extends StatelessWidget {
final String text;

View File

@ -99,6 +99,9 @@ abstract class _AccountsStore with Store {
_defaultAccounts[instanceUrl] = username;
}
bool isAnonymousFor(String instanceUrl) =>
Computed(() => users[instanceUrl].isEmpty).value;
/// adds a new account
/// if it's the first account ever the account is
/// set as default for the app
@ -110,6 +113,10 @@ abstract class _AccountsStore with Store {
String usernameOrEmail,
String password,
) async {
if (!users.containsKey(instanceUrl)) {
throw Exception('No such instance was added');
}
var lemmy = LemmyApi(instanceUrl).v1;
var token = await lemmy.login(
@ -119,17 +126,37 @@ abstract class _AccountsStore with Store {
var userData =
await lemmy.getSite(auth: token.raw).then((value) => value.myUser);
if (!users.containsKey(instanceUrl)) {
if (users.isEmpty) {
// first account for this instance
if (users[instanceUrl].isEmpty) {
// first account ever
if (users.values.every((e) => e.isEmpty)) {
setDefaultAccount(instanceUrl, userData.name);
}
users[instanceUrl] = ObservableMap();
tokens[instanceUrl] = ObservableMap();
setDefaultAccountFor(instanceUrl, userData.name);
}
users[instanceUrl][userData.name] = userData;
tokens[instanceUrl][userData.name] = token;
}
/// adds a new instance with no accounts associated with it.
/// Additionally makes a test GET /site request to check if the instance exists
@action
Future<void> addInstance(String instanceUrl) async {
if (users.containsKey(instanceUrl)) {
throw Exception('This instance has already been added');
}
try {
await LemmyApi(instanceUrl).v1.getSite();
// ignore: avoid_catches_without_on_clauses
} catch (_) {
throw Exception('This instance seems to not exist');
}
users[instanceUrl] = ObservableMap();
tokens[instanceUrl] = ObservableMap();
}
// TODO: add a way of removing accounts/instances
}

View File

@ -93,6 +93,13 @@ mixin _$AccountsStore on _AccountsStore, Store {
.run(() => super.addAccount(instanceUrl, usernameOrEmail, password));
}
final _$addInstanceAsyncAction = AsyncAction('_AccountsStore.addInstance');
@override
Future<void> addInstance(String instanceUrl) {
return _$addInstanceAsyncAction.run(() => super.addInstance(instanceUrl));
}
final _$_AccountsStoreActionController =
ActionController(name: '_AccountsStore');