From b5f4fcbc0c98c5fd6fd1b6db096408a61a92fdb1 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 21 Nov 2023 18:09:09 -0800 Subject: [PATCH 1/4] migrate to pkg web --- CHANGELOG.md | 3 ++- lib/html.dart | 30 ++++++++++++++++++++---------- lib/src/web_helpers.dart | 12 ++++++++++++ pubspec.yaml | 1 + test/html_test.dart | 8 +++++--- 5 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 lib/src/web_helpers.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e4630..011eed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ ## 2.4.1-wip -- Bump minimum Dart version to 3.2.0 - Update the examples to use `WebSocketChannel.ready` and clarify that `WebSocketChannel.ready` should be awaited before sending data over the `WebSocketChannel`. - Mention `ready` in the docs for `connect`. +- Bump minimum Dart version to 3.2.0 +- Move to `pkg:web` to support WebAssembly compilation. ## 2.4.0 diff --git a/lib/html.dart b/lib/html.dart index 0cb7ffd..ff7b67b 100644 --- a/lib/html.dart +++ b/lib/html.dart @@ -3,14 +3,16 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:html'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:stream_channel/stream_channel.dart'; +import 'package:web/helpers.dart'; import 'src/channel.dart'; import 'src/exception.dart'; +import 'src/web_helpers.dart'; /// A [WebSocketChannel] that communicates using a `dart:html` [WebSocket]. class HtmlWebSocketChannel extends StreamChannelMixin @@ -73,7 +75,8 @@ class HtmlWebSocketChannel extends StreamChannelMixin /// [BinaryType.blob], they're delivered as [Blob]s instead. HtmlWebSocketChannel.connect(Object url, {Iterable? protocols, BinaryType? binaryType}) - : this(WebSocket(url.toString(), protocols) + : this(WebSocket(url.toString(), + (protocols ?? []).toList().map((e) => e.toJS).toList().toJS) ..binaryType = (binaryType ?? BinaryType.list).value); /// Creates a channel wrapping [innerWebSocket]. @@ -109,11 +112,7 @@ class HtmlWebSocketChannel extends StreamChannelMixin _controller.local.sink.close(); }); - innerWebSocket.onMessage.listen((event) { - var data = event.data; - if (data is ByteBuffer) data = data.asUint8List(); - _controller.local.sink.add(data); - }); + innerWebSocket.onMessage.listen(_innerListen); // The socket API guarantees that only a single error event will be emitted, // and that once it is no other events will be emitted. @@ -124,16 +123,27 @@ class HtmlWebSocketChannel extends StreamChannelMixin }); } + void _innerListen(MessageEvent event) { + dynamic data = event.data; + + if ((data as JSAny?).typeofEquals('object') && + (data as JSObject).instanceOfString('ArrayBuffer')) { + data = (data as JSArrayBuffer).toDart.asUint8List(); + } + _controller.local.sink.add(data); + } + /// Pipes user events to [innerWebSocket]. void _listen() { - _controller.local.stream.listen(innerWebSocket.send, onDone: () { + _controller.local.stream.listen((obj) => innerWebSocket.send(obj!.jsify()!), + onDone: () { // On Chrome and possibly other browsers, `null` can't be passed as the // default here. The actual arity of the function call must be correct or // it will fail. if (_localCloseCode != null && _localCloseReason != null) { - innerWebSocket.close(_localCloseCode, _localCloseReason); + innerWebSocket.close(_localCloseCode!, _localCloseReason!); } else if (_localCloseCode != null) { - innerWebSocket.close(_localCloseCode); + innerWebSocket.close(_localCloseCode!); } else { innerWebSocket.close(); } diff --git a/lib/src/web_helpers.dart b/lib/src/web_helpers.dart new file mode 100644 index 0000000..1821a26 --- /dev/null +++ b/lib/src/web_helpers.dart @@ -0,0 +1,12 @@ +import 'package:web/helpers.dart'; + +extension WebSocketExtension on WebSocket { + Stream get onOpen => + const EventStreamProvider('open').forTarget(this); + Stream get onMessage => + const EventStreamProvider('message').forTarget(this); + Stream get onClose => + const EventStreamProvider('close').forTarget(this); + Stream get onError => + const EventStreamProvider('error').forTarget(this); +} diff --git a/pubspec.yaml b/pubspec.yaml index c772f4d..5c3b9cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: async: ^2.5.0 crypto: ^3.0.0 stream_channel: ^2.1.0 + web: ^0.4.0 dev_dependencies: dart_flutter_team_lints: ^2.0.0 diff --git a/test/html_test.dart b/test/html_test.dart index d444d7c..9d91025 100644 --- a/test/html_test.dart +++ b/test/html_test.dart @@ -6,12 +6,14 @@ library; import 'dart:async'; -import 'dart:html'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:async/async.dart'; import 'package:test/test.dart'; +import 'package:web/helpers.dart' hide BinaryType; import 'package:web_socket_channel/html.dart'; +import 'package:web_socket_channel/src/web_helpers.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; void main() { @@ -176,6 +178,6 @@ void main() { Future> _decodeBlob(Blob blob) async { final reader = FileReader(); reader.readAsArrayBuffer(blob); - await reader.onLoad.first; - return reader.result as Uint8List; + await reader.onLoadEnd.first; + return (reader.result as JSArrayBuffer).toDart.asUint8List(); } From 43ce1973d066d8ffa05524fd2a943631e1e4056f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Nov 2023 10:35:21 -0800 Subject: [PATCH 2/4] Use EventStreamProviders --- lib/src/web_helpers.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/src/web_helpers.dart b/lib/src/web_helpers.dart index 1821a26..c1a6baf 100644 --- a/lib/src/web_helpers.dart +++ b/lib/src/web_helpers.dart @@ -1,12 +1,11 @@ import 'package:web/helpers.dart'; -extension WebSocketExtension on WebSocket { - Stream get onOpen => - const EventStreamProvider('open').forTarget(this); +extension WebSocketEvents on WebSocket { + Stream get onOpen => EventStreamProviders.openEvent.forTarget(this); Stream get onMessage => - const EventStreamProvider('message').forTarget(this); + EventStreamProviders.messageEvent.forTarget(this); Stream get onClose => - const EventStreamProvider('close').forTarget(this); + EventStreamProviders.closeEvent.forTarget(this); Stream get onError => - const EventStreamProvider('error').forTarget(this); + EventStreamProviders.errorEventSourceEvent.forTarget(this); } From d509d1b87f98ebfff8f3adac1533d44ee4c06778 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Nov 2023 10:39:12 -0800 Subject: [PATCH 3/4] nit --- lib/src/web_helpers.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/web_helpers.dart b/lib/src/web_helpers.dart index c1a6baf..f8b3784 100644 --- a/lib/src/web_helpers.dart +++ b/lib/src/web_helpers.dart @@ -1,5 +1,7 @@ import 'package:web/helpers.dart'; +// TODO: remove when https://github.com/dart-lang/web/pull/102 is landed +// and the min constraint on pkg:web is updated extension WebSocketEvents on WebSocket { Stream get onOpen => EventStreamProviders.openEvent.forTarget(this); Stream get onMessage => From bafe97e27abb7256cf0114a3000a3821b1e8c9bd Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Nov 2023 11:29:27 -0800 Subject: [PATCH 4/4] nits --- lib/html.dart | 24 ++++++++++++++++-------- lib/src/web_helpers.dart | 8 ++++++-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/html.dart b/lib/html.dart index ff7b67b..2263e6e 100644 --- a/lib/html.dart +++ b/lib/html.dart @@ -75,9 +75,15 @@ class HtmlWebSocketChannel extends StreamChannelMixin /// [BinaryType.blob], they're delivered as [Blob]s instead. HtmlWebSocketChannel.connect(Object url, {Iterable? protocols, BinaryType? binaryType}) - : this(WebSocket(url.toString(), - (protocols ?? []).toList().map((e) => e.toJS).toList().toJS) - ..binaryType = (binaryType ?? BinaryType.list).value); + : this( + WebSocket( + url.toString(), + (protocols?.toList() ?? const []) + .map((e) => e.toJS) + .toList() + .toJS, + )..binaryType = (binaryType ?? BinaryType.list).value, + ); /// Creates a channel wrapping [innerWebSocket]. HtmlWebSocketChannel(this.innerWebSocket) { @@ -124,11 +130,13 @@ class HtmlWebSocketChannel extends StreamChannelMixin } void _innerListen(MessageEvent event) { - dynamic data = event.data; - - if ((data as JSAny?).typeofEquals('object') && - (data as JSObject).instanceOfString('ArrayBuffer')) { - data = (data as JSArrayBuffer).toDart.asUint8List(); + final eventData = event.data; + Object? data; + if (eventData.typeofEquals('object') && + (eventData as JSObject).instanceOfString('ArrayBuffer')) { + data = (eventData as JSArrayBuffer).toDart.asUint8List(); + } else { + data = event.data; } _controller.local.sink.add(data); } diff --git a/lib/src/web_helpers.dart b/lib/src/web_helpers.dart index f8b3784..7ef46e5 100644 --- a/lib/src/web_helpers.dart +++ b/lib/src/web_helpers.dart @@ -1,7 +1,11 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + import 'package:web/helpers.dart'; -// TODO: remove when https://github.com/dart-lang/web/pull/102 is landed -// and the min constraint on pkg:web is updated +// TODO(kevmoo): remove when https://github.com/dart-lang/web/commit/4cb5811ed06 +// is in a published release and the min constraint on pkg:web is updated extension WebSocketEvents on WebSocket { Stream get onOpen => EventStreamProviders.openEvent.forTarget(this); Stream get onMessage =>