Skip to content

Commit 63f8b50

Browse files
committed
feat: Add WebSocket support for real-time communication in NodeService
1 parent a0a2d2a commit 63f8b50

3 files changed

Lines changed: 93 additions & 1 deletion

File tree

lib/services/node_service.dart

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import 'dart:async';
12
import 'dart:convert';
23

34
import 'package:flutter/foundation.dart';
45
import 'package:hushnet_frontend/services/key_provider.dart';
56
import 'package:hushnet_frontend/services/secure_storage_service.dart';
67
import 'package:dio/dio.dart';
78
import 'package:logging/logging.dart';
9+
import 'package:web_socket_channel/web_socket_channel.dart';
10+
import 'package:web_socket_channel/status.dart' as status;
811

912
class NodeService {
1013
static final NodeService _instance = NodeService._internal();
@@ -16,6 +19,12 @@ class NodeService {
1619
final log = Logger('NodeService');
1720
final KeyProvider _keyProvider = KeyProvider();
1821

22+
WebSocketChannel? _channel;
23+
StreamSubscription? _subscription;
24+
String? _connectedUserId;
25+
final _controller = StreamController<Map<String, dynamic>>.broadcast();
26+
27+
Stream<Map<String, dynamic>> get stream => _controller.stream;
1928

2029

2130

@@ -72,6 +81,7 @@ class NodeService {
7281
await _storage.write('username', username);
7382
await _storage.write('user_id', userId);
7483
await _storage.write('node_url', nodeUrl);
84+
await connectWebSocket(nodeUrl, userId);
7585
return username;
7686
} else {
7787
log.severe('Login failed: ${response.statusCode} ${response.data}');
@@ -117,7 +127,8 @@ class NodeService {
117127
final deviceKey = data['device_key'];
118128
await _storage.write('device_id', deviceId);
119129
await _storage.write('device_key', deviceKey);
120-
stepNotifier?.value = 8;
130+
await connectWebSocket(nodeUrl, userId);
131+
stepNotifier?.value = 8;
121132

122133
return true;
123134
} else {
@@ -140,5 +151,69 @@ class NodeService {
140151
Future<String?> getCurrentDeviceId() async {
141152
return await _storage.read('device_id');
142153
}
154+
Future<void> connectWebSocket(String nodeUrl, String userId) async {
155+
if (_connectedUserId == userId && _channel != null) {
156+
debugPrint("🔁 WebSocket already connected for $userId");
157+
return;
158+
}
159+
160+
var clean = nodeUrl.trim().replaceAll('#', '');
161+
final parsed = Uri.parse(clean);
162+
163+
final scheme = (parsed.scheme == 'https' || parsed.scheme == 'wss')
164+
? 'wss'
165+
: 'ws';
166+
167+
final host = parsed.host;
168+
final port = parsed.hasPort ? ':${parsed.port}' : '';
169+
170+
final wsUrl = Uri.parse("$scheme://$host$port/ws/$userId");
171+
debugPrint("Connecting WS to $wsUrl with $userId");
172+
173+
try {
174+
_channel = WebSocketChannel.connect(wsUrl);
175+
debugPrint("WS connected for $userId");
176+
_connectedUserId = userId;
177+
178+
_subscription = _channel!.stream.listen(
179+
(event) {
180+
try {
181+
final decoded = jsonDecode(event);
182+
_controller.add(decoded);
183+
debugPrint("WS event: $decoded");
184+
} catch (e) {
185+
debugPrint("Invalid WS payload: $e");
186+
}
187+
},
188+
onError: (err) {
189+
debugPrint("WS error: $err");
190+
_retry(nodeUrl, userId);
191+
},
192+
onDone: () {
193+
debugPrint("WS closed for $userId");
194+
_retry(nodeUrl, userId);
195+
},
196+
);
197+
} catch (e) {
198+
debugPrint("Failed to connect WebSocket: $e");
199+
_retry(nodeUrl, userId);
200+
}
201+
}
202+
203+
void _retry(String nodeUrl, String userId) {
204+
Future.delayed(const Duration(seconds: 3), () {
205+
if (_connectedUserId == userId) {
206+
connectWebSocket(nodeUrl, userId);
207+
}
208+
});
209+
}
210+
211+
void disconnectWebSocket() {
212+
//TODO : Implement disconnect logic
213+
debugPrint("Closing WS connection...");
214+
_subscription?.cancel();
215+
_channel?.sink.close(status.normalClosure);
216+
_connectedUserId = null;
217+
}
143218

144219
}

pubspec.lock

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,22 @@ packages:
566566
url: "https://pub.dev"
567567
source: hosted
568568
version: "1.1.1"
569+
web_socket:
570+
dependency: transitive
571+
description:
572+
name: web_socket
573+
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
574+
url: "https://pub.dev"
575+
source: hosted
576+
version: "1.0.1"
577+
web_socket_channel:
578+
dependency: "direct main"
579+
description:
580+
name: web_socket_channel
581+
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
582+
url: "https://pub.dev"
583+
source: hosted
584+
version: "3.0.3"
569585
win32:
570586
dependency: transitive
571587
description:

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dependencies:
4444
cryptography: ^2.7.0
4545
intl: ^0.20.2
4646
uuid: ^4.5.1
47+
web_socket_channel: ^3.0.3
4748

4849
dependency_overrides:
4950
flutter_secure_storage_linux:

0 commit comments

Comments
 (0)