From 0b389e8089da5b3f8b5d24fde52e3e5617d19220 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 30 Jul 2024 18:08:49 +0300 Subject: [PATCH] Migrate Dart sources from `dart:html` to `package:web` to support WASM (#178) Co-authored-by: alexlapa --- Cargo.lock | 4 +- flutter/CHANGELOG.md | 7 + flutter/example/lib/call_route.dart | 1072 ++++++++++--------- flutter/example/lib/join_route.dart | 134 +-- flutter/example/pubspec.lock | 34 +- flutter/example/pubspec.yaml | 4 +- flutter/lib/medea_jason.dart | 11 +- flutter/lib/src/interface/enums.dart | 2 +- flutter/lib/src/web/connection_handle.dart | 22 +- flutter/lib/src/web/exceptions.dart | 22 +- flutter/lib/src/web/jason_wasm.dart | 321 ++---- flutter/lib/src/web/local_media_track.dart | 13 +- flutter/lib/src/web/media_manager.dart | 25 +- flutter/lib/src/web/reconnect_handle.dart | 23 +- flutter/lib/src/web/remote_media_track.dart | 15 +- flutter/lib/src/web/room_handle.dart | 71 +- flutter/pubspec.yaml | 6 +- src/platform/dart/transport.rs | 4 +- 18 files changed, 852 insertions(+), 938 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e4d36329..4ebf92297 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,9 +711,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", diff --git a/flutter/CHANGELOG.md b/flutter/CHANGELOG.md index dbe1dcbbd..957b6dda5 100644 --- a/flutter/CHANGELOG.md +++ b/flutter/CHANGELOG.md @@ -13,7 +13,14 @@ All user visible changes to this project will be documented in this file. This p - More information in `toString()` on custom exceptions ([#140]). +### Changed + +- Migrated from [`dart:html`] to [`package:web`] package ([#178]). + [#140]: /../../pull/140 +[#178]: /../../pull/178 +[`dart:html`]: https://dart.dev/libraries/dart-html +[`package:web`]: https://pub.dev/packages/web diff --git a/flutter/example/lib/call_route.dart b/flutter/example/lib/call_route.dart index 802c49792..1701244a2 100644 --- a/flutter/example/lib/call_route.dart +++ b/flutter/example/lib/call_route.dart @@ -14,21 +14,25 @@ bool _audioSend = true; bool _audioRecv = true; class CallRoute extends StatefulWidget { - final String _roomId; - final String _memberId; - final bool _isPublish; - final bool _publishAudio; - final bool _publishVideo; - final bool _fakeMedia; + const CallRoute( + this.roomId, + this.memberId, + this.isPublish, + this.publishVideo, + this.publishAudio, + this.fakeMedia, { + super.key, + }); - const CallRoute(this._roomId, this._memberId, this._isPublish, - this._publishVideo, this._publishAudio, this._fakeMedia, - {super.key}); + final String roomId; + final String memberId; + final bool isPublish; + final bool publishAudio; + final bool publishVideo; + final bool fakeMedia; @override - // ignore: no_logic_in_create_state - State createState() => _CallState( - _roomId, _memberId, _isPublish, _publishVideo, _publishAudio, _fakeMedia); + State createState() => _CallState(); } class ConnectWidgets { @@ -75,20 +79,17 @@ class _CallState extends State { late String _roomId; late String _memberId; - _CallState(String roomId, String memberId, bool isPublish, bool publishVideo, - bool publishAudio, bool fakeMedia) { - _roomId = roomId; - _memberId = memberId; - _isPublish = isPublish; - _publishVideo = publishVideo; - _publishAudio = publishAudio; - _fakeMedia = fakeMedia; - } - @override void initState() { + _roomId = widget.roomId; + _memberId = widget.memberId; + _isPublish = widget.isPublish; + _publishVideo = widget.publishVideo; + _publishAudio = widget.publishAudio; + _fakeMedia = widget.fakeMedia; + _call.onNewRemoteStream((track, remoteId, conn) async { - var trackId = track.getTrack().id(); + final trackId = track.getTrack().id(); if (track.mediaDirection() == jason.TrackMediaDirection.sendRecv) { var renderer = createVideoRenderer(); await renderer.initialize(); @@ -103,37 +104,40 @@ class _CallState extends State { connectionWidgets.name = Text(remoteId); connectionWidgets.toggleButtons = [ TextButton( - onPressed: () { - if (!connectionWidgets!.recvVideoDevice) { - conn.enableRemoteVideo(jason.MediaSourceKind.device); - } else { - conn.disableRemoteVideo(jason.MediaSourceKind.device); - } - connectionWidgets.recvVideoDevice = - !connectionWidgets.recvVideoDevice; - }, - child: const Text('Toggle divece video recv')), + onPressed: () { + if (!connectionWidgets!.recvVideoDevice) { + conn.enableRemoteVideo(jason.MediaSourceKind.device); + } else { + conn.disableRemoteVideo(jason.MediaSourceKind.device); + } + connectionWidgets.recvVideoDevice = + !connectionWidgets.recvVideoDevice; + }, + child: const Text('Toggle device video recv'), + ), TextButton( - onPressed: () { - if (!connectionWidgets!.recvVideoDisplay) { - conn.enableRemoteVideo(jason.MediaSourceKind.display); - } else { - conn.disableRemoteVideo(jason.MediaSourceKind.display); - } - connectionWidgets.recvVideoDisplay = - !connectionWidgets.recvVideoDisplay; - }, - child: const Text('Toggle display video recv')), + onPressed: () { + if (!connectionWidgets!.recvVideoDisplay) { + conn.enableRemoteVideo(jason.MediaSourceKind.display); + } else { + conn.disableRemoteVideo(jason.MediaSourceKind.display); + } + connectionWidgets.recvVideoDisplay = + !connectionWidgets.recvVideoDisplay; + }, + child: const Text('Toggle display video recv'), + ), TextButton( - onPressed: () { - if (!connectionWidgets!.recvAudio) { - conn.enableRemoteAudio(); - } else { - conn.disableRemoteAudio(); - } - connectionWidgets.recvAudio = !connectionWidgets.recvAudio; - }, - child: const Text('Toggle audio recv')) + onPressed: () { + if (!connectionWidgets!.recvAudio) { + conn.enableRemoteAudio(); + } else { + conn.disableRemoteAudio(); + } + connectionWidgets.recvAudio = !connectionWidgets.recvAudio; + }, + child: const Text('Toggle audio recv'), + ) ]; } else { connectionWidgets.videoTracks[trackId] = VideoView(renderer); @@ -235,157 +239,179 @@ class _CallState extends State { ScaffoldMessenger.of(context).showSnackBar(snackBar); }); - _call.start(_roomId, _memberId, _isPublish, _publishVideo, _publishAudio, - _fakeMedia); + _call.start( + _roomId, + _memberId, + _isPublish, + _publishVideo, + _publishAudio, + _fakeMedia, + ); + super.initState(); } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Medea call demo'), actions: [ - TextButton( - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, - ), - child: const Text('MediaSetting'), - onPressed: () async { - await mediaSettingDialog(context, _call); - }), - TextButton( - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, - ), - child: const Text('Create'), - onPressed: () async { - await controlApiCreateDialog(context, _call); - }), - TextButton( - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, - ), - child: const Text('Get'), - onPressed: () async { - await controlApiGetDialog(context, _call); - }), - TextButton( - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, - ), - child: const Text('Delete'), - onPressed: () async { - await controlApiDeleteDialog(context, _call); - }), - TextButton( - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, + appBar: AppBar(title: const Text('Medea call demo'), actions: [ + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.blue, + ), + child: const Text('MediaSetting'), + onPressed: () async { + await mediaSettingDialog(context, _call); + }, + ), + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.blue, + ), + child: const Text('Create'), + onPressed: () async { + await controlApiCreateDialog(context, _call); + }, + ), + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.blue, + ), + child: const Text('Get'), + onPressed: () async { + await controlApiGetDialog(context, _call); + }, + ), + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.blue, + ), + child: const Text('Delete'), + onPressed: () async { + await controlApiDeleteDialog(context, _call); + }, + ), + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.blue, + ), + child: const Text('Callbacks'), + onPressed: () async { + await showCallbacks(context, _call); + }, + ), + ]), + body: Center( + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Column( + children: [ + LinearProgressIndicator( + value: currentAudioLevel, + minHeight: 10.0, ), - child: const Text('Callbacks'), - onPressed: () async { - await showCallbacks(context, _call); - }), - ]), - body: Center( - child: SizedBox( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - child: Column( - children: [ - LinearProgressIndicator( - value: currentAudioLevel, - minHeight: 10.0, + Expanded( + child: Row( + children: _widgets.values + .map( + (videoMap) => Expanded( + child: Column(children: videoMap.all().toList()), + ), + ) + .toList(), ), - Expanded( - child: Row( - children: _widgets.values - .map((videoMap) => Expanded( - child: Column( - children: videoMap.all().toList(), - ), - )) - .toList(), - ), - ) - ], - ), + ) + ], ), ), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: Padding( - padding: const EdgeInsets.only(bottom: 50.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(right: 30.0), - child: FloatingActionButton( - onPressed: () async { - setState(() { - _audioEnabled = !_audioEnabled; - }); - await _call.toggleAudio(_audioEnabled); - }, - heroTag: null, - child: Icon(_audioEnabled ? Icons.mic_off : Icons.mic), - )), - Padding( - padding: const EdgeInsets.only(right: 30.0), - child: FloatingActionButton( - onPressed: () async { - setState(() { - _videoEnabled = !_videoEnabled; - }); - await _call.toggleVideo(_videoEnabled); - }, - heroTag: null, - child: Icon( - _videoEnabled ? Icons.videocam_off : Icons.videocam), - )), - FloatingActionButton( + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: Padding( + padding: const EdgeInsets.only(bottom: 50.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(right: 30.0), + child: FloatingActionButton( onPressed: () async { - await _call.dispose(); - Navigator.pop(context); + setState(() { + _audioEnabled = !_audioEnabled; + }); + + await _call.toggleAudio(_audioEnabled); }, heroTag: null, - backgroundColor: Colors.red, - child: const Icon(Icons.call_end), - ), - ], - ))); + child: Icon(_audioEnabled ? Icons.mic_off : Icons.mic), + )), + Padding( + padding: const EdgeInsets.only(right: 30.0), + child: FloatingActionButton( + onPressed: () async { + setState(() { + _videoEnabled = !_videoEnabled; + }); + + await _call.toggleVideo(_videoEnabled); + }, + heroTag: null, + child: Icon( + _videoEnabled ? Icons.videocam_off : Icons.videocam, + ), + )), + FloatingActionButton( + onPressed: () async { + await _call.dispose(); + Navigator.pop(context); + }, + heroTag: null, + backgroundColor: Colors.red, + child: const Icon(Icons.call_end), + ), + ], + ), + ), + ); } } -Future showCallbacks(BuildContext context, Call call) async { - var cbs = await call.controlApi.getCallbacks(); +Future showCallbacks(BuildContext context, Call call) async { + final cbs = await call.controlApi.getCallbacks(); + await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: double.maxFinite, - child: ListView( - shrinkWrap: true, - children: cbs - .map((cb) => Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Text(cb.event.toJson().toString())), - Expanded(child: Text(cb.at)), - Expanded(child: Text(cb.fid)), - ], - )) - .toList(), - ))); - }); + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: double.maxFinite, + child: ListView( + shrinkWrap: true, + children: cbs + .map( + (cb) => Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded(child: Text(cb.event.toJson().toString())), + Expanded(child: Text(cb.at)), + Expanded(child: Text(cb.fid)), + ], + ), + ) + .toList(), + ), + ), + ); + }, + ); } -Future controlApiGetDialog(BuildContext context, Call call) async { +Future controlApiGetDialog(BuildContext context, Call call) async { var roomId = ''; var memberId = ''; var endpointId = ''; @@ -446,12 +472,13 @@ Future controlApiGetDialog(BuildContext context, Call call) async { ), const SizedBox(height: 10), TextButton( - onPressed: () async { - await call.controlApi.get(roomId, memberId, endpointId); + onPressed: () async { + await call.controlApi.get(roomId, memberId, endpointId); - Navigator.pop(context); - }, - child: const Text('Get')) + Navigator.pop(context); + }, + child: const Text('Get'), + ) ], ); }, @@ -461,7 +488,7 @@ Future controlApiGetDialog(BuildContext context, Call call) async { ); } -Future controlApiDeleteDialog(BuildContext context, Call call) async { +Future controlApiDeleteDialog(BuildContext context, Call call) async { var roomId = ''; var memberId = ''; var endpointId = ''; @@ -478,9 +505,7 @@ Future controlApiDeleteDialog(BuildContext context, Call call) async { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Flexible( - child: Text('local://'), - ), + const Flexible(child: Text('local://')), const SizedBox(width: 10), Expanded( child: TextFormField( @@ -522,12 +547,12 @@ Future controlApiDeleteDialog(BuildContext context, Call call) async { ), const SizedBox(height: 10), TextButton( - onPressed: () async { - await call.controlApi - .delete(roomId, memberId, endpointId); - Navigator.pop(context); - }, - child: const Text('Get')) + onPressed: () async { + await call.controlApi.delete(roomId, memberId, endpointId); + Navigator.pop(context); + }, + child: const Text('Get'), + ) ], ); }, @@ -537,14 +562,14 @@ Future controlApiDeleteDialog(BuildContext context, Call call) async { ); } -Future mediaSettingDialog(BuildContext context, Call call) async { - var displayList = await call.enumerateDisplay(); +Future mediaSettingDialog(BuildContext context, Call call) async { + final displayList = await call.enumerateDisplay(); - var deviceList = await call.enumerateDevice(); - var videoDevices = deviceList + final deviceList = await call.enumerateDevice(); + final videoDevices = deviceList .where((element) => element.kind() == jason.MediaDeviceKind.videoInput) .toList(); - var audioDevices = deviceList + final audioDevices = deviceList .where((element) => element.kind() == jason.MediaDeviceKind.audioInput) .toList(); @@ -558,11 +583,12 @@ Future mediaSettingDialog(BuildContext context, Call call) async { mainAxisSize: MainAxisSize.min, children: [ SwitchListTile( - title: const Text('Screen share'), - value: call.screenShare, - onChanged: (v) => setStateSb(() { - call.screenShare = v; - })), + title: const Text('Screen share'), + value: call.screenShare, + onChanged: (v) => setStateSb(() { + call.screenShare = v; + }), + ), DropdownButton( value: call.videoDisplayId, icon: const Icon(Icons.arrow_downward), @@ -595,8 +621,9 @@ Future mediaSettingDialog(BuildContext context, Call call) async { onChanged: (text) { try { call.selectedDisplayFrameRate = int.parse(text); - // ignore: empty_catches - } catch (e) {} + } catch (e) { + // No-op. + } }, decoration: const InputDecoration( labelText: 'Display FPS', @@ -610,8 +637,9 @@ Future mediaSettingDialog(BuildContext context, Call call) async { onChanged: (text) { try { call.selectedDisplayWidth = int.parse(text); - // ignore: empty_catches - } catch (e) {} + } catch (e) { + // No-op. + } }, decoration: const InputDecoration( labelText: 'Display width', @@ -625,8 +653,9 @@ Future mediaSettingDialog(BuildContext context, Call call) async { onChanged: (text) { try { call.selectedDisplayHeight = int.parse(text); - // ignore: empty_catches - } catch (e) {} + } catch (e) { + // No-op. + } }, decoration: const InputDecoration( labelText: 'Display height', @@ -684,12 +713,11 @@ Future mediaSettingDialog(BuildContext context, Call call) async { onChanged: (text) { try { call.selectedDeviceWidth = int.parse(text); - // ignore: empty_catches - } catch (e) {} + } catch (e) { + // No-op. + } }, - decoration: const InputDecoration( - labelText: 'Device width', - ), + decoration: const InputDecoration(labelText: 'Device width'), ), TextFormField( initialValue: call.selectedDeviceHeight == null @@ -699,92 +727,100 @@ Future mediaSettingDialog(BuildContext context, Call call) async { onChanged: (text) { try { call.selectedDeviceHeight = int.parse(text); - // ignore: empty_catches - } catch (e) {} + } catch (e) { + // No-op. + } }, - decoration: const InputDecoration( - labelText: 'Device height', - ), + decoration: const InputDecoration(labelText: 'Device height'), ), Row( children: [ TextButton( - onPressed: () async { - await call.setSendAudio(!_audioSend); - setStateSb(() { - _audioSend = !_audioSend; - }); - }, - child: Text( - '${_audioSend ? 'Disable' : 'Enable'} Audio Send')), + onPressed: () async { + await call.setSendAudio(!_audioSend); + setStateSb(() { + _audioSend = !_audioSend; + }); + }, + child: Text( + '${_audioSend ? 'Disable' : 'Enable'} Audio Send', + ), + ), TextButton( - onPressed: () async { - await call.setSendVideo(!_videoSend); - setStateSb(() { - _videoSend = !_videoSend; - }); - }, - child: Text( - '${_videoSend ? 'Disable' : 'Enable'} Video Send')), + onPressed: () async { + await call.setSendVideo(!_videoSend); + setStateSb(() { + _videoSend = !_videoSend; + }); + }, + child: Text( + '${_videoSend ? 'Disable' : 'Enable'} Video Send', + ), + ), ], ), Row( children: [ TextButton( - onPressed: () async { - await call.setRecvAudio(!_audioRecv); - setStateSb(() { - _audioRecv = !_audioRecv; - }); - }, - child: Text( - '${_audioRecv ? 'Disable' : 'Enable'} Audio Recv')), + onPressed: () async { + await call.setRecvAudio(!_audioRecv); + setStateSb(() { + _audioRecv = !_audioRecv; + }); + }, + child: Text( + '${_audioRecv ? 'Disable' : 'Enable'} Audio Recv', + ), + ), TextButton( - onPressed: () async { - await call.setRecvVideo(!_videoRecv); - setStateSb(() { - _videoRecv = !_videoRecv; - }); - }, - child: Text( - '${_videoRecv ? 'Disable' : 'Enable'} Video Recv')), + onPressed: () async { + await call.setRecvVideo(!_videoRecv); + setStateSb(() { + _videoRecv = !_videoRecv; + }); + }, + child: Text( + '${_videoRecv ? 'Disable' : 'Enable'} Video Recv', + ), + ), ], ), const SizedBox( height: 10, ), TextButton( - onPressed: () async { - var videoTrack = jason.DeviceVideoTrackConstraints(); - videoTrack.deviceId(call.videoDeviceId!); - if (call.selectedDeviceHeight != null) { - videoTrack.exactHeight(call.selectedDeviceHeight!); - } - if (call.selectedDeviceWidth != null) { - videoTrack.exactWidth(call.selectedDeviceWidth!); - } + onPressed: () async { + var videoTrack = jason.DeviceVideoTrackConstraints(); + videoTrack.deviceId(call.videoDeviceId!); + if (call.selectedDeviceHeight != null) { + videoTrack.exactHeight(call.selectedDeviceHeight!); + } + if (call.selectedDeviceWidth != null) { + videoTrack.exactWidth(call.selectedDeviceWidth!); + } - var displayTrack = jason.DisplayVideoTrackConstraints(); - if (call.videoDisplayId != null) { - displayTrack.deviceId(call.videoDisplayId!); - } - if (call.selectedDisplayHeight != null) { - displayTrack.exactHeight(call.selectedDisplayHeight!); - } - if (call.selectedDisplayWidth != null) { - displayTrack.exactWidth(call.selectedDisplayWidth!); - } - if (call.selectedDisplayFrameRate != null) { - displayTrack - .exactFrameRate(call.selectedDisplayFrameRate!); - } + var displayTrack = jason.DisplayVideoTrackConstraints(); + if (call.videoDisplayId != null) { + displayTrack.deviceId(call.videoDisplayId!); + } + if (call.selectedDisplayHeight != null) { + displayTrack.exactHeight(call.selectedDisplayHeight!); + } + if (call.selectedDisplayWidth != null) { + displayTrack.exactWidth(call.selectedDisplayWidth!); + } + if (call.selectedDisplayFrameRate != null) { + displayTrack + .exactFrameRate(call.selectedDisplayFrameRate!); + } - var audioTrack = jason.AudioTrackConstraints(); - audioTrack.deviceId(call.audioDeviceId!); + var audioTrack = jason.AudioTrackConstraints(); + audioTrack.deviceId(call.audioDeviceId!); - await call.setMedia(videoTrack, audioTrack, displayTrack); - }, - child: const Text('Set media setting')), + await call.setMedia(videoTrack, audioTrack, displayTrack); + }, + child: const Text('Set media setting'), + ), ], ); }, @@ -794,15 +830,17 @@ Future mediaSettingDialog(BuildContext context, Call call) async { ); } -Future controlApiCreateDialog(BuildContext context, Call call) { +Future controlApiCreateDialog(BuildContext context, Call call) { return showDialog( context: context, builder: (BuildContext context) { return AlertDialog( content: StatefulBuilder( builder: (BuildContext context, StateSetter setStateSb) { - return Column(mainAxisSize: MainAxisSize.min, children: [ - TextButton( + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( style: TextButton.styleFrom( foregroundColor: Colors.white, backgroundColor: Colors.blue, @@ -810,9 +848,10 @@ Future controlApiCreateDialog(BuildContext context, Call call) { child: const Text('Room'), onPressed: () async { await controlApiCreateRoomDialog(context, call); - }), - const SizedBox(height: 10), - TextButton( + }, + ), + const SizedBox(height: 10), + TextButton( style: TextButton.styleFrom( foregroundColor: Colors.white, backgroundColor: Colors.blue, @@ -820,9 +859,10 @@ Future controlApiCreateDialog(BuildContext context, Call call) { child: const Text('Member'), onPressed: () async { await controlApiCreateMemberDialog(context, call); - }), - const SizedBox(height: 10), - TextButton( + }, + ), + const SizedBox(height: 10), + TextButton( style: TextButton.styleFrom( foregroundColor: Colors.white, backgroundColor: Colors.blue, @@ -830,8 +870,10 @@ Future controlApiCreateDialog(BuildContext context, Call call) { child: const Text('Endpoint'), onPressed: () async { await controlApiCreateEndpointDialog(context, call); - }), - ]); + }, + ), + ], + ); }, ), ); @@ -839,7 +881,7 @@ Future controlApiCreateDialog(BuildContext context, Call call) { ); } -Future controlApiCreateRoomDialog(BuildContext context, Call call) { +Future controlApiCreateRoomDialog(BuildContext context, Call call) { return showDialog( context: context, builder: (BuildContext context) { @@ -847,36 +889,36 @@ Future controlApiCreateRoomDialog(BuildContext context, Call call) { return AlertDialog( content: StatefulBuilder( builder: (BuildContext context, StateSetter setStateSb) { - return Column(mainAxisSize: MainAxisSize.min, children: [ - Flexible( + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Flexible( - child: Text('local://'), - ), - const SizedBox(width: 10), - Expanded( - child: TextFormField( - initialValue: null, - onChanged: (newRoomId) async { - roomId = newRoomId; - }, - decoration: const InputDecoration( - hintText: 'Room ID', + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Flexible(child: Text('local://')), + const SizedBox(width: 10), + Expanded( + child: TextFormField( + initialValue: null, + onChanged: (newRoomId) => roomId = newRoomId, + decoration: + const InputDecoration(hintText: 'Room ID'), + ), ), - ), + ], ), - ], - )), - const SizedBox(height: 10), - TextButton( + ), + const SizedBox(height: 10), + TextButton( onPressed: () async { await call.controlApi.createRoom(roomId); Navigator.pop(context); }, - child: const Text('Create')) - ]); + child: const Text('Create'), + ) + ], + ); }, ), ); @@ -884,7 +926,7 @@ Future controlApiCreateRoomDialog(BuildContext context, Call call) { ); } -Future controlApiCreateMemberDialog(BuildContext context, Call call) { +Future controlApiCreateMemberDialog(BuildContext context, Call call) { return showDialog( context: context, builder: (BuildContext context) { @@ -898,118 +940,108 @@ Future controlApiCreateMemberDialog(BuildContext context, Call call) { return AlertDialog( content: StatefulBuilder( builder: (BuildContext context, StateSetter setStateSb) { - return Column(mainAxisSize: MainAxisSize.min, children: [ - Flexible( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Flexible( - child: Text('local://'), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newRoomId) async { - roomId = newRoomId; - }, - decoration: const InputDecoration( - hintText: 'Room ID', + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Flexible(child: Text('local://')), + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newRoomId) { + roomId = newRoomId; + }, + decoration: const InputDecoration(hintText: 'Room ID'), ), ), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newMemberId) async { - memberId = newMemberId; - }, - decoration: const InputDecoration( - hintText: 'Member ID', + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newMemberId) { + memberId = newMemberId; + }, + decoration: + const InputDecoration(hintText: 'Member ID'), ), ), - ), - ], - )), - const SizedBox(height: 10), - const Flexible( - child: Text('Credentials'), - ), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newCredentials) async { - credentials = newCredentials; - }, - decoration: const InputDecoration( - hintText: 'Credentials', - ), + ], + )), + const SizedBox(height: 10), + const Flexible( + child: Text('Credentials'), ), - ), - const SizedBox(height: 10), - const Flexible( - child: Text('Timeout'), - ), - Flexible( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(width: 10), - const Flexible( - child: Text('IDLE'), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newCredentials) async { + credentials = newCredentials; + }, + decoration: const InputDecoration(hintText: 'Credentials'), ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newIdle) async { - idle = newIdle; - }, - decoration: const InputDecoration( - hintText: '10s', + ), + const SizedBox(height: 10), + const Flexible(child: Text('Timeout')), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(width: 10), + const Flexible(child: Text('IDLE')), + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newIdle) async { + idle = newIdle; + }, + decoration: const InputDecoration(hintText: '10s'), ), ), - ), - const SizedBox(width: 10), - const Flexible( - child: Text('Reconnect'), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newReconnect) async { - reconnectTimeout = newReconnect; - }, - decoration: const InputDecoration( - hintText: '10s', + const SizedBox(width: 10), + const Flexible( + child: Text('Reconnect'), + ), + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newReconnect) async { + reconnectTimeout = newReconnect; + }, + decoration: const InputDecoration( + hintText: '10s', + ), ), ), - ), - const SizedBox(width: 10), - const Flexible( - child: Text('Ping'), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newPing) async { - ping = newPing; - }, - decoration: const InputDecoration( - hintText: '3s', + const SizedBox(width: 10), + const Flexible(child: Text('Ping')), + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newPing) async { + ping = newPing; + }, + decoration: const InputDecoration(hintText: '3s'), ), ), - ), - ], - )), - const SizedBox(height: 10), - TextButton( + ], + )), + const SizedBox(height: 10), + TextButton( onPressed: () async { - var member = Member(memberId, {}, Plain(credentials), - 'grpc://127.0.0.1:9099', 'grpc://127.0.0.1:9099'); + final member = Member( + memberId, + {}, + Plain(credentials), + 'grpc://127.0.0.1:9099', + 'grpc://127.0.0.1:9099', + ); member.idle_timeout = idle; member.reconnect_timeout = reconnectTimeout; @@ -1018,8 +1050,10 @@ Future controlApiCreateMemberDialog(BuildContext context, Call call) { await call.controlApi.createMember(roomId, member); Navigator.pop(context); }, - child: const Text('Create')) - ]); + child: const Text('Create'), + ) + ], + ); }, ), ); @@ -1027,7 +1061,7 @@ Future controlApiCreateMemberDialog(BuildContext context, Call call) { ); } -Future controlApiCreateEndpointDialog(BuildContext context, Call call) { +Future controlApiCreateEndpointDialog(BuildContext context, Call call) { return showDialog( context: context, builder: (BuildContext context) { @@ -1041,131 +1075,125 @@ Future controlApiCreateEndpointDialog(BuildContext context, Call call) { return AlertDialog( content: StatefulBuilder( builder: (BuildContext context, StateSetter setStateSb) { - return Column(mainAxisSize: MainAxisSize.min, children: [ - const SizedBox(height: 10), - const Text('Endpoint URI'), - Flexible( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Flexible( - child: Text('local://'), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newRoomId) async { - roomId = newRoomId; - }, - decoration: const InputDecoration( - hintText: 'Room ID', - ), - ), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newMemberId) async { - memberId = newMemberId; - }, - decoration: const InputDecoration( - hintText: 'Member ID', - ), - ), - ), - const SizedBox(width: 10), - Flexible( - child: TextFormField( - initialValue: null, - onChanged: (newEndpointId) async { - endpointId = newEndpointId; - }, - decoration: const InputDecoration( - hintText: 'Endpoint ID', + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 10), + const Text('Endpoint URI'), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Flexible(child: Text('local://')), + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newRoomId) async { + roomId = newRoomId; + }, + decoration: const InputDecoration(hintText: 'Room ID'), ), ), - ), - ], - )), - const SizedBox(height: 10), - const Flexible( - child: Text('Endpoint type'), - ), - Flexible( - child: DropdownButton( - value: endpointType, - icon: const Icon(Icons.arrow_downward), - elevation: 16, - style: const TextStyle(color: Colors.deepPurple), - underline: Container( - height: 2, - color: Colors.deepPurpleAccent, - ), - onChanged: (String? value) { - endpointType = value!; - }, - items: ['PlayEndpoint', 'PublishEndpoint'] - .map>((value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - ), - ), - const SizedBox(height: 10), - const Flexible( - child: Text('Source URI'), - ), - Flexible( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ const SizedBox(width: 10), - const Flexible( - child: Text('local://'), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newMemberId) async { + memberId = newMemberId; + }, + decoration: + const InputDecoration(hintText: 'Member ID'), + ), ), const SizedBox(width: 10), Flexible( child: TextFormField( initialValue: null, - onChanged: (newUrl) async { - url = newUrl; + onChanged: (newEndpointId) async { + endpointId = newEndpointId; }, decoration: const InputDecoration( - hintText: 'roomId/memberId/sourceId', + hintText: 'Endpoint ID', ), ), ), - ])), - const SizedBox(height: 10), - Flexible( - child: SwitchListTile( + ], + )), + const SizedBox(height: 10), + const Flexible(child: Text('Endpoint type')), + Flexible( + child: DropdownButton( + value: endpointType, + icon: const Icon(Icons.arrow_downward), + elevation: 16, + style: const TextStyle(color: Colors.deepPurple), + underline: Container( + height: 2, + color: Colors.deepPurpleAccent, + ), + onChanged: (String? value) { + endpointType = value!; + }, + items: ['PlayEndpoint', 'PublishEndpoint'] + .map>((value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + ), + const SizedBox(height: 10), + const Flexible(child: Text('Source URI')), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(width: 10), + const Flexible(child: Text('local://')), + const SizedBox(width: 10), + Flexible( + child: TextFormField( + initialValue: null, + onChanged: (newUrl) async { + url = newUrl; + }, + decoration: const InputDecoration( + hintText: 'roomId/memberId/sourceId', + ), + ), + ), + ], + ), + ), + const SizedBox(height: 10), + Flexible( + child: SwitchListTile( title: const Text('Force relay'), value: forceRelay, - onChanged: (v) => setStateSb(() { - forceRelay = v; - })), - ), - const SizedBox(height: 10), - TextButton( + onChanged: (v) => setStateSb(() => forceRelay = v), + ), + ), + const SizedBox(height: 10), + TextButton( onPressed: () async { if (endpointType == 'PlayEndpoint') { - var endpoint = WebRtcPlayEndpoint(endpointId, url); + final endpoint = WebRtcPlayEndpoint(endpointId, url); await call.controlApi .createPlayEndpoint(roomId, memberId, endpoint); } else { - var endpoint = + final endpoint = WebRtcPublishEndpoint(endpointId, P2pMode.Always); await call.controlApi .createPublishEndpoint(roomId, memberId, endpoint); } Navigator.pop(context); }, - child: const Text('Create')) - ]); + child: const Text('Create'), + ) + ], + ); }, ), ); diff --git a/flutter/example/lib/join_route.dart b/flutter/example/lib/join_route.dart index 70f870325..cd6099818 100644 --- a/flutter/example/lib/join_route.dart +++ b/flutter/example/lib/join_route.dart @@ -26,84 +26,68 @@ class _JoinRouteState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Jason demo'), - ), + appBar: AppBar(title: const Text('Jason demo')), body: Center( - child: Container( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Image.asset('assets/images/logo.png', height: 200), - TextFormField( - initialValue: defaultRoomId, - onChanged: (text) { - setState(() { - _roomId = text; - }); - }, - decoration: const InputDecoration( - hintText: 'Room ID', - ), - ), - TextFormField( - initialValue: defaultMemberId, - onChanged: (text) { - setState(() { - _memberId = text; - }); - }, - decoration: const InputDecoration( - hintText: 'Member ID', + child: ListView( + padding: const EdgeInsets.all(20), + children: [ + Image.asset('assets/images/logo.png', height: 200), + TextFormField( + initialValue: defaultRoomId, + onChanged: (text) => setState(() => _roomId = text), + decoration: const InputDecoration(hintText: 'Room ID'), + ), + TextFormField( + initialValue: defaultMemberId, + onChanged: (text) => setState(() => _memberId = text), + decoration: const InputDecoration(hintText: 'Member ID'), + ), + SwitchListTile( + title: const Text('Publish'), + value: isPublish, + onChanged: (v) => setState(() => isPublish = v), + ), + SwitchListTile( + title: const Text('Publish Video'), + value: publishVideo, + onChanged: (v) => setState(() => publishVideo = v), + ), + SwitchListTile( + title: const Text('Publish Audio'), + value: publishAudio, + onChanged: (v) => setState(() => publishAudio = v), + ), + SwitchListTile( + title: const Text('FakeMedia'), + value: fakeMedia, + onChanged: (v) => setState(() => fakeMedia = v), + ), + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.blue, + disabledForegroundColor: Colors.grey.withOpacity(0.38), + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CallRoute( + _roomId, + _memberId, + isPublish, + publishVideo, + publishAudio, + fakeMedia, ), ), - SwitchListTile( - title: const Text('Publish'), - value: isPublish, - onChanged: (v) => setState(() { - isPublish = v; - })), - SwitchListTile( - title: const Text('Publish Video'), - value: publishVideo, - onChanged: (v) => setState(() { - publishVideo = v; - })), - SwitchListTile( - title: const Text('Publish Audio'), - value: publishAudio, - onChanged: (v) => setState(() { - publishAudio = v; - })), - SwitchListTile( - title: const Text('FakeMedia'), - value: fakeMedia, - onChanged: (v) => setState(() { - fakeMedia = v; - })), - TextButton( - style: TextButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.blue, - disabledForegroundColor: Colors.grey.withOpacity(0.38), - ), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CallRoute( - _roomId, - _memberId, - isPublish, - publishVideo, - publishAudio, - fakeMedia))); - }, - child: const Text('Join Room'), - ) - ], - ))), + ); + }, + child: const Text('Join Room'), + ) + ], + ), + ), ); } } diff --git a/flutter/example/pubspec.lock b/flutter/example/pubspec.lock index e18f0c62b..c2c163dd5 100644 --- a/flutter/example/pubspec.lock +++ b/flutter/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: archive - sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265 + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "3.5.1" + version: "3.6.1" args: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: "direct main" description: name: faker_dart - sha256: aaf3756cb19947340af10602be86a607dd6d2acf007ea351cdea36a5533c77f7 + sha256: "73c082ed0d71e0824b9a59ba61640479d536588030b2c55033593d8bd820b204" url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.2.2" ffi: dependency: transitive description: @@ -212,10 +212,10 @@ packages: dependency: "direct main" description: name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.4" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -342,10 +342,10 @@ packages: dependency: "direct main" description: name: medea_flutter_webrtc - sha256: "6a472fda429341aaa777613332927f2fae8282736fc3a6d4dea4fb87a5ad9b11" + sha256: "821270c1151dfffdaba3db6aed50929204b23eff6aaba9d268c3fe041826e8ad" url: "https://pub.dev" source: hosted - version: "0.10.0-dev+rev.c8bcd8b26c90ce3a348d696a46b89890422b3413" + version: "0.10.0-dev+rev.b348da36900ad9ee40e3649e78575d2ecce45fa6" medea_jason: dependency: "direct main" description: @@ -429,18 +429,18 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.3.0" puppeteer: dependency: transitive description: name: puppeteer - sha256: "37293a2379ec5d022b7af6e5967304e74c69ebf7b72925b965a3c1ff9ccd9a4c" + sha256: de3f921154e5d336b14cdc05b674ac3db5701a5338f3cb0042868a5146f16e67 url: "https://pub.dev" source: hosted - version: "3.10.0" + version: "3.12.0" retry: dependency: "direct main" description: @@ -602,18 +602,18 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.0.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.0" webdriver: dependency: transitive description: @@ -631,5 +631,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index 9e4f5ea5a..6487f1af1 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: none environment: flutter: ">=3.10.0" - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: flutter: @@ -23,7 +23,7 @@ dependencies: faker_dart: ^0.2.1 freezed_annotation: ^2.2.0 http: ^0.13.6 - medea_flutter_webrtc: 0.10.0-dev+rev.c8bcd8b26c90ce3a348d696a46b89890422b3413 + medea_flutter_webrtc: 0.10.0-dev+rev.b348da36900ad9ee40e3649e78575d2ecce45fa6 retry: ^3.1.2 dev_dependencies: diff --git a/flutter/lib/medea_jason.dart b/flutter/lib/medea_jason.dart index f58db2a3f..783bd7e5a 100644 --- a/flutter/lib/medea_jason.dart +++ b/flutter/lib/medea_jason.dart @@ -9,13 +9,14 @@ export 'src/interface/media_display_details.dart'; export 'src/interface/reconnect_handle.dart'; export 'src/interface/room_close_reason.dart'; export 'src/interface/room_handle.dart'; -export 'src/native/jason.dart' if (dart.library.html) 'src/web/jason.dart'; +export 'src/native/jason.dart' + if (dart.library.js_interop) 'src/web/jason.dart'; export 'src/native/audio_track_constraints.dart' - if (dart.library.html) 'src/web/audio_track_constraints.dart'; + if (dart.library.js_interop) 'src/web/audio_track_constraints.dart'; export 'src/interface/device_video_track_constraints.dart' show FacingMode; export 'src/native/device_video_track_constraints.dart' - if (dart.library.html) 'src/web/device_video_track_constraints.dart'; + if (dart.library.js_interop) 'src/web/device_video_track_constraints.dart'; export 'src/native/display_video_track_constraints.dart' - if (dart.library.html) 'src/web/display_video_track_constraints.dart'; + if (dart.library.js_interop) 'src/web/display_video_track_constraints.dart'; export 'src/native/media_stream_settings.dart' - if (dart.library.html) 'src/web/media_stream_settings.dart'; + if (dart.library.js_interop) 'src/web/media_stream_settings.dart'; diff --git a/flutter/lib/src/interface/enums.dart b/flutter/lib/src/interface/enums.dart index 877763986..f51c6d3a7 100644 --- a/flutter/lib/src/interface/enums.dart +++ b/flutter/lib/src/interface/enums.dart @@ -1,2 +1,2 @@ export '../native/native_enums.dart' - if (dart.library.html) '../web/web_enums.dart'; + if (dart.library.js_interop) '../web/web_enums.dart'; diff --git a/flutter/lib/src/web/connection_handle.dart b/flutter/lib/src/web/connection_handle.dart index a006d0489..0b45ebd67 100644 --- a/flutter/lib/src/web/connection_handle.dart +++ b/flutter/lib/src/web/connection_handle.dart @@ -1,8 +1,6 @@ // ignore_for_file: avoid_web_libraries_in_flutter -import 'dart:js'; - -import 'package:js/js.dart'; +import 'dart:js_interop'; import '../interface/connection_handle.dart'; import '../interface/media_track.dart'; @@ -23,39 +21,39 @@ class WebConnectionHandle implements ConnectionHandle { @override void onClose(void Function() f) { - fallibleFunction(() => obj.on_close(allowInterop(f))); + fallibleFunction(() => obj.on_close(f.toJS)); } @override void onRemoteTrackAdded(void Function(RemoteMediaTrack) f) { - fallibleFunction(() => obj.on_remote_track_added(allowInterop((track) { - f(WebRemoteMediaTrack(track)); - }))); + void fn(JSAny? track) => + f(WebRemoteMediaTrack(track as wasm.RemoteMediaTrack)); + fallibleFunction(() => obj.on_remote_track_added(fn.toJS)); } @override void onQualityScoreUpdate(void Function(int) f) { - fallibleFunction(() => obj.on_quality_score_update(allowInterop(f))); + fallibleFunction(() => obj.on_quality_score_update(f.toJS)); } @override Future enableRemoteAudio() async { - await fallibleFuture(obj.enable_remote_audio()); + await fallibleFuture(obj.enable_remote_audio().toDart); } @override Future disableRemoteAudio() async { - await fallibleFuture(obj.disable_remote_audio()); + await fallibleFuture(obj.disable_remote_audio().toDart); } @override Future enableRemoteVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.enable_remote_video(kind?.index)); + await fallibleFuture(obj.enable_remote_video(kind?.index).toDart); } @override Future disableRemoteVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.disable_remote_video(kind?.index)); + await fallibleFuture(obj.disable_remote_video(kind?.index).toDart); } @moveSemantics diff --git a/flutter/lib/src/web/exceptions.dart b/flutter/lib/src/web/exceptions.dart index fd4deb37a..2f3d039c6 100644 --- a/flutter/lib/src/web/exceptions.dart +++ b/flutter/lib/src/web/exceptions.dart @@ -1,6 +1,5 @@ -// ignore_for_file: avoid_web_libraries_in_flutter - -import 'dart:js_util'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import '../interface/exceptions.dart'; import 'jason_wasm.dart' as wasm; @@ -9,12 +8,21 @@ import 'jason_wasm.dart' as wasm; /// /// Returns `null` in case if the provided exception is not from Jason. String? _getName(dynamic e) { - try { - var exceptionConstructor = getProperty(e, 'constructor'); - return getProperty(exceptionConstructor, 'name'); - } catch (e) { + if (e is! JSObject) { return null; } + + final constructor = e.getProperty('constructor'.toJS); + if (constructor is JSObject) { + if (constructor.hasProperty('name'.toJS).toDart) { + final name = constructor.getProperty('name'.toJS); + if (name is JSString) { + return name.toDart; + } + } + } + + return null; } /// Converts the provided [wasm] exception into the Dart exception. diff --git a/flutter/lib/src/web/jason_wasm.dart b/flutter/lib/src/web/jason_wasm.dart index 35aa5c157..bcacb5c19 100644 --- a/flutter/lib/src/web/jason_wasm.dart +++ b/flutter/lib/src/web/jason_wasm.dart @@ -3,13 +3,12 @@ @JS() library medea_jason; -import 'dart:html' as html; +import 'dart:js_interop'; -import 'package:js/js.dart'; -import 'package:js/js_util.dart' show promiseToFuture; +import 'package:web/web.dart' as web; @JS() -class FacingMode { +extension type FacingMode._(JSObject _) implements JSObject { external static num get User; external static num get Environment; external static num get Left; @@ -17,33 +16,33 @@ class FacingMode { } @JS() -class MediaSourceKind { +extension type MediaSourceKind._(JSObject _) implements JSObject { external static num get Device; external static num get Display; } @JS() -class MediaKind { +extension type MediaKind._(JSObject _) implements JSObject { external static num get Audio; external static num get Video; } @JS() -class LocalMediaInitExceptionKind { +extension type LocalMediaInitExceptionKind._(JSObject _) implements JSObject { external static num get GetUserMediaFailed; external static num get GetDisplayMediaFailed; external static num get LocalTrackIsEnded; } @JS() -class RpcClientExceptionKind { +extension type RpcClientExceptionKind._(JSObject _) implements JSObject { external static num get ConnectionLost; external static num get AuthorizationFailed; external static num get SessionFinished; } @JS() -class AudioTrackConstraints { +extension type AudioTrackConstraints._(JSObject _) implements JSObject { external void free(); external factory AudioTrackConstraints(); external void device_id(String device_id); @@ -52,46 +51,20 @@ class AudioTrackConstraints { } @JS() -class ConnectionHandle { +extension type ConnectionHandle._(JSObject _) implements JSObject { external void free(); - external void on_close(Function cb); + external void on_close(JSFunction cb); external String get_remote_member_id(); - external void on_remote_track_added(Function cb); - external void on_quality_score_update(Function cb); -} - -@JS('ConnectionHandle') -abstract class _ConnectionHandle { - external Promise disable_remote_audio(); - external Promise disable_remote_video(num? source_kind); - external Promise enable_remote_audio(); - external Promise enable_remote_video(num? source_kind); -} - -extension ConnectionHandleExtensions on ConnectionHandle { - Future disable_remote_audio() { - final tt = this as _ConnectionHandle; - return promiseToFuture(tt.disable_remote_audio()); - } - - Future disable_remote_video(num? source_kind) { - final tt = this as _ConnectionHandle; - return promiseToFuture(tt.disable_remote_video(source_kind)); - } - - Future enable_remote_audio() { - final tt = this as _ConnectionHandle; - return promiseToFuture(tt.enable_remote_audio()); - } - - Future enable_remote_video(num? source_kind) { - final tt = this as _ConnectionHandle; - return promiseToFuture(tt.enable_remote_video(source_kind)); - } + external void on_remote_track_added(JSFunction cb); + external void on_quality_score_update(JSFunction cb); + external JSPromise disable_remote_audio(); + external JSPromise disable_remote_video(num? source_kind); + external JSPromise enable_remote_audio(); + external JSPromise enable_remote_video(num? source_kind); } @JS() -class DeviceVideoTrackConstraints { +extension type DeviceVideoTrackConstraints._(JSObject _) implements JSObject { external void free(); external factory DeviceVideoTrackConstraints(); external void device_id(String device_id); @@ -108,7 +81,7 @@ class DeviceVideoTrackConstraints { } @JS() -class DisplayVideoTrackConstraints { +extension type DisplayVideoTrackConstraints._(JSObject _) implements JSObject { external void free(); external factory DisplayVideoTrackConstraints(); external void exact_height(num height); @@ -120,20 +93,20 @@ class DisplayVideoTrackConstraints { } @JS() -class EnumerateDevicesException { +extension type EnumerateDevicesException._(JSObject _) implements JSObject { external void free(); - external Error cause(); + external JSObject cause(); external String trace(); } @JS() -class FormatException { +extension type FormatException._(JSObject _) implements JSObject { external void free(); external String message(); } @JS() -class MediaDeviceDetails { +extension type MediaDeviceDetails._(JSObject _) implements JSObject { external void free(); external String device_id(); external num kind(); @@ -142,15 +115,15 @@ class MediaDeviceDetails { } @JS() -class InternalException { +extension type InternalException._(JSObject _) implements JSObject { external void free(); external String message(); - external dynamic cause(); + external String cause(); external String trace(); } @JS() -class Jason { +extension type Jason._(JSObject _) implements JSObject { external void free(); external factory Jason(); external RoomHandle init_room(); @@ -160,69 +133,44 @@ class Jason { } @JS() -class LocalMediaInitException { +extension type LocalMediaInitException._(JSObject _) implements JSObject { external void free(); external num kind(); external String message(); - external dynamic cause(); + external JSObject cause(); external String trace(); } @JS() -class LocalMediaTrack { +extension type LocalMediaTrack._(JSObject _) implements JSObject { external void free(); - external html.MediaStreamTrack get_track(); + external web.MediaStreamTrack get_track(); external num kind(); external num media_source_kind(); - external void on_enabled(Function cb); -} - -@JS('LocalMediaTrack') -abstract class _LocalMediaTrack { - external Promise state(); -} - -extension LocalMediaTrackExtensions on LocalMediaTrack { - Future state() { - final tt = this as _LocalMediaTrack; - return promiseToFuture(tt.state()); - } + external void on_enabled(JSFunction cb); + external JSPromise state(); } @JS() -class MediaManagerHandle { - external void on_device_change(Function cb); +extension type MediaManagerHandle._(JSObject _) implements JSObject { + external void on_device_change(JSFunction cb); external void free(); -} - -@JS('MediaManagerHandle') -abstract class _MediaManagerHandle { - external Promise> enumerate_devices(); - external Promise> init_local_tracks(MediaStreamSettings caps); -} - -extension MediaManagerHandleExtensions on MediaManagerHandle { - Future> enumerate_devices() { - final tt = this as _MediaManagerHandle; - return promiseToFuture(tt.enumerate_devices()); - } - - Future> init_local_tracks(MediaStreamSettings caps) { - final tt = this as _MediaManagerHandle; - return promiseToFuture(tt.init_local_tracks(caps)); - } + external JSPromise> enumerate_devices(); + external JSPromise> init_local_tracks( + MediaStreamSettings caps, + ); } @JS() -class MediaSettingsUpdateException { +extension type MediaSettingsUpdateException._(JSObject _) implements JSObject { external void free(); external String message(); - external dynamic cause(); + external JSObject cause(); external bool rolled_back(); } @JS() -class MediaStateTransitionException { +extension type MediaStateTransitionException._(JSObject _) implements JSObject { external void free(); external String message(); external String trace(); @@ -230,7 +178,7 @@ class MediaStateTransitionException { } @JS() -class MediaStreamSettings { +extension type MediaStreamSettings._(JSObject _) implements JSObject { external void free(); external factory MediaStreamSettings(); external void audio(AudioTrackConstraints constraints); @@ -238,51 +186,37 @@ class MediaStreamSettings { external void display_video(DisplayVideoTrackConstraints constraints); } -@JS() -class ReconnectHandle { - external void free(); -} - @JS('ReconnectHandle') -abstract class _ReconnectHandle { - external Promise reconnect_with_delay(num delay_ms); - external Promise reconnect_with_backoff(num starting_delay_ms, - num multiplier, num max_delay, num? max_elapsed_time_ms); -} - -extension ReconnectHandleExtensions on ReconnectHandle { - Future reconnect_with_delay(num delay_ms) { - final tt = this as _ReconnectHandle; - return promiseToFuture(tt.reconnect_with_delay(delay_ms)); - } - - Future reconnect_with_backoff(num starting_delay_ms, num multiplier, - num max_delay, num? max_elapsed_time_ms) { - final tt = this as _ReconnectHandle; - return promiseToFuture(tt.reconnect_with_backoff( - starting_delay_ms, multiplier, max_delay, max_elapsed_time_ms)); - } +extension type ReconnectHandle._(JSObject _) implements JSObject { + external void free(); + external JSPromise reconnect_with_delay(num delay_ms); + external JSPromise reconnect_with_backoff( + num starting_delay_ms, + num multiplier, + num max_delay, + num? max_elapsed_time_ms, + ); } @JS() -class RemoteMediaTrack { +extension type RemoteMediaTrack._(JSObject _) implements JSObject { external void free(); - external html.MediaStreamTrack get_track(); + external web.MediaStreamTrack get_track(); external bool enabled(); external bool muted(); - external void on_enabled(Function cb); - external void on_disabled(Function cb); - external void on_muted(Function cb); - external void on_unmuted(Function cb); - external void on_stopped(Function cb); - external void on_media_direction_changed(Function cb); + external void on_enabled(JSFunction cb); + external void on_disabled(JSFunction cb); + external void on_muted(JSFunction cb); + external void on_unmuted(JSFunction cb); + external void on_stopped(JSFunction cb); + external void on_media_direction_changed(JSFunction cb); external num kind(); external num media_source_kind(); external num media_direction(); } @JS() -class RoomCloseReason { +extension type RoomCloseReason._(JSObject _) implements JSObject { external void free(); external String reason(); external bool is_closed_by_server(); @@ -290,128 +224,45 @@ class RoomCloseReason { } @JS() -class RoomHandle { +extension type RoomHandle._(JSObject _) implements JSObject { external void free(); - external void on_new_connection(Function cb); - external void on_close(Function cb); - external void on_local_track(Function cb); - external void on_failed_local_media(Function cb); - external void on_connection_loss(Function cb); -} - -@JS('RoomHandle') -abstract class _RoomHandle { - external Promise join(String token); - external Promise set_local_media_settings( - MediaStreamSettings settings, bool stop_first, bool rollback_on_fail); - external Promise mute_audio(); - external Promise unmute_audio(); - external Promise mute_video(num? source_kind); - external Promise unmute_video(num? source_kind); - external Promise disable_audio(); - external Promise enable_audio(); - external Promise disable_video(num? source_kind); - external Promise enable_video(num? source_kind); - external Promise disable_remote_audio(); - external Promise disable_remote_video(num? source_kind); - external Promise enable_remote_audio(); - external Promise enable_remote_video(num? source_kind); -} - -extension RoomHandleExtensions on RoomHandle { - Future join(String token) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.join(token)); - } - - Future set_local_media_settings( - MediaStreamSettings settings, bool stop_first, bool rollback_on_fail) { - final tt = this as _RoomHandle; - return promiseToFuture( - tt.set_local_media_settings(settings, stop_first, rollback_on_fail)); - } - - Future mute_audio() { - final tt = this as _RoomHandle; - return promiseToFuture(tt.mute_audio()); - } - - Future unmute_audio() { - final tt = this as _RoomHandle; - return promiseToFuture(tt.unmute_audio()); - } - - Future mute_video(num? source_kind) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.mute_video(source_kind)); - } - - Future unmute_video(num? source_kind) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.unmute_video(source_kind)); - } - - Future disable_audio() { - final tt = this as _RoomHandle; - return promiseToFuture(tt.disable_audio()); - } - - Future enable_audio() { - final tt = this as _RoomHandle; - return promiseToFuture(tt.enable_audio()); - } - - Future disable_video(num? source_kind) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.disable_video(source_kind)); - } - - Future enable_video(num? source_kind) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.enable_video(source_kind)); - } - - Future disable_remote_audio() { - final tt = this as _RoomHandle; - return promiseToFuture(tt.disable_remote_audio()); - } - - Future disable_remote_video(num? source_kind) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.disable_remote_video(source_kind)); - } - - Future enable_remote_audio() { - final tt = this as _RoomHandle; - return promiseToFuture(tt.enable_remote_audio()); - } - - Future enable_remote_video(num? source_kind) { - final tt = this as _RoomHandle; - return promiseToFuture(tt.enable_remote_video(source_kind)); - } + external void on_new_connection(JSFunction cb); + external void on_close(JSFunction cb); + external void on_local_track(JSFunction cb); + external void on_failed_local_media(JSFunction cb); + external void on_connection_loss(JSFunction cb); + external JSPromise join(String token); + external JSPromise set_local_media_settings( + MediaStreamSettings settings, + bool stop_first, + bool rollback_on_fail, + ); + external JSPromise mute_audio(); + external JSPromise unmute_audio(); + external JSPromise mute_video(num? source_kind); + external JSPromise unmute_video(num? source_kind); + external JSPromise disable_audio(); + external JSPromise enable_audio(); + external JSPromise disable_video(num? source_kind); + external JSPromise enable_video(num? source_kind); + external JSPromise disable_remote_audio(); + external JSPromise disable_remote_video(num? source_kind); + external JSPromise enable_remote_audio(); + external JSPromise enable_remote_video(num? source_kind); } @JS() -class RpcClientException { +extension type RpcClientException._(JSObject _) implements JSObject { external void free(); external num kind(); external String message(); - external dynamic cause(); + external JSObject cause(); external String trace(); } @JS() -class StateError { +extension type StateError._(JSObject _) implements JSObject { external void free(); external String message(); external String trace(); } - -@JS() -abstract class Promise { - external factory Promise( - void Function(void Function(T result) resolve, Function reject) executor); - external Promise then(void Function(T result) onFulfilled, - [Function onRejected]); -} diff --git a/flutter/lib/src/web/local_media_track.dart b/flutter/lib/src/web/local_media_track.dart index 461e47d82..fd0916009 100644 --- a/flutter/lib/src/web/local_media_track.dart +++ b/flutter/lib/src/web/local_media_track.dart @@ -1,5 +1,7 @@ // ignore_for_file: implementation_imports +import 'dart:js_interop'; + import 'package:medea_flutter_webrtc/medea_flutter_webrtc.dart' as webrtc; import 'package:medea_flutter_webrtc/src/platform/web/media_stream_track.dart'; @@ -22,7 +24,8 @@ class WebLocalMediaTrack implements LocalMediaTrack { @override MediaSourceKind mediaSourceKind() { return fallibleFunction( - () => MediaSourceKind.values[obj.media_source_kind().toInt()]); + () => MediaSourceKind.values[obj.media_source_kind().toInt()], + ); } @override @@ -32,9 +35,7 @@ class WebLocalMediaTrack implements LocalMediaTrack { @override void onEnded(OnEndedCallback f) { - obj.get_track().onEnded.listen((event) { - f(); - }); + obj.get_track().onended = f.toJS; } @moveSemantics @@ -45,8 +46,8 @@ class WebLocalMediaTrack implements LocalMediaTrack { @override Future state() async { - var index = await fallibleFuture(obj.state()); - return MediaStreamTrackState.values[index]; + final index = await fallibleFuture(obj.state().toDart) as JSNumber; + return MediaStreamTrackState.values[index.toDartInt]; } @override diff --git a/flutter/lib/src/web/media_manager.dart b/flutter/lib/src/web/media_manager.dart index 35013fa40..40c70f2e7 100644 --- a/flutter/lib/src/web/media_manager.dart +++ b/flutter/lib/src/web/media_manager.dart @@ -1,9 +1,11 @@ // ignore_for_file: implementation_imports +import 'package:medea_flutter_webrtc/src/platform/web/audio_renderer.dart' + as audio_renderer; import 'package:medea_flutter_webrtc/src/platform/web/video_renderer.dart' as video_renderer; -import 'package:js/js.dart'; +import 'dart:js_interop'; import '../interface/media_device_details.dart'; import '../interface/media_display_details.dart'; @@ -25,15 +27,21 @@ class WebMediaManagerHandle implements MediaManagerHandle { @override Future> initLocalTracks( base_settings.MediaStreamSettings caps) async { - var tracks = await fallibleFuture( - obj.init_local_tracks((caps as MediaStreamSettings).obj)); - return tracks.map((t) => WebLocalMediaTrack(t)).toList(); + final tracks = await fallibleFuture( + obj.init_local_tracks((caps as MediaStreamSettings).obj).toDart, + ); + + return tracks.toDart + .map((t) => WebLocalMediaTrack(t as wasm.LocalMediaTrack)) + .toList(); } @override Future> enumerateDevices() async { - var tracks = await fallibleFuture(obj.enumerate_devices()); - return tracks.map((t) => WebMediaDeviceDetails(t)).toList(); + final tracks = await fallibleFuture(obj.enumerate_devices().toDart); + return tracks.toDart + .map((t) => WebMediaDeviceDetails(t as wasm.MediaDeviceDetails)) + .toList(); } @override @@ -50,11 +58,12 @@ class WebMediaManagerHandle implements MediaManagerHandle { @override Future setOutputAudioId(String deviceId) async { video_renderer.setOutputAudioSinkId(deviceId); + audio_renderer.setOutputAudioSinkId(deviceId); } @override - void onDeviceChange(Function cb) { - obj.on_device_change(allowInterop(cb)); + void onDeviceChange(void Function() cb) { + obj.on_device_change(cb.toJS); } @override diff --git a/flutter/lib/src/web/reconnect_handle.dart b/flutter/lib/src/web/reconnect_handle.dart index 4690669b3..e1e5558bd 100644 --- a/flutter/lib/src/web/reconnect_handle.dart +++ b/flutter/lib/src/web/reconnect_handle.dart @@ -1,3 +1,5 @@ +import 'dart:js_interop'; + import '../interface/reconnect_handle.dart'; import '../util/move_semantic.dart'; import 'exceptions.dart'; @@ -10,15 +12,26 @@ class WebReconnectHandle implements ReconnectHandle { @override Future reconnectWithDelay(int delayMs) async { - await fallibleFuture(obj.reconnect_with_delay(delayMs)); + await fallibleFuture(obj.reconnect_with_delay(delayMs).toDart); } @override Future reconnectWithBackoff( - int startingDelayMs, double multiplier, int maxDelay, - [int? maxElapsedTimeMs]) async { - await fallibleFuture(obj.reconnect_with_backoff( - startingDelayMs, multiplier, maxDelay, maxElapsedTimeMs)); + int startingDelayMs, + double multiplier, + int maxDelay, [ + int? maxElapsedTimeMs, + ]) async { + await fallibleFuture( + obj + .reconnect_with_backoff( + startingDelayMs, + multiplier, + maxDelay, + maxElapsedTimeMs, + ) + .toDart, + ); } @moveSemantics diff --git a/flutter/lib/src/web/remote_media_track.dart b/flutter/lib/src/web/remote_media_track.dart index cecdca827..f7a0cbd5c 100644 --- a/flutter/lib/src/web/remote_media_track.dart +++ b/flutter/lib/src/web/remote_media_track.dart @@ -1,6 +1,7 @@ // ignore_for_file: implementation_imports -import 'package:js/js.dart'; +import 'dart:js_interop'; + import 'package:medea_flutter_webrtc/medea_flutter_webrtc.dart' as webrtc; import 'package:medea_flutter_webrtc/src/platform/web/media_stream_track.dart'; @@ -43,17 +44,17 @@ class WebRemoteMediaTrack implements RemoteMediaTrack { @override void onMuted(void Function() f) { - fallibleFunction(() => obj.on_muted(allowInterop(f))); + fallibleFunction(() => obj.on_muted(f.toJS)); } @override void onUnmuted(void Function() f) { - fallibleFunction(() => obj.on_unmuted(allowInterop(f))); + fallibleFunction(() => obj.on_unmuted(f.toJS)); } @override void onStopped(void Function() f) { - fallibleFunction(() => obj.on_stopped(allowInterop(f))); + fallibleFunction(() => obj.on_stopped(f.toJS)); } @moveSemantics @@ -64,7 +65,9 @@ class WebRemoteMediaTrack implements RemoteMediaTrack { @override void onMediaDirectionChanged(void Function(TrackMediaDirection) f) { - fallibleFunction(() => obj.on_media_direction_changed( - allowInterop((i) => f(TrackMediaDirection.values[i])))); + void fn(JSAny? i) => + f(TrackMediaDirection.values[(i as JSNumber).toDartInt]); + + fallibleFunction(() => obj.on_media_direction_changed(fn.toJS)); } } diff --git a/flutter/lib/src/web/room_handle.dart b/flutter/lib/src/web/room_handle.dart index 9c27bac75..65345868c 100644 --- a/flutter/lib/src/web/room_handle.dart +++ b/flutter/lib/src/web/room_handle.dart @@ -1,4 +1,4 @@ -import 'package:js/js.dart'; +import 'dart:js_interop'; import '../interface/connection_handle.dart'; import '../interface/media_stream_settings.dart' as base_settings; @@ -22,109 +22,118 @@ class WebRoomHandle implements RoomHandle { @override Future join(String token) async { - await fallibleFuture(obj.join(token)); + await fallibleFuture(obj.join(token).toDart); } @override Future setLocalMediaSettings(base_settings.MediaStreamSettings settings, bool stopFirst, bool rollbackOnFail) async { - await fallibleFuture(obj.set_local_media_settings( - (settings as MediaStreamSettings).obj, stopFirst, rollbackOnFail)); + await fallibleFuture( + obj + .set_local_media_settings( + (settings as MediaStreamSettings).obj, + stopFirst, + rollbackOnFail, + ) + .toDart, + ); } @override Future muteAudio() async { - await fallibleFuture(obj.mute_audio()); + await fallibleFuture(obj.mute_audio().toDart); } @override Future unmuteAudio() async { - await fallibleFuture(obj.unmute_audio()); + await fallibleFuture(obj.unmute_audio().toDart); } @override Future enableAudio() async { - await fallibleFuture(obj.enable_audio()); + await fallibleFuture(obj.enable_audio().toDart); } @override Future disableAudio() async { - await fallibleFuture(obj.disable_audio()); + await fallibleFuture(obj.disable_audio().toDart); } @override Future muteVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.mute_video(kind?.index)); + await fallibleFuture(obj.mute_video(kind?.index).toDart); } @override Future unmuteVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.unmute_video(kind?.index)); + await fallibleFuture(obj.unmute_video(kind?.index).toDart); } @override Future enableVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.enable_video(kind?.index)); + await fallibleFuture(obj.enable_video(kind?.index).toDart); } @override Future disableVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.disable_video(kind?.index)); + await fallibleFuture(obj.disable_video(kind?.index).toDart); } @override Future enableRemoteAudio() async { - await fallibleFuture(obj.enable_remote_audio()); + await fallibleFuture(obj.enable_remote_audio().toDart); } @override Future disableRemoteAudio() async { - await fallibleFuture(obj.disable_remote_audio()); + await fallibleFuture(obj.disable_remote_audio().toDart); } @override Future enableRemoteVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.enable_remote_video(kind?.index)); + await fallibleFuture(obj.enable_remote_video(kind?.index).toDart); } @override Future disableRemoteVideo([MediaSourceKind? kind]) async { - await fallibleFuture(obj.disable_remote_video(kind?.index)); + await fallibleFuture(obj.disable_remote_video(kind?.index).toDart); } @override void onNewConnection(void Function(ConnectionHandle) f) { - fallibleFunction(() => obj.on_new_connection(allowInterop((handle) { - f(WebConnectionHandle(handle)); - }))); + void fn(JSAny? handle) => + f(WebConnectionHandle(handle as wasm.ConnectionHandle)); + + fallibleFunction(() => obj.on_new_connection(fn.toJS)); } @override void onClose(void Function(RoomCloseReason) f) { - fallibleFunction(() => obj.on_close(allowInterop((reason) { - f(WebRoomCloseReason(reason)); - }))); + void fn(JSAny? reason) => + f(WebRoomCloseReason(reason as wasm.RoomCloseReason)); + fallibleFunction(() => obj.on_close(fn.toJS)); } @override void onLocalTrack(void Function(LocalMediaTrack) f) { - fallibleFunction(() => obj.on_local_track(allowInterop((track) { - f(WebLocalMediaTrack(track)); - }))); + void fn(JSAny? track) => + f(WebLocalMediaTrack(track as wasm.LocalMediaTrack)); + fallibleFunction( + () => obj.on_local_track(fn.toJS), + ); } @override void onConnectionLoss(void Function(ReconnectHandle) f) { - fallibleFunction(() => obj.on_connection_loss(allowInterop((handle) { - f(WebReconnectHandle(handle)); - }))); + void fn(JSAny? handle) => + f(WebReconnectHandle(handle as wasm.ReconnectHandle)); + fallibleFunction(() => obj.on_connection_loss(fn.toJS)); } @override void onFailedLocalMedia(void Function(Object) f) { - fallibleFunction(() => obj.on_failed_local_media(allowInterop((e) { - f(convertException(e)); - }))); + void fn(JSAny? e) => f(convertException(e)); + fallibleFunction(() => obj.on_failed_local_media(fn.toJS)); } @moveSemantics diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index aaeb59988..ab8edd0f7 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/instrumentisto/medea-jason/blob/master/flutter environment: flutter: ">=3.10.0" - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: flutter: @@ -14,13 +14,13 @@ dependencies: flutter_rust_bridge: 1.82.4 # should be the same as in `Cargo.lock` freezed_annotation: ^2.2.0 http: ^0.13.5 - js: ^0.6.5 json_annotation: ^4.7.0 - medea_flutter_webrtc: 0.10.0-dev+rev.c8bcd8b26c90ce3a348d696a46b89890422b3413 + medea_flutter_webrtc: 0.10.0-dev+rev.b348da36900ad9ee40e3649e78575d2ecce45fa6 meta: ^1.8.0 retry: ^3.1.0 tuple: ^2.0.1 uuid: ^3.0.7 + web: ">=0.5.1 <2.0.0" dev_dependencies: build_runner: ^2.4.5 diff --git a/src/platform/dart/transport.rs b/src/platform/dart/transport.rs index 35a9998c3..ea5fe20cf 100644 --- a/src/platform/dart/transport.rs +++ b/src/platform/dart/transport.rs @@ -224,7 +224,9 @@ impl RpcTransport for WebSocketRpcTransport { match state { TransportState::Open => unsafe { let msg = serde_json::to_string(msg).unwrap(); - transport::send(handle.get(), string_into_c_str(msg)).unwrap(); + transport::send(handle.get(), string_into_c_str(msg)) + .map_err(TransportError::SendMessage) + .map_err(tracerr::wrap!())?; Ok(()) }, TransportState::Connecting