From ed6c00172cf16996d2b18838a6e46ce463917b8f Mon Sep 17 00:00:00 2001 From: shilangyu Date: Mon, 7 Sep 2020 23:43:23 +0200 Subject: [PATCH 1/7] seperated addition of instances and accounts --- lib/stores/accounts_store.dart | 29 ++++++++++++++++++++++++----- lib/stores/accounts_store.g.dart | 11 +++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/stores/accounts_store.dart b/lib/stores/accounts_store.dart index 4db49c2..2f366e2 100644 --- a/lib/stores/accounts_store.dart +++ b/lib/stores/accounts_store.dart @@ -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,29 @@ 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 + @action + void addInstance(String instanceUrl) { + if (users.containsKey(instanceUrl)) { + throw Exception('This instance has already been added'); + } + + users[instanceUrl] = ObservableMap(); + tokens[instanceUrl] = ObservableMap(); + } + + // TODO: add a way of removing accounts/instances } diff --git a/lib/stores/accounts_store.g.dart b/lib/stores/accounts_store.g.dart index e91514f..986dbeb 100644 --- a/lib/stores/accounts_store.g.dart +++ b/lib/stores/accounts_store.g.dart @@ -118,6 +118,17 @@ mixin _$AccountsStore on _AccountsStore, Store { } } + @override + void addInstance(String instanceUrl) { + final _$actionInfo = _$_AccountsStoreActionController.startAction( + name: '_AccountsStore.addInstance'); + try { + return super.addInstance(instanceUrl); + } finally { + _$_AccountsStoreActionController.endAction(_$actionInfo); + } + } + @override String toString() { return ''' From 1dee2e21f5d7fd934caddaab42f620cb3dde0bc5 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Tue, 8 Sep 2020 00:34:09 +0200 Subject: [PATCH 2/7] added adding instances --- lib/pages/settings.dart | 49 +++++++++++++++++++++++++++++++- lib/stores/accounts_store.dart | 12 ++++++-- lib/stores/accounts_store.g.dart | 18 +++++------- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index bb994a2..6d0ae8c 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -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,16 @@ class _AppearanceConfig extends StatelessWidget { } } -class _AccountsConfig extends StatelessWidget { +class _AccountsConfig extends HookWidget { + final GlobalKey _scaffoldKey = GlobalKey(); + @override Widget build(BuildContext context) { var theme = Theme.of(context); + var textFieldController = useTextEditingController(); return Scaffold( + key: _scaffoldKey, appBar: AppBar( backgroundColor: theme.scaffoldBackgroundColor, shadowColor: Colors.transparent, @@ -90,6 +95,48 @@ class _AccountsConfig extends StatelessWidget { title: Text('Accounts', style: theme.textTheme.headline6), centerTitle: true, ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text('Add instance'), + content: TextField( + controller: textFieldController, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Instance url', + ), + ), + actions: [ + FlatButton( + child: Text('Add'), + onPressed: () async { + try { + await context + .read() + .addInstance(textFieldController.text); + } on Exception catch (err) { + _scaffoldKey.currentState.showSnackBar(SnackBar( + content: Text(err.toString()), + )); + } + Navigator.of(context).pop(); + }, + ), + FlatButton( + child: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + ), + ); + textFieldController.clear(); + }, + child: Icon(Icons.add), + ), body: Observer( builder: (ctx) { var accountsStore = ctx.watch(); diff --git a/lib/stores/accounts_store.dart b/lib/stores/accounts_store.dart index 2f366e2..6463f97 100644 --- a/lib/stores/accounts_store.dart +++ b/lib/stores/accounts_store.dart @@ -139,13 +139,21 @@ abstract class _AccountsStore with Store { tokens[instanceUrl][userData.name] = token; } - /// adds a new instance with no accounts associated with it + /// adds a new instance with no accounts associated with it. + /// Additionally makes a test GET /site request to check if the instance exists @action - void addInstance(String instanceUrl) { + Future 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(); } diff --git a/lib/stores/accounts_store.g.dart b/lib/stores/accounts_store.g.dart index 986dbeb..99a7e3c 100644 --- a/lib/stores/accounts_store.g.dart +++ b/lib/stores/accounts_store.g.dart @@ -93,6 +93,13 @@ mixin _$AccountsStore on _AccountsStore, Store { .run(() => super.addAccount(instanceUrl, usernameOrEmail, password)); } + final _$addInstanceAsyncAction = AsyncAction('_AccountsStore.addInstance'); + + @override + Future addInstance(String instanceUrl) { + return _$addInstanceAsyncAction.run(() => super.addInstance(instanceUrl)); + } + final _$_AccountsStoreActionController = ActionController(name: '_AccountsStore'); @@ -118,17 +125,6 @@ mixin _$AccountsStore on _AccountsStore, Store { } } - @override - void addInstance(String instanceUrl) { - final _$actionInfo = _$_AccountsStoreActionController.startAction( - name: '_AccountsStore.addInstance'); - try { - return super.addInstance(instanceUrl); - } finally { - _$_AccountsStoreActionController.endAction(_$actionInfo); - } - } - @override String toString() { return ''' From c08e07311a1894ecfefab73eadce1e4c4284644c Mon Sep 17 00:00:00 2001 From: shilangyu Date: Tue, 8 Sep 2020 01:27:02 +0200 Subject: [PATCH 3/7] you can now add accounts/instances --- lib/pages/settings.dart | 185 ++++++++++++++++++++++++++++++++-------- 1 file changed, 148 insertions(+), 37 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 6d0ae8c..655d99c 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -84,7 +84,6 @@ class _AccountsConfig extends HookWidget { @override Widget build(BuildContext context) { var theme = Theme.of(context); - var textFieldController = useTextEditingController(); return Scaffold( key: _scaffoldKey, @@ -96,44 +95,12 @@ class _AccountsConfig extends HookWidget { centerTitle: true, ), floatingActionButton: FloatingActionButton( - onPressed: () async { - await showDialog( + onPressed: () { + showDialog( context: context, - builder: (ctx) => AlertDialog( - title: Text('Add instance'), - content: TextField( - controller: textFieldController, - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Instance url', - ), - ), - actions: [ - FlatButton( - child: Text('Add'), - onPressed: () async { - try { - await context - .read() - .addInstance(textFieldController.text); - } on Exception catch (err) { - _scaffoldKey.currentState.showSnackBar(SnackBar( - content: Text(err.toString()), - )); - } - Navigator.of(context).pop(); - }, - ), - FlatButton( - child: Text('Cancel'), - onPressed: () { - Navigator.of(context).pop(); - }, - ) - ], - ), + builder: (_) => + _AccountsConfigAddInstanceDialog(scaffoldKey: _scaffoldKey), ); - textFieldController.clear(); }, child: Icon(Icons.add), ), @@ -163,6 +130,19 @@ class _AccountsConfig extends HookWidget { onTap: () {}, // TODO: go to managing account ), ], + ListTile( + leading: Icon(Icons.add), + title: Text('Add account'), + onTap: () { + showDialog( + context: context, + builder: (_) => _AccountsConfigAddAccountDialog( + scaffoldKey: _scaffoldKey, + instanceUrl: entry.key, + ), + ); + }, + ), Divider(), ] ]..removeLast(), // removes trailing Divider @@ -173,6 +153,137 @@ class _AccountsConfig extends HookWidget { } } +class _AccountsConfigAddInstanceDialog extends HookWidget { + final GlobalKey scaffoldKey; + + const _AccountsConfigAddInstanceDialog({@required this.scaffoldKey}) + : assert(scaffoldKey != null); + + @override + Widget build(BuildContext context) { + var instanceController = useTextEditingController(); + useValueListenable(instanceController); + + var loading = useState(false); + + return AlertDialog( + title: Text('Add instance'), + content: TextField( + autofocus: true, + controller: instanceController, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Instance url', + ), + ), + actions: [ + FlatButton( + child: !loading.value ? Text('Add') : CircularProgressIndicator(), + onPressed: instanceController.text.isEmpty + ? null + : () async { + try { + loading.value = true; + await context + .read() + .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(); + }, + ), + FlatButton( + child: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + ); + } +} + +class _AccountsConfigAddAccountDialog extends HookWidget { + final GlobalKey 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); + + 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: [ + FlatButton( + child: !loading.value ? Text('Add') : CircularProgressIndicator(), + onPressed: + usernameController.text.isEmpty || passwordController.text.isEmpty + ? null + : () async { + try { + loading.value = true; + await context.read().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(); + }, + ), + FlatButton( + child: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + ); + } +} + class _SectionHeading extends StatelessWidget { final String text; From 94ff01bc550a7b495df5056a51a206dd719380f7 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Tue, 8 Sep 2020 16:35:54 +0200 Subject: [PATCH 4/7] moved cancel buttons before add buttons --- lib/pages/settings.dart | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 655d99c..8087e3d 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -177,6 +177,12 @@ class _AccountsConfigAddInstanceDialog extends HookWidget { ), ), actions: [ + FlatButton( + child: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), FlatButton( child: !loading.value ? Text('Add') : CircularProgressIndicator(), onPressed: instanceController.text.isEmpty @@ -197,12 +203,6 @@ class _AccountsConfigAddInstanceDialog extends HookWidget { Navigator.of(context).pop(); }, ), - FlatButton( - child: Text('Cancel'), - onPressed: () { - Navigator.of(context).pop(); - }, - ) ], ); } @@ -251,6 +251,12 @@ class _AccountsConfigAddAccountDialog extends HookWidget { ], ), actions: [ + FlatButton( + child: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), FlatButton( child: !loading.value ? Text('Add') : CircularProgressIndicator(), onPressed: @@ -273,12 +279,6 @@ class _AccountsConfigAddAccountDialog extends HookWidget { Navigator.of(context).pop(); }, ), - FlatButton( - child: Text('Cancel'), - onPressed: () { - Navigator.of(context).pop(); - }, - ) ], ); } From 81b10cf3e3b60bedfe6f2e136fda28179ed8b885 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Tue, 8 Sep 2020 16:37:58 +0200 Subject: [PATCH 5/7] removed unnecessary dividers --- lib/pages/settings.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 8087e3d..95255c7 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -143,9 +143,8 @@ class _AccountsConfig extends HookWidget { ); }, ), - Divider(), ] - ]..removeLast(), // removes trailing Divider + ], ); }, ), From 8eb31434e244413316eb67bca3bc9b0f2bc93f53 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Tue, 8 Sep 2020 16:38:59 +0200 Subject: [PATCH 6/7] used expression functions where possible --- lib/pages/settings.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 95255c7..69c9b36 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -178,9 +178,7 @@ class _AccountsConfigAddInstanceDialog extends HookWidget { actions: [ FlatButton( child: Text('Cancel'), - onPressed: () { - Navigator.of(context).pop(); - }, + onPressed: () => Navigator.of(context).pop(), ), FlatButton( child: !loading.value ? Text('Add') : CircularProgressIndicator(), @@ -252,9 +250,7 @@ class _AccountsConfigAddAccountDialog extends HookWidget { actions: [ FlatButton( child: Text('Cancel'), - onPressed: () { - Navigator.of(context).pop(); - }, + onPressed: () => Navigator.of(context).pop(), ), FlatButton( child: !loading.value ? Text('Add') : CircularProgressIndicator(), From 94daf2acc6af44092423763ce5aa3a546304b3fa Mon Sep 17 00:00:00 2001 From: shilangyu Date: Tue, 8 Sep 2020 16:41:46 +0200 Subject: [PATCH 7/7] moved out logic from UI --- lib/pages/settings.dart | 68 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 69c9b36..43a8707 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -165,6 +165,22 @@ class _AccountsConfigAddInstanceDialog extends HookWidget { var loading = useState(false); + handleOnAdd() async { + try { + loading.value = true; + await context + .read() + .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( @@ -182,23 +198,7 @@ class _AccountsConfigAddInstanceDialog extends HookWidget { ), FlatButton( child: !loading.value ? Text('Add') : CircularProgressIndicator(), - onPressed: instanceController.text.isEmpty - ? null - : () async { - try { - loading.value = true; - await context - .read() - .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(); - }, + onPressed: instanceController.text.isEmpty ? null : handleOnAdd, ), ], ); @@ -223,6 +223,23 @@ class _AccountsConfigAddAccountDialog extends HookWidget { var loading = useState(false); + handleOnAdd() async { + try { + loading.value = true; + await context.read().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( @@ -257,22 +274,7 @@ class _AccountsConfigAddAccountDialog extends HookWidget { onPressed: usernameController.text.isEmpty || passwordController.text.isEmpty ? null - : () async { - try { - loading.value = true; - await context.read().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(); - }, + : handleOnAdd, ), ], );