From 5e81fd5d670b4b5583983ac8081b46c29249afcf Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Fri, 8 Dec 2023 17:07:05 -0800 Subject: [PATCH] [google_sign_in_web] Migrate to pkg:web. (#5612) This PR updates `google_sign_in_web` to: * Use the latest `google_identity_services_web` and `http` * Use `package:web` This should allow the package to compile to WASM. ## Issues * Fixes https://github.com/flutter/flutter/issues/139169 * Needed to https://github.com/flutter/flutter/issues/139170 ## Tests * All tests should pass (ran locally) * Demo app compiled to WASM: https://dit-gis-test.web.app --- .../google_sign_in_web/CHANGELOG.md | 5 + .../flexible_size_html_element_view_test.dart | 25 ++- .../google_sign_in_web_test.dart | 8 +- .../example/integration_test/src/dom.dart | 70 ------ .../integration_test/src/jsify_as.dart | 4 +- .../google_sign_in_web/example/pubspec.yaml | 8 +- .../lib/google_sign_in_web.dart | 19 +- .../lib/src/button_configuration.dart | 22 +- .../google_sign_in_web/lib/src/dom.dart | 211 ------------------ .../src/flexible_size_html_element_view.dart | 58 +++-- .../lib/src/gis_client.dart | 71 +++--- .../google_sign_in_web/pubspec.yaml | 10 +- script/configs/exclude_all_packages_app.yaml | 3 - 13 files changed, 125 insertions(+), 389 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in_web/example/integration_test/src/dom.dart delete mode 100644 packages/google_sign_in/google_sign_in_web/lib/src/dom.dart diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 7dcf4e5853..b11c6249a5 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.3 + +* Migrates to `package:web`. +* Updates minimum supported SDK version to Flutter 3.16.0/Dart 3.2.0. + ## 0.12.2+1 * Re-publishes `0.12.2` with a small fix to the CodeClient initialization. diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/flexible_size_html_element_view_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/flexible_size_html_element_view_test.dart index 555b1711bd..23ff99ac30 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/flexible_size_html_element_view_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/flexible_size_html_element_view_test.dart @@ -9,8 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_web/src/flexible_size_html_element_view.dart'; import 'package:integration_test/integration_test.dart'; - -import 'src/dom.dart'; +import 'package:web/web.dart' as web; /// Used to keep track of the number of HtmlElementView factories the test has registered. int widgetFactoryNumber = 0; @@ -54,7 +53,8 @@ void main() { (WidgetTester tester) async { const Size childSize = Size(300, 40); - final DomHtmlElement resizable = document.createElement('div'); + final web.HTMLDivElement resizable = + web.document.createElement('div') as web.HTMLDivElement; resize(resizable, childSize); final Element element = await pumpResizableWidget( @@ -73,7 +73,8 @@ void main() { const Size initialSize = Size(160, 100); const Size newSize = Size(300, 40); - final DomHtmlElement resizable = document.createElement('div'); + final web.HTMLDivElement resizable = + web.document.createElement('div') as web.HTMLDivElement; resize(resizable, newSize); final Element element = await pumpResizableWidget( @@ -94,7 +95,8 @@ void main() { final Size expandedSize = initialSize * 2; final Size contractedSize = initialSize / 2; - final DomHtmlElement resizable = document.createElement('div') + final web.HTMLDivElement resizable = web.document.createElement('div') + as web.HTMLDivElement ..setAttribute( 'style', 'width: 100%; height: 100%; background: #fabada;'); @@ -160,7 +162,8 @@ class ResizableFromJs extends StatelessWidget { ui_web.platformViewRegistry.registerViewFactory( 'resizable_from_js_$instanceId', (int viewId) { - final DomHtmlElement element = document.createElement('div'); + final web.HTMLDivElement element = + web.document.createElement('div') as web.HTMLDivElement; element.setAttribute('style', 'width: 100%; height: 100%; overflow: hidden; background: red;'); element.id = 'test_element_$viewId'; @@ -191,16 +194,16 @@ class ResizableFromJs extends StatelessWidget { } /// Resizes `resizable` to `size`. -void resize(DomHtmlElement resizable, Size size) { +void resize(web.HTMLElement resizable, Size size) { resizable.setAttribute('style', 'width: ${size.width}px; height: ${size.height}px; background: #fabada'); } /// Returns a function that can be used to inject `element` in `onPlatformViewCreated` callbacks. -void Function(int) injectElement(DomHtmlElement element) { +void Function(int) injectElement(web.HTMLElement element) { return (int viewId) { - final DomHtmlElement root = - document.querySelector('#test_element_$viewId')!; - root.appendChild(element); + final web.Element? root = + web.document.querySelector('#test_element_$viewId'); + root!.appendChild(element); }; } diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/google_sign_in_web_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/google_sign_in_web_test.dart index e7c7c75739..1b5f11d2f9 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/google_sign_in_web_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/google_sign_in_web_test.dart @@ -13,9 +13,9 @@ import 'package:google_sign_in_web/src/people.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart' as mockito; +import 'package:web/web.dart' as web; import 'google_sign_in_web_test.mocks.dart'; -import 'src/dom.dart'; import 'src/person.dart'; // Mock GisSdkClient so we can simulate any response from the JS side. @@ -36,12 +36,12 @@ void main() { expect(plugin.autoDetectedClientId, isNull); // Add it to the test page now, and try again - final DomHtmlMetaElement meta = - document.createElement('meta') as DomHtmlMetaElement + final web.HTMLMetaElement meta = + web.document.createElement('meta') as web.HTMLMetaElement ..name = clientIdMetaName ..content = expectedClientId; - document.head.appendChild(meta); + web.document.head!.appendChild(meta); final GoogleSignInPlugin another = GoogleSignInPlugin( debugOverrideLoader: true, diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/dom.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/dom.dart deleted file mode 100644 index 58bbe2138d..0000000000 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/dom.dart +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* -// DOM shim. This file contains everything we need from the DOM API written as -// @staticInterop, so we don't need dart:html -// https://developer.mozilla.org/en-US/docs/Web/API/ -// -// (To be replaced by `package:web`) -*/ - -import 'package:js/js.dart'; - -/// Document interface -@JS() -@staticInterop -abstract class DomHtmlDocument {} - -/// Some methods of document -extension DomHtmlDocumentExtension on DomHtmlDocument { - /// document.head - external DomHtmlElement get head; - - /// document.createElement - external DomHtmlElement createElement(String tagName); - - /// document.querySelector - external DomHtmlElement? querySelector(String selector); -} - -/// An instance of an HTMLElement -@JS() -@staticInterop -abstract class DomHtmlElement {} - -/// (Some) methods of HtmlElement -extension DomHtmlElementExtension on DomHtmlElement { - external String get id; - external set id(String id); - external set innerText(String innerText); - external String? getAttribute(String attributeName); - - /// Node.appendChild - external DomHtmlElement appendChild(DomHtmlElement child); - - /// Element.setAttribute - external void setAttribute(String name, Object value); - - /// Element.remove - external void remove(); -} - -/// An instance of an HTMLMetaElement -@JS() -@staticInterop -abstract class DomHtmlMetaElement extends DomHtmlElement {} - -/// Some methods exclusive of Script elements -extension DomHtmlMetaElementExtension on DomHtmlMetaElement { - external set name(String name); - external set content(String content); -} - -// Getters - -/// window.document -@JS() -@staticInterop -external DomHtmlDocument get document; diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jsify_as.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jsify_as.dart index 82547b284f..fe8247594f 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jsify_as.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/jsify_as.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:js/js_util.dart' as js_util; +import 'dart:js_interop'; /// Converts a [data] object into a JS Object of type `T`. T jsifyAs(Map data) { - return js_util.jsify(data) as T; + return data.jsify() as T; } diff --git a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml index 451467ec07..bdcfdd7981 100644 --- a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml @@ -2,14 +2,14 @@ name: google_sign_in_web_integration_tests publish_to: none environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" dependencies: cupertino_icons: ^1.0.2 flutter: sdk: flutter - google_identity_services_web: ^0.2.1 + google_identity_services_web: ^0.3.0 google_sign_in_platform_interface: ^2.4.0 google_sign_in_web: path: ../ @@ -21,8 +21,8 @@ dev_dependencies: http: ">=0.13.0 <2.0.0" integration_test: sdk: flutter - js: ^0.6.3 mockito: 5.4.3 + web: ">=0.3.0 <0.5.0" flutter: uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index 8d7a341387..0412821685 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:ui_web' as ui_web; import 'package:flutter/foundation.dart' show visibleForTesting, kDebugMode; @@ -13,9 +13,9 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:google_identity_services_web/loader.dart' as loader; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:web/web.dart' as web; import 'src/button_configuration.dart' show GSIButtonConfiguration; -import 'src/dom.dart'; import 'src/flexible_size_html_element_view.dart'; import 'src/gis_client.dart'; @@ -54,7 +54,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { }) : _gisSdkClient = debugOverrideGisSdkClient, _userDataController = debugOverrideUserDataController ?? StreamController.broadcast() { - autoDetectedClientId = html + autoDetectedClientId = web.document .querySelector(clientIdMetaSelector) ?.getAttribute(clientIdAttributeName); @@ -175,7 +175,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { ui_web.platformViewRegistry.registerViewFactory( 'gsi_login_button', (int viewId) { - final DomElement element = createDomElement('div'); + final web.Element element = web.document.createElement('div'); element.setAttribute('style', 'width: 100%; height: 100%; overflow: hidden; display: flex; flex-wrap: wrap; align-content: center; justify-content: center;'); element.id = 'sign_in_button_$viewId'; @@ -196,8 +196,8 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { return FlexHtmlElementView( viewType: 'gsi_login_button', onPlatformViewCreated: (int viewId) { - final DomElement? element = - domDocument.querySelector('#sign_in_button_$viewId'); + final web.Element? element = + web.document.querySelector('#sign_in_button_$viewId'); assert(element != null, 'Cannot render GSI button. DOM is not ready!'); _gisClient.renderButton(element!, config); @@ -219,10 +219,11 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { @override Future signIn() async { if (kDebugMode) { - domConsole.warn( + web.console.warn( "The `signIn` method is discouraged on the web because it can't reliably provide an `idToken`.\n" - 'Use `signInSilently` and `renderButton` to authenticate your users instead.\n' - 'Read more: https://pub.dev/packages/google_sign_in_web'); + 'Use `signInSilently` and `renderButton` to authenticate your users instead.\n' + 'Read more: https://pub.dev/packages/google_sign_in_web' + .toJS); } await initialized; diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/button_configuration.dart b/packages/google_sign_in/google_sign_in_web/lib/src/button_configuration.dart index 24d13d5ae4..7869348dfe 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/button_configuration.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/button_configuration.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:google_identity_services_web/id.dart' as id; -import 'package:js/js_util.dart' as js_util; /// Converts user-facing `GisButtonConfiguration` into the JS-Interop `id.GsiButtonConfiguration`. id.GsiButtonConfiguration? convertButtonConfiguration( @@ -12,17 +11,16 @@ id.GsiButtonConfiguration? convertButtonConfiguration( if (config == null) { return null; } - return js_util.jsify({ - if (config.type != null) 'type': _idType[config.type], - if (config.theme != null) 'theme': _idTheme[config.theme], - if (config.size != null) 'size': _idSize[config.size], - if (config.text != null) 'text': _idText[config.text], - if (config.shape != null) 'shape': _idShape[config.shape], - if (config.logoAlignment != null) - 'logo_alignment': _idLogoAlignment[config.logoAlignment], - if (config.minimumWidth != null) 'width': config.minimumWidth, - if (config.locale != null) 'locale': config.locale, - }) as id.GsiButtonConfiguration; + return id.GsiButtonConfiguration( + type: _idType[config.type], + theme: _idTheme[config.theme], + size: _idSize[config.size], + text: _idText[config.text], + shape: _idShape[config.shape], + logo_alignment: _idLogoAlignment[config.logoAlignment], + width: config.minimumWidth, + locale: config.locale, + ); } /// A class to configure the Google Sign-In Button for web. diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/dom.dart b/packages/google_sign_in/google_sign_in_web/lib/src/dom.dart deleted file mode 100644 index a4d5b6799f..0000000000 --- a/packages/google_sign_in/google_sign_in_web/lib/src/dom.dart +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// ignore_for_file: public_member_api_docs - -import 'package:js/js.dart'; -import 'package:js/js_util.dart' as js_util; - -@JS() -@staticInterop -class DomConsole {} - -extension DomConsoleExtension on DomConsole { - void debug(String message, [List? more]) => - js_util.callMethod(this, 'debug', [message, ...?more]); - void info(String message, [List? more]) => - js_util.callMethod(this, 'info', [message, ...?more]); - void warn(String message, [List? more]) => - js_util.callMethod(this, 'warn', [message, ...?more]); -} - -@JS() -@staticInterop -class DomWindow {} - -@JS() -@staticInterop -class DomDocument {} - -extension DomDocumentExtension on DomDocument { - external DomElement? querySelector(String selectors); - DomElement createElement(String name, [Object? options]) => - js_util.callMethod(this, 'createElement', - [name, if (options != null) options]) as DomElement; -} - -@JS() -@staticInterop -class DomElement {} - -extension DomElementExtension on DomElement { - external String get id; - external set id(String id); - external String? getAttribute(String attributeName); - external void remove(); - external void setAttribute(String name, Object value); - external void removeAttribute(String name); - external set tabIndex(double? value); - external double? get tabIndex; - external set className(String value); - external String get className; - external bool hasAttribute(String name); - external DomElement? get firstChild; - external DomElement? querySelector(String selectors); - external String get tagName; -} - -@JS('window') -external DomWindow get domWindow; - -@JS('document') -external DomDocument get domDocument; - -@JS('console') -external DomConsole get domConsole; - -DomElement createDomElement(String tag) => domDocument.createElement(tag); - -/// DOM Observers: Mutation and Size -typedef DomMutationCallbackFn = void Function( - List mutation, DomMutationObserver observer); - -@JS() -@staticInterop -class DomMutationObserver {} - -DomMutationObserver createDomMutationObserver(DomMutationCallbackFn fn) => - domCallConstructorString('MutationObserver', [ - allowInterop( - (List mutations, DomMutationObserver observer) { - fn(mutations.cast(), observer); - }, - ) - ])! as DomMutationObserver; - -extension DomMutationObserverExtension on DomMutationObserver { - external void disconnect(); - void observe(DomElement target, - {bool? childList, - bool? attributes, - bool? subtree, - List? attributeFilter}) { - final Map options = { - if (childList != null) 'childList': childList, - if (attributes != null) 'attributes': attributes, - if (subtree != null) 'subtree': subtree, - if (attributeFilter != null) 'attributeFilter': attributeFilter - }; - return js_util - .callMethod(this, 'observe', [target, js_util.jsify(options)]); - } -} - -@JS() -@staticInterop -class DomMutationRecord {} - -extension DomMutationRecordExtension on DomMutationRecord { - external List? get addedNodes; - external List? get removedNodes; - external String? get attributeName; - external String? get type; -} - -/// ResizeObserver JS binding. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver -@JS() -@staticInterop -abstract class DomResizeObserver {} - -/// Creates a DomResizeObserver with a callback. -/// -/// Internally converts the `List` of entries into the expected -/// `List` -DomResizeObserver? createDomResizeObserver(DomResizeObserverCallbackFn fn) { - return domCallConstructorString('ResizeObserver', [ - allowInterop((List entries, DomResizeObserver observer) { - fn(entries.cast(), observer); - }), - ]) as DomResizeObserver?; -} - -/// ResizeObserver instance methods. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#instance_methods -extension DomResizeObserverExtension on DomResizeObserver { - external void disconnect(); - external void observe(DomElement target, - [DomResizeObserverObserveOptions options]); - external void unobserve(DomElement target); -} - -/// Options object passed to the `observe` method of a [DomResizeObserver]. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#parameters -@JS() -@staticInterop -@anonymous -abstract class DomResizeObserverObserveOptions { - external factory DomResizeObserverObserveOptions({ - String box, - }); -} - -/// Type of the function used to create a Resize Observer. -typedef DomResizeObserverCallbackFn = void Function( - List entries, DomResizeObserver observer); - -/// The object passed to the [DomResizeObserverCallbackFn], which allows access to the new dimensions of the observed element. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry -@JS() -@staticInterop -abstract class DomResizeObserverEntry {} - -/// ResizeObserverEntry instance properties. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry#instance_properties -extension DomResizeObserverEntryExtension on DomResizeObserverEntry { - /// A DOMRectReadOnly object containing the new size of the observed element when the callback is run. - /// - /// Note that this is better supported than the above two properties, but it - /// is left over from an earlier implementation of the Resize Observer API, is - /// still included in the spec for web compat reasons, and may be deprecated - /// in future versions. - external DomRectReadOnly get contentRect; - external DomElement get target; - // Some more future getters: - // - // borderBoxSize - // contentBoxSize - // devicePixelContentBoxSize -} - -@JS() -@staticInterop -class DomRectReadOnly {} - -extension DomRectReadOnlyExtension on DomRectReadOnly { - external double get x; - external double get y; - external double get width; - external double get height; - external double get top; - external double get right; - external double get bottom; - external double get left; -} - -Object? domGetConstructor(String constructorName) => - js_util.getProperty(domWindow, constructorName); - -T? domCallConstructorString(String constructorName, List args) { - final Object? constructor = domGetConstructor(constructorName); - if (constructor == null) { - return null; - } - return js_util.callConstructor(constructor, args); -} diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart b/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart index fb639faccd..56a251d4ed 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart @@ -1,11 +1,11 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:js_interop'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - -import 'dom.dart'; +import 'package:web/web.dart' as web; /// An HTMLElementView widget that resizes with its contents. class FlexHtmlElementView extends StatefulWidget { @@ -37,12 +37,12 @@ class _FlexHtmlElementView extends State { /// Watches for changes being made to the DOM tree. /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver - DomMutationObserver? _mutationObserver; + web.MutationObserver? _mutationObserver; /// Reports changes to the dimensions of an Element's content box. /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API - DomResizeObserver? _resizeObserver; + web.ResizeObserver? _resizeObserver; @override void dispose() { @@ -55,8 +55,13 @@ class _FlexHtmlElementView extends State { /// Update the state with the new `size`, if needed. void _doResize(Size size) { if (size != _lastReportedSize) { - domConsole.debug( - 'Resizing', [widget.viewType, size.width, size.height]); + final String log = [ + 'Resizing: ', + widget.viewType, + size.width, + size.height + ].join(' '); + web.console.debug(log.toJS); setState(() { _lastReportedSize = size; }); @@ -65,12 +70,13 @@ class _FlexHtmlElementView extends State { /// The function called whenever an observed resize occurs. void _onResizeEntries( - List resizes, - DomResizeObserver observer, + JSArray resizes, + web.ResizeObserver observer, ) { - final DomRectReadOnly rect = resizes.last.contentRect; + final web.DOMRectReadOnly rect = + resizes.toDart.cast().last.contentRect; if (rect.width > 0 && rect.height > 0) { - _doResize(Size(rect.width, rect.height)); + _doResize(Size(rect.width.toDouble(), rect.height.toDouble())); } } @@ -79,31 +85,35 @@ class _FlexHtmlElementView extends State { /// When mutations are received, this function attaches a Resize Observer to /// the first child of the mutation, which will drive void _onMutationRecords( - List mutations, - DomMutationObserver observer, + JSArray mutations, + web.MutationObserver observer, ) { - for (final DomMutationRecord mutation in mutations) { - if (mutation.addedNodes != null) { - final DomElement? element = _locateSizeProvider(mutation.addedNodes!); + mutations.toDart + .cast() + .forEach((web.MutationRecord mutation) { + if (mutation.addedNodes.length > 0) { + final web.Element? element = _locateSizeProvider(mutation.addedNodes); if (element != null) { - _resizeObserver = createDomResizeObserver(_onResizeEntries); + _resizeObserver = web.ResizeObserver(_onResizeEntries.toJS); _resizeObserver?.observe(element); // Stop looking at other mutations observer.disconnect(); return; } } - } + }); } /// Registers a MutationObserver on the root element of the HtmlElementView. - void _registerListeners(DomElement? root) { + void _registerListeners(web.Element? root) { assert(root != null, 'DOM is not ready for the FlexHtmlElementView'); - _mutationObserver = createDomMutationObserver(_onMutationRecords); + _mutationObserver = web.MutationObserver(_onMutationRecords.toJS); // Monitor the size of the child element, whenever it's created... _mutationObserver!.observe( root!, - childList: true, + web.MutationObserverInit( + childList: true, + ), ); } @@ -127,14 +137,14 @@ class _FlexHtmlElementView extends State { /// /// The `elements` list should contain a single element: the only child of the /// element returned by `_locatePlatformViewRoot`. -DomElement? _locateSizeProvider(List elements) { - return elements.first; +web.Element? _locateSizeProvider(web.NodeList elements) { + return elements.item(0) as web.Element?; } /// Finds the root element of a platform view by its `viewId`. /// /// This element matches the one returned by the registered platform view factory. -DomElement? _locatePlatformViewRoot(int viewId) { - return domDocument +web.Element? _locatePlatformViewRoot(int viewId) { + return web.document .querySelector('flt-platform-view[slot\$="-$viewId"] :first-child'); } diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/gis_client.dart b/packages/google_sign_in/google_sign_in_web/lib/src/gis_client.dart index e88f797aa2..b28796b8d6 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/gis_client.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/gis_client.dart @@ -2,18 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; +import 'dart:js_interop'; // TODO(dit): Split `id` and `oauth2` "services" for mocking. https://github.com/flutter/flutter/issues/120657 import 'package:google_identity_services_web/id.dart'; import 'package:google_identity_services_web/oauth2.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; -// ignore: unnecessary_import -import 'package:js/js.dart'; -import 'package:js/js_util.dart'; +import 'package:web/web.dart' as web; import 'button_configuration.dart' show GSIButtonConfiguration, convertButtonConfiguration; -import 'dom.dart'; import 'people.dart' as people; import 'utils.dart' as utils; @@ -65,7 +63,9 @@ class GisSdkClient { void _logIfEnabled(String message, [List? more]) { if (_loggingEnabled) { - domConsole.info('[google_sign_in_web] $message', more); + final String log = + ['[google_sign_in_web]', message, ...?more].join(' '); + web.console.info(log.toJS); } } @@ -78,7 +78,7 @@ class GisSdkClient { _tokenResponses.stream.listen((TokenResponse response) { _lastTokenResponse = response; _lastTokenResponseExpiration = - DateTime.now().add(Duration(seconds: response.expires_in)); + DateTime.now().add(Duration(seconds: response.expires_in!)); }, onError: (Object error) { _logIfEnabled('Error on TokenResponse:', [error.toString()]); _lastTokenResponse = null; @@ -130,7 +130,7 @@ class GisSdkClient { // Initialize `id` for the silent-sign in code. final IdConfiguration idConfig = IdConfiguration( client_id: clientId, - callback: allowInterop(onResponse), + callback: onResponse, cancel_on_tap_outside: false, auto_select: true, // Attempt to sign-in silently. hd: hostedDomain, @@ -161,12 +161,14 @@ class GisSdkClient { // Create a Token Client for authorization calls. final TokenClientConfig tokenConfig = TokenClientConfig( client_id: clientId, - hosted_domain: hostedDomain, - callback: allowInterop(_onTokenResponse), - error_callback: allowInterop(_onTokenError), - // `scope` will be modified by the `signIn` method, in case we need to - // backfill user Profile info. - scope: ' ', + hd: hostedDomain, + callback: _onTokenResponse, + error_callback: _onTokenError, + // This is here only to satisfy the initialization of the JS TokenClient. + // In reality, `scope` is always overridden when calling `requestScopes` + // (or the deprecated `signIn`) through an [OverridableTokenClientConfig] + // object. + scope: [' '], // Fake (but non-empty) list of scopes. ); return oauth2.initTokenClient(tokenConfig); } @@ -187,9 +189,9 @@ class GisSdkClient { // Token clients have an additional `error_callback` for miscellaneous // errors, like "popup couldn't open" or "popup closed by user". void _onTokenError(Object? error) { - // This is handled in a funky (js_interop) way because of: - // https://github.com/dart-lang/sdk/issues/50899 - _tokenResponses.addError(getProperty(error!, 'type')); + if (error != null) { + _tokenResponses.addError((error as GoogleIdentityServicesError).type); + } } // Creates a `oauth2.CodeClient` used for authorization (scope) requests. @@ -203,10 +205,10 @@ class GisSdkClient { // Create a Token Client for authorization calls. final CodeClientConfig codeConfig = CodeClientConfig( client_id: clientId, - hosted_domain: hostedDomain, - callback: allowInterop(_onCodeResponse), - error_callback: allowInterop(_onCodeError), - scope: scopes.join(' '), + hd: hostedDomain, + callback: _onCodeResponse, + error_callback: _onCodeError, + scope: scopes, select_account: true, ux_mode: UxMode.popup, ); @@ -222,7 +224,9 @@ class GisSdkClient { } void _onCodeError(Object? error) { - _codeResponses.addError(getProperty(error!, 'type')); + if (error != null) { + _codeResponses.addError((error as GoogleIdentityServicesError).type); + } } /// Attempts to sign-in the user using the OneTap UX flow. @@ -238,9 +242,9 @@ class GisSdkClient { // Ask the SDK to render the OneClick sign-in. // // And also handle its "moments". - id.prompt(allowInterop((PromptMomentNotification moment) { + id.prompt((PromptMomentNotification moment) { _onPromptMoment(moment, userDataCompleter); - })); + }); return userDataCompleter.future; } @@ -287,7 +291,7 @@ class GisSdkClient { Object parent, GSIButtonConfiguration options, ) async { - return id.renderButton(parent, convertButtonConfiguration(options)!); + return id.renderButton(parent, convertButtonConfiguration(options)); } /// Requests a server auth code per: @@ -318,11 +322,10 @@ class GisSdkClient { 'Use `renderButton` instead. See: https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services') Future signIn() async { // Warn users that this method will be removed. - domConsole.warn( - 'The google_sign_in plugin `signIn` method is deprecated on the web, and will be removed in Q2 2024. Please use `renderButton` instead. See: ', - [ - 'https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services' - ]); + web.console.warn( + 'The google_sign_in plugin `signIn` method is deprecated on the web, and will be removed in Q2 2024. Please use `renderButton` instead. See: ' + 'https://pub.dev/packages/google_sign_in_web#migrating-to-v011-and-v012-google-identity-services' + .toJS); // If we already know the user, use their `email` as a `hint`, so they don't // have to pick their user again in the Authorization popup. final GoogleSignInUserData? knownUser = @@ -331,14 +334,14 @@ class GisSdkClient { // user activation. _tokenClient.requestAccessToken(OverridableTokenClientConfig( prompt: knownUser == null ? 'select_account' : '', - hint: knownUser?.email, + login_hint: knownUser?.email, scope: [ ..._initialScopes, // If the user hasn't gone through the auth process, // the plugin will attempt to `requestUserData` after, // so we need extra scopes to retrieve that info. if (_lastCredentialResponse == null) ...people.scopes, - ].join(' '), + ], )); await _tokenResponses.stream.first; @@ -384,7 +387,7 @@ class GisSdkClient { /// Revokes the current authorization and authentication. Future disconnect() async { if (_lastTokenResponse != null) { - oauth2.revoke(_lastTokenResponse!.access_token); + oauth2.revoke(_lastTokenResponse!.access_token!); } await signOut(); } @@ -431,8 +434,8 @@ class GisSdkClient { _tokenClient.requestAccessToken(OverridableTokenClientConfig( prompt: knownUser == null ? 'select_account' : '', - hint: knownUser?.email, - scope: scopes.join(' '), + login_hint: knownUser?.email, + scope: scopes, include_granted_scopes: true, )); diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 98d9ea5b57..c0c7e16dd3 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,11 +3,11 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.12.2+1 +version: 0.12.3 environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" flutter: plugin: @@ -22,10 +22,10 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - google_identity_services_web: ^0.2.2 + google_identity_services_web: ^0.3.0 google_sign_in_platform_interface: ^2.4.0 http: ">=0.13.0 <2.0.0" - js: ^0.6.3 + web: ">=0.3.0 <0.5.0" dev_dependencies: flutter_test: diff --git a/script/configs/exclude_all_packages_app.yaml b/script/configs/exclude_all_packages_app.yaml index 0a43bb8988..040087f10f 100644 --- a/script/configs/exclude_all_packages_app.yaml +++ b/script/configs/exclude_all_packages_app.yaml @@ -9,8 +9,5 @@ # NOTE: camera_android is semi-excluded via special casing in the repo tools. # See create_all_packages_app_command.dart. -# Excluded until the google_sign_in_web package updates -- google_identity_services_web - # This is a permament entry, as it should never be a direct app dependency. - plugin_platform_interface