Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implement some of the methods of the plugin.
  • Loading branch information
ditman committed Oct 22, 2019
commit b721c7a24f591b5f122a31ebcae9925647ca4683
110 changes: 87 additions & 23 deletions packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@ import 'dart:html' as html;

import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:google_sign_in_web/src/gapi.dart';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer to use relative imports for same-package imports (i.e. import 'src/gapi.dart';)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change this, but I don't know enough dart to have been bitten by any issue with package vs relative imports. Any reason for this?

I did this because I think I remember reading somewhere in the dart docs something along the lines "always prefer import from package:blah, because it always works" or similar :/

import 'package:js/js.dart';

const String _kClientIdMetaSelector = 'meta[name=google-signin-client_id]';
const String _kClientIdAttributeName = 'content';
const List<String> _kJsLibraries = <String>[
'https://apis.google.com/js/platform.js'
];

/// Implementation of the google_sign_in plugin for Web
class GoogleSignInPlugin {
GoogleSignInPlugin() {
_jsLibrariesLoading = _injectJSLibraries(<String>[
'https://apis.google.com/js/platform.js',
])..then(_initGapi);
_autoDetectedClientId = html
.querySelector(_kClientIdMetaSelector)
?.getAttribute(_kClientIdAttributeName);

_isGapiInitialized =
Future.wait(_injectJSLibraries(_kJsLibraries)).then(_initGapi);
}

Future<List<void>> _jsLibrariesLoading;
Future<void> _isGapiInitialized;
String _autoDetectedClientId;

Future<void> get isJsLoaded => _jsLibrariesLoading;

static void registerWith(Registrar registrar) {
final MethodChannel channel = MethodChannel(
'plugins.flutter.io/google_sign_in',
Expand All @@ -28,14 +38,35 @@ class GoogleSignInPlugin {

Future<dynamic> handleMethodCall(MethodCall call) async {
// Await for initialization promises to complete, then do your thing...
await isJsLoaded;
await _isGapiInitialized;

html.window.console.debug(call);

html.window.console.log('Doing things! $call');
final GoogleUser currentUser =
gapi.auth2.getAuthInstance()?.currentUser?.get();

switch (call.method) {
case 'init':
// Initialize the gapi
return _init(call.arguments);
case 'init': // void
return _init(call.arguments)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should be able to just return null

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think returning null breaks this logic, but that's what the java implementation is doing, ssssoooo null it is!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I think you're right about breaking that null-check logic. Maybe return true or something? Just wary of the toString() call just to return a signal that the initialization was successful

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like return true, I'll get that done. .toString rubs me the wrong way too.

However, the Java implementation returns a success-with-null; I guess null is being handled differently, OR the plugin itself makes sure not to call init multiple times.

.toString(); // Anything serializable, really!
break;
case 'signInSilently':
await _silentSignIn(currentUser, call.arguments);
return _currentUserToPluginMap(currentUser);
break;
case 'signIn':
await _signIn(call.arguments);
return _currentUserToPluginMap(currentUser);
break;
case 'disconnect':
currentUser.disconnect();
return null;
case 'getTokens':
final Auth2AuthResponse response = currentUser.getAuthResponse();
return <String, String>{
'idToken': response.id_token,
'accessToken': response.access_token,
};
break;
default:
throw PlatformException(
Expand All @@ -45,34 +76,67 @@ class GoogleSignInPlugin {
}
}

Map<String, dynamic> _currentUserToPluginMap(GoogleUser currentUser) {
assert(currentUser != null);
final Auth2BasicProfile profile = currentUser.getBasicProfile();
return <String, dynamic>{
'displayName': profile?.getName(),
'email': profile?.getEmail(),
'id': profile?.getId(),
'photoUrl': profile?.getImageUrl(),
'idToken': currentUser?.getAuthResponse()?.id_token,
};
}

// Load the auth2 library
Future<void> _init(Map<String, dynamic> arguments) {
//
GoogleAuth _init(dynamic arguments) => gapi.auth2.init(Auth2ClientConfig(
hostedDomain: arguments['hostedDomain'],
scope: arguments['scopes'].join(
' '), // The backend wants a space-separated list of values, not an array
clientId: arguments['clientId'] ?? _autoDetectedClientId,
));

Future<dynamic> _signIn(dynamic arguments) async {
return html.promiseToFuture<dynamic>(gapi.auth2.getAuthInstance().signIn());
}

Future<void> _initGapi(List<void> _) {
// JS-interop with the global gapi method and call gapi.load('auth2'), and wait for the
Future<dynamic> _silentSignIn(
GoogleUser currentUser, dynamic arguments) async {
return html.promiseToFuture<dynamic>(
gapi.auth2.getAuthInstance().signIn(Auth2SignInOptions(
prompt: 'none',
)));
}

Future<void> _initGapi(dynamic _) {
// JS-interop with the global gapi method and call gapi.load('auth2'), and wait for the
// promise to resolve...

final Completer<void> gapiLoadCompleter = Completer<void>();
gapi.load('auth2', allowInterop(() {
gapiLoadCompleter.complete();
}));

return gapiLoadCompleter
.future; // After this is resolved, we can use gapi.auth2!
}

/// Injects a bunch of libraries in the <head> and returns a
/// Injects a bunch of libraries in the <head> and returns a
/// Future that resolves when all load.
Future<List<void>> _injectJSLibraries(List<String> libraries, { Duration timeout }) {
final List<Future<void>> loading = <Future<void>>[
Future<bool>.delayed(const Duration(seconds: 10), () => true) // TODO: Remove this delay before submitting :)
];
List<Future<void>> _injectJSLibraries(List<String> libraries,
{Duration timeout}) {
final List<Future<void>> loading = <Future<void>>[];
final List<html.HtmlElement> tags = <html.HtmlElement>[];

libraries.forEach((String library) {
final html.ScriptElement script = html.ScriptElement()
..async = true
..defer = true
..src = library;
loading.add(script.onLoad.first); // TODO add a timeout race to fail this future
loading.add(
script.onLoad.first); // TODO add a timeout race to fail this future
tags.add(script);
});
html.querySelector('head').children.addAll(tags);
return Future.wait(loading);
return loading;
}
}
1 change: 1 addition & 0 deletions packages/google_sign_in/google_sign_in_web/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies:
flutter_web_plugins:
sdk: flutter
meta: ^1.1.7
js: any

dev_dependencies:
flutter_test:
Expand Down