From 25df00b4e950ba00ea30c8bdeaedca4b60ae75e0 Mon Sep 17 00:00:00 2001 From: Hamad alghanim Date: Mon, 21 Oct 2024 11:35:35 -0600 Subject: [PATCH 1/4] upgraded dependency of websocket --- README.md | 9 +- pubspec.lock | 256 ++++++++++++++++++++++++++++++--------------------- pubspec.yaml | 6 +- 3 files changed, 161 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index bdcc483..d7c1ac0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ ![Pub](https://img.shields.io/pub/v/action_cable) + + [![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-) + # ActionCable in Dart @@ -14,17 +17,17 @@ This is a dart port of the client and protocol implementation which is available ### Connecting to a channel 🙌 ```dart -cable = ActionCable.Connect( +cable = ActionCable.connect( "ws://10.0.2.2:3000/cable", headers: { "Authorization": "Some Token", }, onConnected: (){ print("connected"); - }, + }, onConnectionLost: () { print("connection lost"); - }, + }, onCannotConnect: () { print("cannot connect"); }); diff --git a/pubspec.lock b/pubspec.lock index 2cc3796..1b8defa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,337 +5,385 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" source: hosted - version: "0.41.2" + version: "6.4.1" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "2.6.0" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "5bbf32bc9e518d41ec49718e2931cd4527292c9b0c6d2dffcf7fe6b9a8a8cf72" + url: "https://pub.dev" source: hosted version: "2.1.0" charcode: dependency: transitive description: name: charcode - url: "https://pub.dartlang.org" + sha256: "8e36feea6de5ea69f2199f29cf42a450a855738c498b57c0b980e2d3cca9c362" + url: "https://pub.dev" source: hosted version: "1.2.0" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.19.1" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.1.2" coverage: dependency: transitive description: name: coverage - url: "https://pub.dartlang.org" + sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" + url: "https://pub.dev" source: hosted - version: "0.13.9" + version: "1.10.0" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: "8be10341257b613566fdc9fd073c46f7c032ed329b1c732bda17aca29f2366c8" + url: "https://pub.dev" source: hosted version: "3.0.0" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" glob: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "2.1.2" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.dartlang.org" + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" + url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "4.1.1" io: dependency: transitive description: name: io - url: "https://pub.dartlang.org" + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" source: hosted - version: "0.3.4" + version: "1.0.4" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.7.1" logging: dependency: transitive description: name: logging - url: "https://pub.dartlang.org" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" source: hosted - version: "0.11.4" + version: "1.3.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.16+1" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.16.0" mime: dependency: transitive description: name: mime - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.6+3" - node_interop: - dependency: transitive - description: - name: node_interop - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - node_io: - dependency: transitive - description: - name: node_io - url: "https://pub.dartlang.org" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "2.0.0" node_preamble: dependency: transitive description: name: node_preamble - url: "https://pub.dartlang.org" + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" source: hosted - version: "1.4.8" + version: "2.0.2" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted - version: "1.9.3" + version: "2.1.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" + url: "https://pub.dev" source: hosted version: "1.8.0" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.11.0" pool: dependency: transitive description: name: pool - url: "https://pub.dartlang.org" + sha256: "05955e3de2683e1746222efd14b775df7131139e07695dc8e24650f6b4204504" + url: "https://pub.dev" source: hosted version: "1.5.0" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" source: hosted - version: "1.4.4" + version: "2.1.4" shelf: dependency: transitive description: name: shelf - url: "https://pub.dartlang.org" + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" source: hosted - version: "0.7.5" + version: "1.4.2" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - url: "https://pub.dartlang.org" + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.2" shelf_static: dependency: transitive description: name: shelf_static - url: "https://pub.dartlang.org" + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" source: hosted - version: "0.2.8" + version: "1.1.3" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.dartlang.org" + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" source: hosted - version: "0.2.4+1" + version: "2.0.0" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - url: "https://pub.dartlang.org" + sha256: "8c463326277f68a628abab20580047b419c2ff66756fd0affd451f73f9508c11" + url: "https://pub.dev" source: hosted version: "2.1.0" source_maps: dependency: transitive description: name: source_maps - url: "https://pub.dartlang.org" + sha256: "52de2200bb098de739794c82d09c41ac27b2e42fd7e23cce7b9c74bf653c7296" + url: "https://pub.dev" source: hosted version: "0.10.10" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: d5f89a9e52b36240a80282b3dc0667dd36e53459717bb17b8fb102d30496606a + url: "https://pub.dev" source: hosted version: "1.8.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: f8d9f247e2f9f90e32d1495ff32dac7e4ae34ffa7194c5ff8fcc0fd0e52df774 + url: "https://pub.dev" source: hosted version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: db47e4797198ee601990820437179bb90219f918962318d494ada2b4b11e6f6d + url: "https://pub.dev" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: dd11571b8a03f7cadcf91ec26a77e02bfbd6bbba2a512924d3116646b4198fc4 + url: "https://pub.dev" source: hosted version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a88162591b02c1f3a3db3af8ce1ea2b374bd75a7bb8d5e353bcfbdc79d719830 + url: "https://pub.dev" source: hosted version: "1.2.0" test: dependency: "direct dev" description: name: test - url: "https://pub.dartlang.org" + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + url: "https://pub.dev" source: hosted - version: "1.16.5" + version: "1.25.8" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + url: "https://pub.dev" source: hosted - version: "0.2.19" + version: "0.7.3" test_core: dependency: transitive description: name: test_core - url: "https://pub.dartlang.org" + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" + url: "https://pub.dev" source: hosted - version: "0.3.15" + version: "0.6.5" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" + url: "https://pub.dev" source: hosted version: "1.3.0" vm_service: dependency: transitive description: name: vm_service - url: "https://pub.dartlang.org" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "14.3.0" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" source: hosted - version: "0.9.7+15" + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" web_socket_channel: dependency: "direct main" description: name: web_socket_channel - url: "https://pub.dartlang.org" + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.1" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - url: "https://pub.dartlang.org" + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "1.2.1" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "3.1.2" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=3.4.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 517e7f8..dc0b665 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,12 @@ name: action_cable -version: 1.2.0 +version: 1.2.1 description: >- ActionCable client port in dart, available in web, dartVM and flutter. homepage: https://github.com/namiwang/actioncable_dart documentation: https://github.com/namiwang/actioncable_dart environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: ">=3.2.0 <4.0.0" dependencies: - web_socket_channel: ^2.0.0 + web_socket_channel: ^3.0.1 dev_dependencies: test: From 651e491675bc8a814cd88437b086c3d579c14e74 Mon Sep 17 00:00:00 2001 From: Hamad alghanim Date: Mon, 21 Oct 2024 11:35:46 -0600 Subject: [PATCH 2/4] Refactor ActionCable.connect function and update dependencies - Refactored the ActionCable.connect function to improve readability and leverage Dart's null safety. - Updated the websocket dependency to the latest version. BREAKING CHANGE: - Changed the connect function from `ActionCable.Connect` to `ActionCable.connect`. Closes #13 --- CHANGELOG.md | 11 +++ lib/action_cable.dart | 189 +++++++++++++++++++++++------------------- 2 files changed, 117 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ebbfd6..73cbe00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,3 +16,14 @@ Major update. - null safety - updated dependencies + +# 2021-04-01 1.2.1 + +### Breaking Changes + +- change connect function from `ActionCable.Connect` to `ActionCable.connect` + +### Updates + +- updated websocket dependency +- improve readability of the core code by leveraging dart's null safety diff --git a/lib/action_cable.dart b/lib/action_cable.dart index 5556103..db52dc7 100644 --- a/lib/action_cable.dart +++ b/lib/action_cable.dart @@ -1,94 +1,124 @@ import 'dart:async'; import 'dart:convert'; - import 'package:web_socket_channel/io.dart'; - import 'channel_id.dart'; -typedef _OnConnectedFunction = void Function(); -typedef _OnConnectionLostFunction = void Function(); -typedef _OnCannotConnectFunction = void Function(); -typedef _OnChannelSubscribedFunction = void Function(); -typedef _OnChannelDisconnectedFunction = void Function(); -typedef _OnChannelMessageFunction = void Function(Map message); +typedef OnConnectedFunction = void Function(); +typedef OnConnectionLostFunction = void Function(); +typedef OnCannotConnectFunction = void Function(); +typedef OnChannelSubscribedFunction = void Function(); +typedef OnChannelDisconnectedFunction = void Function(); +typedef OnChannelMessageFunction = void Function(Map message); class ActionCable { DateTime? _lastPing; - late Timer _timer; - late IOWebSocketChannel _socketChannel; - late StreamSubscription _listener; - _OnConnectedFunction? onConnected; - _OnCannotConnectFunction? onCannotConnect; - _OnConnectionLostFunction? onConnectionLost; - Map _onChannelSubscribedCallbacks = {}; - Map _onChannelDisconnectedCallbacks = - {}; - Map _onChannelMessageCallbacks = {}; - - ActionCable.Connect( + Timer? _timer; + IOWebSocketChannel? _socketChannel; + StreamSubscription? _listener; + + final OnConnectedFunction? onConnected; + final OnConnectionLostFunction? onConnectionLost; + final OnCannotConnectFunction? onCannotConnect; + + final Map + _onChannelSubscribedCallbacks = {}; + final Map + _onChannelDisconnectedCallbacks = {}; + final Map _onChannelMessageCallbacks = {}; + + ActionCable.connect( String url, { - Map headers: const {}, + Map headers = const {}, this.onConnected, this.onConnectionLost, this.onCannotConnect, }) { - // rails gets a ping every 3 seconds - _socketChannel = IOWebSocketChannel.connect(url, - headers: headers, pingInterval: Duration(seconds: 3)); - _listener = _socketChannel.stream.listen(_onData, onError: (_) { - this.disconnect(); // close a socket and the timer - if (this.onCannotConnect != null) this.onCannotConnect!(); - }); - _timer = Timer.periodic(const Duration(seconds: 3), healthCheck); + _connect(url, headers); + } + + void _connect(String url, Map headers) { + _socketChannel = IOWebSocketChannel.connect( + url, + headers: headers, + pingInterval: const Duration(seconds: 3), + ); + + _listener = _socketChannel?.stream.listen( + _onData, + onError: (error) { + _handleError(); + }, + onDone: () { + _handleDone(); + }, + ); + + _timer = Timer.periodic(const Duration(seconds: 3), _healthCheck); } void disconnect() { - _timer.cancel(); - _socketChannel.sink.close(); - _listener.cancel(); + _timer?.cancel(); + _listener?.cancel(); + _socketChannel?.sink.close(); + _socketChannel = null; } - // check if there is no ping for 3 seconds and signal a [onConnectionLost] if - // there is no ping for more than 6 seconds - void healthCheck(_) { - if (_lastPing == null) { - return; - } - if (DateTime.now().difference(_lastPing!) > Duration(seconds: 6)) { - this.disconnect(); - if (this.onConnectionLost != null) this.onConnectionLost!(); + void _handleError() { + disconnect(); + onCannotConnect?.call(); + } + + void _handleDone() { + disconnect(); + onConnectionLost?.call(); + } + + void _healthCheck(Timer timer) { + if (_lastPing == null) return; + if (DateTime.now().difference(_lastPing!) > const Duration(seconds: 6)) { + disconnect(); + onConnectionLost?.call(); } } - // channelName being 'Chat' will be considered as 'ChatChannel', - // 'Chat', { id: 1 } => { channel: 'ChatChannel', id: 1 } - void subscribe(String channelName, - {Map? channelParams, - _OnChannelSubscribedFunction? onSubscribed, - _OnChannelDisconnectedFunction? onDisconnected, - _OnChannelMessageFunction? onMessage}) { + void subscribe( + String channelName, { + Map? channelParams, + OnChannelSubscribedFunction? onSubscribed, + OnChannelDisconnectedFunction? onDisconnected, + OnChannelMessageFunction? onMessage, + }) { final channelId = encodeChannelId(channelName, channelParams); _onChannelSubscribedCallbacks[channelId] = onSubscribed; _onChannelDisconnectedCallbacks[channelId] = onDisconnected; _onChannelMessageCallbacks[channelId] = onMessage; - _send({'identifier': channelId, 'command': 'subscribe'}); + _send({ + 'identifier': channelId, + 'command': 'subscribe', + }); } void unsubscribe(String channelName, {Map? channelParams}) { final channelId = encodeChannelId(channelName, channelParams); - _onChannelSubscribedCallbacks[channelId] = null; - _onChannelDisconnectedCallbacks[channelId] = null; - _onChannelMessageCallbacks[channelId] = null; + _onChannelSubscribedCallbacks.remove(channelId); + _onChannelDisconnectedCallbacks.remove(channelId); + _onChannelMessageCallbacks.remove(channelId); - _socketChannel.sink - .add(jsonEncode({'identifier': channelId, 'command': 'unsubscribe'})); + _send({ + 'identifier': channelId, + 'command': 'unsubscribe', + }); } - void performAction(String channelName, - {String? action, Map? channelParams, Map? actionParams}) { + void performAction( + String channelName, { + String? action, + Map? channelParams, + Map? actionParams, + }) { final channelId = encodeChannelId(channelName, channelParams); actionParams ??= {}; @@ -97,45 +127,39 @@ class ActionCable { _send({ 'identifier': channelId, 'command': 'message', - 'data': jsonEncode(actionParams) + 'data': jsonEncode(actionParams), }); } void _onData(dynamic payload) { - payload = jsonDecode(payload); - - if (payload['type'] != null) { - _handleProtocolMessage(payload); - } else { - _handleDataMessage(payload); + try { + final data = jsonDecode(payload); + if (data['type'] != null) { + _handleProtocolMessage(data); + } else { + _handleDataMessage(data); + } + } catch (error) { + throw 'InvalidPayload'; } } - void _handleProtocolMessage(Map payload) { + void _handleProtocolMessage(Map payload) { switch (payload['type']) { case 'ping': - // rails sends epoch as seconds not miliseconds _lastPing = DateTime.fromMillisecondsSinceEpoch(payload['message'] * 1000); break; case 'welcome': - if (onConnected != null) { - onConnected!(); - } + onConnected?.call(); break; case 'disconnect': final channelId = parseChannelId(payload['identifier']); - final onDisconnected = _onChannelDisconnectedCallbacks[channelId]; - if (onDisconnected != null) { - onDisconnected(); - } + _onChannelDisconnectedCallbacks[channelId]?.call(); break; case 'confirm_subscription': final channelId = parseChannelId(payload['identifier']); - final onSubscribed = _onChannelSubscribedCallbacks[channelId]; - if (onSubscribed != null) { - onSubscribed(); - } + _onChannelSubscribedCallbacks[channelId]?.call(); break; case 'reject_subscription': // throw 'Unimplemented'; @@ -145,15 +169,14 @@ class ActionCable { } } - void _handleDataMessage(Map payload) { + void _handleDataMessage(Map payload) { final channelId = parseChannelId(payload['identifier']); - final onMessage = _onChannelMessageCallbacks[channelId]; - if (onMessage != null) { - onMessage(payload['message']); - } + _onChannelMessageCallbacks[channelId]?.call(payload['message']); } - void _send(Map payload) { - _socketChannel.sink.add(jsonEncode(payload)); + void _send(Map payload) { + if (_socketChannel != null) { + _socketChannel!.sink.add(jsonEncode(payload)); + } } } From f3eefa8e065b4ec0615c91a67799971f1dc835d8 Mon Sep 17 00:00:00 2001 From: Hamad alghanim Date: Mon, 21 Oct 2024 12:39:05 -0600 Subject: [PATCH 3/4] Refactor ActionCable.disconnect function to handle different scenarios --- lib/action_cable.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/action_cable.dart b/lib/action_cable.dart index db52dc7..075746d 100644 --- a/lib/action_cable.dart +++ b/lib/action_cable.dart @@ -154,8 +154,17 @@ class ActionCable { onConnected?.call(); break; case 'disconnect': - final channelId = parseChannelId(payload['identifier']); - _onChannelDisconnectedCallbacks[channelId]?.call(); + final identifier = payload['identifier']; + if (identifier != null) { + final channelId = parseChannelId(payload['identifier']); + final onDisconnected = _onChannelDisconnectedCallbacks[channelId]; + onDisconnected?.call(); + } else { + final reason = payload['reason']; + if (reason != null && reason == 'unauthorized') { + this.onCannotConnect?.call(); + } + } break; case 'confirm_subscription': final channelId = parseChannelId(payload['identifier']); From e679c1a7a0d2ae5abe9b3626ce35407e3491fb0b Mon Sep 17 00:00:00 2001 From: Hamad alghanim Date: Mon, 21 Oct 2024 12:40:49 -0600 Subject: [PATCH 4/4] Invoke channel disconnected callbacks when disconnecting from the cable --- lib/action_cable.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/action_cable.dart b/lib/action_cable.dart index 075746d..af0440a 100644 --- a/lib/action_cable.dart +++ b/lib/action_cable.dart @@ -61,6 +61,11 @@ class ActionCable { _listener?.cancel(); _socketChannel?.sink.close(); _socketChannel = null; + _onChannelDisconnectedCallbacks.values + .where((onDisconnected) => onDisconnected != null) + .forEach((onDisconnected) { + onDisconnected!(); + }); } void _handleError() {