diff --git a/.gitignore b/.gitignore index ce8590362..63fdac3bb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ build/ .flutter-plugins .env.dart -key.properties \ No newline at end of file +key.properties +google-services.json \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 3d087b868..287a7a16e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -12,6 +12,7 @@ if (flutterRoot == null) { } apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" def keystorePropertiesFile = rootProject.file("key.properties") diff --git a/android/build.gradle b/android/build.gradle index 447688755..ae1fe01b5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,6 +6,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.google.gms:google-services:3.2.1' } } diff --git a/lib/ui/app/invoice/invoice_email_vm.dart b/lib/ui/app/invoice/invoice_email_vm.dart index 0f8e2e0db..2e897dd8f 100644 --- a/lib/ui/app/invoice/invoice_email_vm.dart +++ b/lib/ui/app/invoice/invoice_email_vm.dart @@ -5,7 +5,6 @@ import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; import 'package:invoiceninja_flutter/ui/app/invoice/invoice_email_view.dart'; import 'package:invoiceninja_flutter/utils/completers.dart'; -import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:redux/redux.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; diff --git a/lib/ui/auth/login.dart b/lib/ui/auth/login.dart index f991eda88..009f40727 100644 --- a/lib/ui/auth/login.dart +++ b/lib/ui/auth/login.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:invoiceninja_flutter/redux/auth/auth_state.dart'; +import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart'; import 'package:invoiceninja_flutter/ui/app/progress_button.dart'; +import 'package:invoiceninja_flutter/ui/auth/login_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart'; @@ -9,15 +10,11 @@ import 'package:invoiceninja_flutter/utils/keys.dart'; class LoginView extends StatefulWidget { - final bool isLoading; - final AuthState authState; - final Function(BuildContext, String, String, String, String) onLoginPressed; + final LoginVM viewModel; const LoginView({ Key key, - @required this.isLoading, - @required this.authState, - @required this.onLoginPressed, + @required this.viewModel, }) : super(key: key); @override @@ -40,7 +37,7 @@ class _LoginState extends State { @override void didChangeDependencies() { - final state = widget.authState; + final state = widget.viewModel.authState; _emailController.text = state.email; _passwordController.text = state.password; _urlController.text = formatApiUrlReadable(state.url); @@ -62,7 +59,10 @@ class _LoginState extends State { @override Widget build(BuildContext context) { - if (!widget.authState.isInitialized) { + final localization = AppLocalization.of(context); + final viewModel = widget.viewModel; + + if (!viewModel.authState.isInitialized) { return Container(); } @@ -83,10 +83,10 @@ class _LoginState extends State { key: _emailKey, autocorrect: false, decoration: InputDecoration( - labelText: AppLocalization.of(context).email), + labelText: localization.email), keyboardType: TextInputType.emailAddress, validator: (val) => val.isEmpty || val.trim().isEmpty - ? AppLocalization.of(context).pleaseEnterYourEmail + ? localization.pleaseEnterYourEmail : null, ), TextFormField( @@ -94,9 +94,9 @@ class _LoginState extends State { key: _passwordKey, autocorrect: false, decoration: InputDecoration( - labelText: AppLocalization.of(context).password), + labelText: localization.password), validator: (val) => val.isEmpty || val.trim().isEmpty - ? AppLocalization.of(context).pleaseEnterYourPassword + ? localization.pleaseEnterYourPassword : null, obscureText: true, ), @@ -105,9 +105,9 @@ class _LoginState extends State { key: _urlKey, autocorrect: false, decoration: - InputDecoration(labelText: AppLocalization.of(context).url), + InputDecoration(labelText: localization.url), validator: (val) => val.isEmpty || val.trim().isEmpty - ? AppLocalization.of(context).pleaseEnterYourUrl + ? localization.pleaseEnterYourUrl : null, keyboardType: TextInputType.url, ), @@ -116,21 +116,16 @@ class _LoginState extends State { key: _secretKey, autocorrect: false, decoration: InputDecoration( - labelText: AppLocalization.of(context).secret), - /* - validator: (val) => val.isEmpty || val.trim().length == 0 - ? AppLocalization.of(context).pleaseEnterYourPassword - : null, - */ + labelText: localization.secret), obscureText: true, ), - widget.authState.error == null + viewModel.authState.error == null ? Container() : Container( padding: EdgeInsets.only(top: 26.0, bottom: 4.0), child: Center( child: Text( - widget.authState.error, + viewModel.authState.error, style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, @@ -142,13 +137,13 @@ class _LoginState extends State { ), ), ProgressButton( - label: AppLocalization.of(context).login.toUpperCase(), - isLoading: widget.isLoading, + label: localization.login.toUpperCase(), + isLoading: viewModel.isLoading, onPressed: () { if (!_formKey.currentState.validate()) { return; } - widget.onLoginPressed( + viewModel.onLoginPressed( context, _emailController.text, _passwordController.text, @@ -156,6 +151,13 @@ class _LoginState extends State { _secretController.text); }, ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ElevatedButton( + label: 'Google ${localization.login}'.toUpperCase(), + onPressed: () => viewModel.onGoogleLoginPressed(context), + ), + ), ], ); } diff --git a/lib/ui/auth/login_vm.dart b/lib/ui/auth/login_vm.dart index d25de41df..3d53b7139 100644 --- a/lib/ui/auth/login_vm.dart +++ b/lib/ui/auth/login_vm.dart @@ -11,6 +11,7 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/ui/auth/login.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_state.dart'; +import 'package:google_sign_in/google_sign_in.dart'; class LoginScreen extends StatelessWidget { const LoginScreen({Key key}) : super(key: key); @@ -22,11 +23,9 @@ class LoginScreen extends StatelessWidget { return Scaffold( body: StoreConnector( converter: LoginVM.fromStore, - builder: (context, vm) { + builder: (context, viewModel) { return LoginView( - isLoading: vm.isLoading, - authState: vm.authState, - onLoginPressed: vm.onLoginPressed, + viewModel: viewModel, ); }, ), @@ -38,31 +37,68 @@ class LoginVM { bool isLoading; AuthState authState; final Function(BuildContext, String, String, String, String) onLoginPressed; + final Function(BuildContext) onGoogleLoginPressed; LoginVM({ @required this.isLoading, @required this.authState, @required this.onLoginPressed, + @required this.onGoogleLoginPressed, }); static LoginVM fromStore(Store store) { + final GoogleSignIn _googleSignIn = new GoogleSignIn( + scopes: [ + 'email', + 'openid', + 'profile', + ], + ); + + _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) { + print('gogole account: $account'); + + if (account != null) { + account.authentication.then((GoogleSignInAuthentication value) { + print('token: ${value.idToken}'); + print('token: ${value.accessToken}'); + }); + } + }); + + //_googleSignIn.signInSilently(); + return LoginVM( isLoading: store.state.isLoading, authState: store.state.authState, + onGoogleLoginPressed: (context) async { + try { + final result = await _googleSignIn.signIn(); + print(result); + } catch (error) { + print(error); + } + }, onLoginPressed: (BuildContext context, String email, String password, - String url, String secret) { + String url, String secret) async { + try { + await _googleSignIn.signIn(); + } catch (error) { + print(error); + } + if (store.state.isLoading) { return; } final Completer completer = Completer(); store.dispatch(UserLoginRequest( - completer: completer, - email: email.trim(), - password: password.trim(), - url: url.trim(), - secret: secret.trim(), - platform: getPlatform(context), + completer: completer, + email: email.trim(), + password: password.trim(), + url: url.trim(), + secret: secret.trim(), + platform: getPlatform(context), )); completer.future.then((_) { Navigator.of(context).pushReplacementNamed(DashboardScreen.route); diff --git a/pubspec.lock b/pubspec.lock index 1632b6bd8..afb42f30d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -162,6 +162,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.1" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.18" fixnum: dependency: transitive description: @@ -199,7 +206,7 @@ packages: name: flutter_html_view url: "https://pub.dartlang.org" source: hosted - version: "0.5.3" + version: "0.5.4" flutter_localizations: dependency: "direct main" description: flutter @@ -252,6 +259,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.7" + google_sign_in: + dependency: "direct main" + description: + name: google_sign_in + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4" graphs: dependency: transitive description: @@ -620,7 +634,7 @@ packages: name: video_player url: "https://pub.dartlang.org" source: hosted - version: "0.5.4" + version: "0.6.4" vm_service_client: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 25ed6ef23..b54eb5fbf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,8 @@ dependencies: intl: ^0.15.6 flutter_pdf_viewer: ^0.0.2 flutter_html_view: ^0.5.2 + google_sign_in: ^3.0.4 + firebase_auth: ^0.5.14 dev_dependencies: flutter_driver: