From 49ec7f95b6bc692e948afcb9c4b2edc6b15e57d5 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 16:57:36 +0200 Subject: [PATCH 1/9] Closes #15. Fixing display of one digit minutes --- lib/src/widget/duration_indicator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/widget/duration_indicator.dart b/lib/src/widget/duration_indicator.dart index b417cbfc..6cd3a95b 100644 --- a/lib/src/widget/duration_indicator.dart +++ b/lib/src/widget/duration_indicator.dart @@ -32,7 +32,7 @@ class DurationIndicator extends StatelessWidget { Container( child: Center( child: Text( - "${duration.inHours % 24}:${duration.inMinutes % 60}", + "${duration.inHours % 24}:${duration.inMinutes % 60 < 10 ? "0" : ""}${duration.inMinutes % 60}", style: TextStyle(fontSize: 16), ), ), From 3ac08ed3b2c28a174f93eb9a1bdea8fccd468199 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 17:17:44 +0200 Subject: [PATCH 2/9] Added an All Category --- lib/src/models/category.dart | 2 ++ lib/src/services/categories_provider.dart | 15 +++++++++++++-- lib/src/services/data_repository.dart | 14 ++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/src/models/category.dart b/lib/src/models/category.dart index 787678bf..977b4328 100644 --- a/lib/src/models/category.dart +++ b/lib/src/models/category.dart @@ -7,6 +7,8 @@ class Category extends Equatable { final int recipeCount; String imageUrl; + Category(this.name, this.recipeCount); + Category.fromJson(Map json) : name = json["name"], recipeCount = json["recipe_count"] is int diff --git a/lib/src/services/categories_provider.dart b/lib/src/services/categories_provider.dart index fedac029..52552f50 100644 --- a/lib/src/services/categories_provider.dart +++ b/lib/src/services/categories_provider.dart @@ -19,8 +19,19 @@ class CategoriesProvider { if (response.statusCode == 200) { try { - return Category.parseCategories(response.body) - ..sort((a, b) => a.name.compareTo(b.name)); + List categories = Category.parseCategories(response.body); + categories.sort((a, b) => a.name.compareTo(b.name)); + categories.insert( + 0, + Category( + "All", + categories.fold( + 0, + (previousValue, element) => + previousValue + element.recipeCount), + ), + ); + return categories; } catch (e) { throw Exception(e); } diff --git a/lib/src/services/data_repository.dart b/lib/src/services/data_repository.dart index 86d38223..946ff7a0 100644 --- a/lib/src/services/data_repository.dart +++ b/lib/src/services/data_repository.dart @@ -25,8 +25,8 @@ class DataRepository { static Future> _allRecipesShort; // Actions - Future> fetchRecipesShort({String category = 'all'}) { - if (category == 'all') { + Future> fetchRecipesShort({String category = 'All'}) { + if (category == 'All') { return recipesShortProvider.fetchRecipesShort(); } else { return categoryRecipesShortProvider.fetchCategoryRecipesShort(category); @@ -52,8 +52,14 @@ class DataRepository { } Future _fetchCategoryImage(Category category) async { - List categoryRecipes = await categoryRecipesShortProvider - .fetchCategoryRecipesShort(category.name); + List categoryRecipes = await () { + if (category.name == "All") { + return recipesShortProvider.fetchRecipesShort(); + } else { + return categoryRecipesShortProvider + .fetchCategoryRecipesShort(category.name); + } + }(); category.imageUrl = categoryRecipes.first.imageUrl; From 0533ce84923eb17a152594ec778707580f8f6a5d Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 17:22:08 +0200 Subject: [PATCH 3/9] Added Padding + Correct Keyboard to login page --- lib/src/screens/form/login_form.dart | 83 +++++++++++++++------------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/lib/src/screens/form/login_form.dart b/lib/src/screens/form/login_form.dart index f22a227a..25241ce4 100644 --- a/lib/src/screens/form/login_form.dart +++ b/lib/src/screens/form/login_form.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; @@ -43,45 +44,49 @@ class _LoginFormState extends State { }, child: BlocBuilder( builder: (context, state) { - return Form( - // Build a Form widget using the _formKey created above. - key: _formKey, - child: Column( - children: [ - TextFormField( - decoration: InputDecoration(labelText: 'Server URL'), - controller: _serverUrl, - validator: (value) { - if (value.isEmpty) { - return 'Please enter a Nextcloud URL'; - } - var urlPattern = - r"([-A-Z0-9.]+)(/[-A-Z0-9+&@#/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#/%=~_|!:‌​,.;]*)?"; - bool _match = new RegExp(urlPattern, caseSensitive: false) - .hasMatch(value); - if (!_match) { - return 'Please enter a valid URL'; - } - return null; - }, - onFieldSubmitted: (val) { - if (state is! LoginLoading) { - _onLoginButtonPressed(); - } - }, - textInputAction: TextInputAction.done, - ), - RaisedButton( - onPressed: - state is! LoginLoading ? _onLoginButtonPressed : null, - child: Text('Login'), - ), - Container( - child: state is LoginLoading - ? SpinKitWave(color: Colors.blue, size: 50.0) - : null, - ), - ], + return Padding( + padding: const EdgeInsets.all(8.0), + child: Form( + // Build a Form widget using the _formKey created above. + key: _formKey, + child: Column( + children: [ + TextFormField( + decoration: InputDecoration(labelText: 'Server URL'), + controller: _serverUrl, + keyboardType: TextInputType.url, + validator: (value) { + if (value.isEmpty) { + return 'Please enter a Nextcloud URL'; + } + var urlPattern = + r"([-A-Z0-9.]+)(/[-A-Z0-9+&@#/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#/%=~_|!:‌​,.;]*)?"; + bool _match = new RegExp(urlPattern, caseSensitive: false) + .hasMatch(value); + if (!_match) { + return 'Please enter a valid URL'; + } + return null; + }, + onFieldSubmitted: (val) { + if (state is! LoginLoading) { + _onLoginButtonPressed(); + } + }, + textInputAction: TextInputAction.done, + ), + RaisedButton( + onPressed: + state is! LoginLoading ? _onLoginButtonPressed : null, + child: Text('Login'), + ), + Container( + child: state is LoginLoading + ? SpinKitWave(color: Colors.blue, size: 50.0) + : null, + ), + ], + ), ), ); }, From c39342bea845b5bbc3cb6e4cee5a461ff5201303 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 17:33:58 +0200 Subject: [PATCH 4/9] Closes #3 Moved Authentication to dedicated provider! --- lib/src/blocs/login/login_bloc.dart | 9 +- lib/src/services/authentication_provider.dart | 104 ++++++++++++++++++ lib/src/services/categories_provider.dart | 2 +- .../category_recipes_short_provider.dart | 2 +- lib/src/services/recipe_provider.dart | 4 +- lib/src/services/recipes_short_provider.dart | 5 +- lib/src/services/user_repository.dart | 96 +++------------- .../authentication_cached_network_image.dart | 2 +- 8 files changed, 129 insertions(+), 95 deletions(-) create mode 100644 lib/src/services/authentication_provider.dart diff --git a/lib/src/blocs/login/login_bloc.dart b/lib/src/blocs/login/login_bloc.dart index ddf699af..4d2d0782 100644 --- a/lib/src/blocs/login/login_bloc.dart +++ b/lib/src/blocs/login/login_bloc.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import 'package:meta/meta.dart'; import 'package:bloc/bloc.dart'; +import 'package:meta/meta.dart'; import 'package:nextcloud_cookbook_flutter/src/blocs/authentication/authentication.dart'; -import '../../services/user_repository.dart'; +import '../../services/user_repository.dart'; import 'login.dart'; class LoginBloc extends Bloc { @@ -23,9 +23,8 @@ class LoginBloc extends Bloc { yield LoginLoading(); try { - final appAuthentication = await userRepository.authenticate( - serverUrl: event.serverURL - ); + final appAuthentication = + await userRepository.authenticate(event.serverURL); authenticationBloc.add(LoggedIn(appAuthentication: appAuthentication)); yield LoginInitial(); diff --git a/lib/src/services/authentication_provider.dart b/lib/src/services/authentication_provider.dart new file mode 100644 index 00000000..de33a7ca --- /dev/null +++ b/lib/src/services/authentication_provider.dart @@ -0,0 +1,104 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:http/http.dart' as http; +import 'package:nextcloud_cookbook_flutter/src/models/app_authentication.dart'; +import 'package:nextcloud_cookbook_flutter/src/models/intial_login.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class AuthenticationProvider { + final FlutterSecureStorage _secureStorage = new FlutterSecureStorage(); + final String _appAuthenticationKey = 'appAuthentication'; + AppAuthentication currentAppAuthentication; + + Future authenticate({ + @required String serverUrl, + }) async { + if (serverUrl.substring(0, 4) != 'http') { + serverUrl = 'https://' + serverUrl; + } + String urlInitialCall = serverUrl + '/index.php/login/v2'; + var response; + try { + response = await http.post(urlInitialCall, + headers: {"User-Agent": "Cookbook App", "Accept-Language": "en-US"}); + } catch (e) { + throw ('Cannot reach: $serverUrl'); + } + + if (response.statusCode == 200) { + final initialLogin = InitialLogin.fromJson(json.decode(response.body)); + + if (await canLaunch(initialLogin.login)) { + Future _launched = _launchURL(initialLogin.login); + + String urlLoginSuccess = + initialLogin.poll.endpoint + "?token=" + initialLogin.poll.token; + //TODO add when users goes back + + var responseLog = await http.post(urlLoginSuccess); + while (responseLog.statusCode != 200) { + //TODO check if time is good + // I think this is no a correct usage of the Timer. We cold use Timer.periodic. But it should call the function. + Timer(const Duration(milliseconds: 500), () {}); + responseLog = await http.post(urlLoginSuccess); + } + + await closeWebView(); + + return AppAuthentication.fromJson(responseLog.body); + } else { + //TODO throw good errror + throw 'Could not launchsade'; + } + } else { + //TODO Catch Errors + throw Exception('Your server Name is not correct'); + } + } + + Future hasAppAuthentication() async { + if (currentAppAuthentication != null) { + return true; + } else { + String appAuthentication = + await _secureStorage.read(key: _appAuthenticationKey); + return appAuthentication != null; + } + } + + Future loadAppAuthentication() async { + String appAuthenticationString = + await _secureStorage.read(key: _appAuthenticationKey); + if (appAuthenticationString == null) { + throw ("No authentication found in Storage"); + } else { + currentAppAuthentication = + AppAuthentication.fromJson(appAuthenticationString); + } + } + + Future persistAppAuthentication( + AppAuthentication appAuthentication) async { + currentAppAuthentication = appAuthentication; + await _secureStorage.write( + key: _appAuthenticationKey, value: appAuthentication.toJson()); + } + + Future deleteAppAuthentication() async { + //TODO Delete Appkey Serverside + currentAppAuthentication = null; + await _secureStorage.delete(key: _appAuthenticationKey); + } + + Future _launchURL(String url) async { + await launch( + url, + forceSafariVC: true, + forceWebView: true, + enableJavaScript: true, + ); + } +} diff --git a/lib/src/services/categories_provider.dart b/lib/src/services/categories_provider.dart index 52552f50..9a51d620 100644 --- a/lib/src/services/categories_provider.dart +++ b/lib/src/services/categories_provider.dart @@ -8,7 +8,7 @@ class CategoriesProvider { Future> fetchCategories() async { AppAuthentication appAuthentication = - UserRepository().currentAppAuthentication; + UserRepository().getCurrentAppAuthentication(); final response = await client.get( "${appAuthentication.server}/index.php/apps/cookbook/categories", diff --git a/lib/src/services/category_recipes_short_provider.dart b/lib/src/services/category_recipes_short_provider.dart index 497a647b..23836897 100644 --- a/lib/src/services/category_recipes_short_provider.dart +++ b/lib/src/services/category_recipes_short_provider.dart @@ -8,7 +8,7 @@ class CategoryRecipesShortProvider { Future> fetchCategoryRecipesShort(String category) async { AppAuthentication appAuthentication = - UserRepository().currentAppAuthentication; + UserRepository().getCurrentAppAuthentication(); final response = await client.get( "${appAuthentication.server}/index.php/apps/cookbook/category/$category", diff --git a/lib/src/services/recipe_provider.dart b/lib/src/services/recipe_provider.dart index d33010e9..8a200a2a 100644 --- a/lib/src/services/recipe_provider.dart +++ b/lib/src/services/recipe_provider.dart @@ -9,7 +9,7 @@ class RecipeProvider { Future fetchRecipe(int id) async { AppAuthentication appAuthentication = - UserRepository().currentAppAuthentication; + UserRepository().getCurrentAppAuthentication(); final response = await client.get( "${appAuthentication.server}/index.php/apps/cookbook/api/recipes/$id", @@ -31,7 +31,7 @@ class RecipeProvider { Future updateRecipe(Recipe recipe) async { AppAuthentication appAuthentication = - UserRepository().currentAppAuthentication; + UserRepository().getCurrentAppAuthentication(); try { var response = await Dio().put( diff --git a/lib/src/services/recipes_short_provider.dart b/lib/src/services/recipes_short_provider.dart index 47d3eb3a..f0e435fe 100644 --- a/lib/src/services/recipes_short_provider.dart +++ b/lib/src/services/recipes_short_provider.dart @@ -3,11 +3,12 @@ import 'package:nextcloud_cookbook_flutter/src/models/app_authentication.dart'; import 'package:nextcloud_cookbook_flutter/src/models/recipe_short.dart'; import 'package:nextcloud_cookbook_flutter/src/services/user_repository.dart'; -class RecipesShortProvider { +class RecipesShortProvider { Client client = Client(); Future> fetchRecipesShort() async { - AppAuthentication appAuthentication = UserRepository().currentAppAuthentication; + AppAuthentication appAuthentication = + UserRepository().getCurrentAppAuthentication(); final response = await client.get( "${appAuthentication.server}/index.php/apps/cookbook/api/recipes", diff --git a/lib/src/services/user_repository.dart b/lib/src/services/user_repository.dart index 7698c455..351089f8 100644 --- a/lib/src/services/user_repository.dart +++ b/lib/src/services/user_repository.dart @@ -1,17 +1,10 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:nextcloud_cookbook_flutter/src/services/authentication_provider.dart'; import '../models/app_authentication.dart'; -import '../models/intial_login.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:http/http.dart' as http; - -import 'dart:convert' show json; -import 'package:url_launcher/url_launcher.dart'; class UserRepository { - // Singleton static final UserRepository _userRepository = UserRepository._internal(); factory UserRepository() { @@ -19,93 +12,30 @@ class UserRepository { } UserRepository._internal(); - final FlutterSecureStorage _secureStorage = new FlutterSecureStorage(); - final String _appAuthenticationKey = 'appAuthentication'; - AppAuthentication currentAppAuthentication; - - Future authenticate({ - @required String serverUrl, - }) async { - - if (serverUrl.substring(0,4) != 'http') { - serverUrl = 'https://'+serverUrl; - } - String urlInitialCall = serverUrl+'/index.php/login/v2'; - var response; - try { - response = await http.post(urlInitialCall, headers: {"User-Agent":"Cookbook App", "Accept-Language":"en-US"}); - } catch (e) { - throw ('Cannot reach: $serverUrl') ; - } - - if (response.statusCode == 200) { - final initialLogin = InitialLogin.fromJson(json.decode(response.body)); - - if (await canLaunch(initialLogin.login)) { + AuthenticationProvider authenticationProvider = AuthenticationProvider(); - Future _launched = _launchURL(initialLogin.login); - - String urlLoginSuccess = initialLogin.poll.endpoint + "?token=" + initialLogin.poll.token; - //TODO add when users goes back - - var responseLog = await http.post(urlLoginSuccess); - while (responseLog.statusCode != 200) { - //TODO check if time is good - // I think this is no a correct usage of the Timer. We cold use Timer.periodic. But it should call the function. - Timer(const Duration(milliseconds: 500), () { - }); - responseLog = await http.post(urlLoginSuccess); - } - - await closeWebView(); + Future authenticate(String serverUrl) async { + return authenticationProvider.authenticate(serverUrl: serverUrl); + } - return AppAuthentication.fromJson(responseLog.body); - } else { - //TODO throw good errror - throw 'Could not launchsade'; - } - } else{ - //TODO Catch Errors - throw Exception('Your server Name is not correct'); - } + AppAuthentication getCurrentAppAuthentication() { + return authenticationProvider.currentAppAuthentication; } Future hasAppAuthentication() async { - if (currentAppAuthentication != null) { - return true; - } else { - String appAuthentication = await _secureStorage.read(key: _appAuthenticationKey); - return appAuthentication != null; - } + return authenticationProvider.hasAppAuthentication(); } Future loadAppAuthentication() async { - String appAuthenticationString = await _secureStorage.read(key: _appAuthenticationKey); - if (appAuthenticationString == null) { - throw("No authentication found in Storage"); - } else{ - currentAppAuthentication = AppAuthentication.fromJson(appAuthenticationString); - } + return authenticationProvider.loadAppAuthentication(); } - Future persistAppAuthentication(AppAuthentication appAuthentication) async { - currentAppAuthentication = appAuthentication; - await _secureStorage.write(key: _appAuthenticationKey, value: appAuthentication.toJson()); + Future persistAppAuthentication( + AppAuthentication appAuthentication) async { + return authenticationProvider.persistAppAuthentication(appAuthentication); } Future deleteAppAuthentication() async { - //TODO Delete Appkey Serverside - currentAppAuthentication = null; - await _secureStorage.delete(key: _appAuthenticationKey); + return authenticationProvider.deleteAppAuthentication(); } } - -// TODO: Move this to more appropriate position -Future _launchURL( String url) async { - await launch( - url, - forceSafariVC: true, - forceWebView: true, - enableJavaScript: true, - ); -} diff --git a/lib/src/widget/authentication_cached_network_image.dart b/lib/src/widget/authentication_cached_network_image.dart index de75ccc0..d366b2a5 100644 --- a/lib/src/widget/authentication_cached_network_image.dart +++ b/lib/src/widget/authentication_cached_network_image.dart @@ -15,7 +15,7 @@ class AuthenticationCachedNetworkImage extends StatelessWidget { @override Widget build(BuildContext context) { AppAuthentication appAuthentication = - UserRepository().currentAppAuthentication; + UserRepository().getCurrentAppAuthentication(); return CachedNetworkImage( width: width, From a0e295ee63040eb1f5bb853eb5c117ab63691260 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 18:46:27 +0200 Subject: [PATCH 5/9] Stopping Login midway no longer ends in endless waiting state --- lib/src/blocs/login/login_event.dart | 10 ++++--- lib/src/screens/form/login_form.dart | 29 ++++++++++++++++++- lib/src/services/authentication_provider.dart | 25 +++++++++------- lib/src/services/user_repository.dart | 4 +++ 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/lib/src/blocs/login/login_event.dart b/lib/src/blocs/login/login_event.dart index d3b3a6df..8b48215f 100644 --- a/lib/src/blocs/login/login_event.dart +++ b/lib/src/blocs/login/login_event.dart @@ -1,8 +1,11 @@ -import 'package:meta/meta.dart'; import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; abstract class LoginEvent extends Equatable { const LoginEvent(); + + @override + List get props => []; } class LoginButtonPressed extends LoginEvent { @@ -16,6 +19,5 @@ class LoginButtonPressed extends LoginEvent { List get props => [serverURL]; @override - String toString() => - 'LoginButtonPressed {serverURL: $serverURL}'; -} \ No newline at end of file + String toString() => 'LoginButtonPressed {serverURL: $serverURL}'; +} diff --git a/lib/src/screens/form/login_form.dart b/lib/src/screens/form/login_form.dart index 25241ce4..48792b18 100644 --- a/lib/src/screens/form/login_form.dart +++ b/lib/src/screens/form/login_form.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:nextcloud_cookbook_flutter/src/services/user_repository.dart'; import '../../blocs/login/login.dart'; @@ -10,7 +11,7 @@ class LoginForm extends StatefulWidget { State createState() => _LoginFormState(); } -class _LoginFormState extends State { +class _LoginFormState extends State with WidgetsBindingObserver { final _serverUrl = TextEditingController(); // Create a global key that uniquely identifies the Form widget // and allows validation of the form. @@ -19,8 +20,34 @@ class _LoginFormState extends State { // not a GlobalKey. final _formKey = GlobalKey(); + Function authenticateInterruptCallback; + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + authenticateInterruptCallback(); + debugPrint("WAT"); + } + } + + @override + initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + @override Widget build(BuildContext context) { + authenticateInterruptCallback = () { + UserRepository().stopAuthenticate(); + }; + _onLoginButtonPressed() { if (_formKey.currentState.validate()) { BlocProvider.of(context).add( diff --git a/lib/src/services/authentication_provider.dart b/lib/src/services/authentication_provider.dart index de33a7ca..c1ed588e 100644 --- a/lib/src/services/authentication_provider.dart +++ b/lib/src/services/authentication_provider.dart @@ -12,10 +12,12 @@ class AuthenticationProvider { final FlutterSecureStorage _secureStorage = new FlutterSecureStorage(); final String _appAuthenticationKey = 'appAuthentication'; AppAuthentication currentAppAuthentication; + bool resumeAuthenticate = true; Future authenticate({ @required String serverUrl, }) async { + resumeAuthenticate = true; if (serverUrl.substring(0, 4) != 'http') { serverUrl = 'https://' + serverUrl; } @@ -32,33 +34,36 @@ class AuthenticationProvider { final initialLogin = InitialLogin.fromJson(json.decode(response.body)); if (await canLaunch(initialLogin.login)) { - Future _launched = _launchURL(initialLogin.login); + _launchURL(initialLogin.login); String urlLoginSuccess = initialLogin.poll.endpoint + "?token=" + initialLogin.poll.token; - //TODO add when users goes back var responseLog = await http.post(urlLoginSuccess); - while (responseLog.statusCode != 200) { - //TODO check if time is good - // I think this is no a correct usage of the Timer. We cold use Timer.periodic. But it should call the function. - Timer(const Duration(milliseconds: 500), () {}); + while (responseLog.statusCode != 200 && resumeAuthenticate) { + await Future.delayed(Duration(milliseconds: 100)); responseLog = await http.post(urlLoginSuccess); } await closeWebView(); - return AppAuthentication.fromJson(responseLog.body); + if (responseLog.statusCode != 200) { + throw "Login Process was interrupted!"; + } else { + return AppAuthentication.fromJson(responseLog.body); + } } else { - //TODO throw good errror - throw 'Could not launchsade'; + throw 'Could not launch the authentication window.'; } } else { - //TODO Catch Errors throw Exception('Your server Name is not correct'); } } + void stopAuthenticate() { + resumeAuthenticate = false; + } + Future hasAppAuthentication() async { if (currentAppAuthentication != null) { return true; diff --git a/lib/src/services/user_repository.dart b/lib/src/services/user_repository.dart index 351089f8..058366f3 100644 --- a/lib/src/services/user_repository.dart +++ b/lib/src/services/user_repository.dart @@ -18,6 +18,10 @@ class UserRepository { return authenticationProvider.authenticate(serverUrl: serverUrl); } + void stopAuthenticate() { + authenticationProvider.stopAuthenticate(); + } + AppAuthentication getCurrentAppAuthentication() { return authenticationProvider.currentAppAuthentication; } From 24f1c7488de4889c26cc86cf8e4b1ea80227fdab Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 21:50:19 +0200 Subject: [PATCH 6/9] Remove remote apptoken --- lib/src/models/app_authentication.dart | 12 +++++++++--- lib/src/services/authentication_provider.dart | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/src/models/app_authentication.dart b/lib/src/models/app_authentication.dart index ea854df8..f01fbf4a 100644 --- a/lib/src/models/app_authentication.dart +++ b/lib/src/models/app_authentication.dart @@ -15,8 +15,14 @@ class AppAuthentication { factory AppAuthentication.fromJson(String jsonString) { Map jsonData = json.decode(jsonString); - String basicAuth = jsonData.containsKey("basicAuth") ? jsonData['basicAuth'] : - 'Basic '+base64Encode(utf8.encode('${jsonData["loginName"]}:${jsonData["appPassword"]}')); + String basicAuth = jsonData.containsKey("basicAuth") + ? jsonData['basicAuth'] + : 'Basic ' + + base64Encode( + utf8.encode( + '${jsonData["loginName"]}:${jsonData["appPassword"]}', + ), + ); return AppAuthentication( server: jsonData["server"], @@ -35,4 +41,4 @@ class AppAuthentication { @override String toString() => 'LoggedIn { token: $server, $loginName, $basicAuth}'; -} \ No newline at end of file +} diff --git a/lib/src/services/authentication_provider.dart b/lib/src/services/authentication_provider.dart index c1ed588e..a7cb39de 100644 --- a/lib/src/services/authentication_provider.dart +++ b/lib/src/services/authentication_provider.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:dio/dio.dart' as dio; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:http/http.dart' as http; @@ -93,6 +94,20 @@ class AuthenticationProvider { } Future deleteAppAuthentication() async { + var response = await dio.Dio().delete( + "${currentAppAuthentication.server}/ocs/v2.php/core/apppassword", + options: new dio.Options( + headers: { + "OCS-APIREQUEST": "true", + "authorization": currentAppAuthentication.basicAuth + }, + ), + ); + + if (response.statusCode != 200) { + debugPrint("Failed to remove remote apppassword!"); + } + //TODO Delete Appkey Serverside currentAppAuthentication = null; await _secureStorage.delete(key: _appAuthenticationKey); From 0258cfa7c8a200eedeb0b41626f41aaaf55c9f7b Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 22:01:23 +0200 Subject: [PATCH 7/9] Added IzzyDroid Badge --- README.md | 5 ++++- assets/IzzyOnDroid.png | Bin 0 -> 20993 bytes 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 assets/IzzyOnDroid.png diff --git a/README.md b/README.md index 368dc9a9..a3eb8f57 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ ![GitHub](https://img.shields.io/github/license/Teifun2/nextcloud-cookbook-flutter) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Teifun2/nextcloud-cookbook-flutter) ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/Teifun2/nextcloud-cookbook-flutter/Build/master) +[![IzzyDorid](assets/IzzyOnDroid.png)(https://apt.izzysoft.de/fdroid/index/apk/com.nextcloud_cookbook_flutter)] # Nextcloud Cookbook Mobile Client written in Flutter -This project aims to provide a mobile client for both Android and IOs for the nextcloud plugin cookbook (https://github.com/nextcloud/cookbook) +This project aims to provide a mobile client for both Android and IOs for the nextcloud app cookbook (https://github.com/nextcloud/cookbook) + +It works best with an Nextcloud installation >= 17 ## Screenshots diff --git a/assets/IzzyOnDroid.png b/assets/IzzyOnDroid.png new file mode 100644 index 0000000000000000000000000000000000000000..af5bf5bdac29faa8f8acc07518fd5867bdf0f009 GIT binary patch literal 20993 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 Y%|lTp0i7XQuvsH=ifC(5X6AeCFS(pD;{X5v literal 0 HcmV?d00001 From dab83dd21b75bd50b706ea9c7d42b207047c683d Mon Sep 17 00:00:00 2001 From: Teifun2 Date: Sun, 6 Sep 2020 22:10:28 +0200 Subject: [PATCH 8/9] Update README.md Updated image sizes / fixed IzzyDroid Badge --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a3eb8f57..7c1278fe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![GitHub](https://img.shields.io/github/license/Teifun2/nextcloud-cookbook-flutter) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Teifun2/nextcloud-cookbook-flutter) ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/Teifun2/nextcloud-cookbook-flutter/Build/master) -[![IzzyDorid](assets/IzzyOnDroid.png)(https://apt.izzysoft.de/fdroid/index/apk/com.nextcloud_cookbook_flutter)] +[IzyyDroid](https://apt.izzysoft.de/fdroid/index/apk/com.nextcloud_cookbook_flutter) # Nextcloud Cookbook Mobile Client written in Flutter This project aims to provide a mobile client for both Android and IOs for the nextcloud app cookbook (https://github.com/nextcloud/cookbook) @@ -9,8 +9,7 @@ It works best with an Nextcloud installation >= 17 ## Screenshots -![Screenshot_categories](https://user-images.githubusercontent.com/7461832/91664922-899d6400-eaf2-11ea-8120-3222bd5b5363.png) -![Screenshot_category_list](https://user-images.githubusercontent.com/7461832/91664920-8904cd80-eaf2-11ea-9bb3-62e0b41f85c0.png) -![Screenshot_recipe_1](https://user-images.githubusercontent.com/7461832/91664918-873b0a00-eaf2-11ea-86a6-e30fde4c98a9.png) -![Screenshot_recipe_2](https://user-images.githubusercontent.com/7461832/91664923-8a35fa80-eaf2-11ea-9bfe-6ed8edc41b49.png) +Screenshot_categories Screenshot_category_list + +Screenshot_recipe_2 Screenshot_recipe_1 From 1b31363827cee3497a004fa27c5f6bb36af27607 Mon Sep 17 00:00:00 2001 From: Linard Schwendener Date: Sun, 6 Sep 2020 22:12:25 +0200 Subject: [PATCH 9/9] Updated Version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 1f29790c..ec6ec9b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: A new Flutter application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.3.2+8 +version: 0.3.3+9 environment: sdk: ">=2.6.0 <3.0.0"