Files
hub/frontend/lib/backend.dart
T

138 lines
4.1 KiB
Dart

import 'dart:html';
import 'dart:convert';
import 'dart:async'; // Import for handling Completers
class Backend {
static WebSocket? _socket;
static String? _url;
static Map<int, Completer> _pendingActions = {};
static Map<int, StreamController<Map<String, dynamic>>> _chatControllers = {};
static int _nextNonce = 0; // Tracking nonce for each action
static StreamController<Map<String, dynamic>> _packetStreamController = StreamController.broadcast();
// Authentication
static ({String username, String password})? _credentials;
static Completer? _loginCompleter;
static Completer? _credentialsCompleter;
static void setup(String url) {
_url = url;
connect();
}
static Future<void> auth(String username, String password) {
_socket!.send(json.encode({'username': username, 'password': password}));
_credentials = (username: username, password: password);
if (_credentialsCompleter != null) {
_credentialsCompleter!.complete(_credentials);
_credentialsCompleter = null;
}
_loginCompleter = Completer();
return _loginCompleter!.future;
}
static void connect() {
_socket = WebSocket(_url!);
_socket!.onOpen.listen((event) {
print('Connected to the WebSocket server');
print('Logging in...');
if (_credentials != null) {
_socket!.send(json.encode({'username': _credentials!.username, 'password': _credentials!.password}));
} else {
_credentialsCompleter = Completer();
_credentialsCompleter!.future.then((credentials) {
_socket!.send(json.encode({'username': credentials.username, 'password': credentials.password}));
});
}
_loginCompleter?.complete();
});
_socket!.onError.listen((event) {
print('Error: $event');
_reconnect();
});
_socket!.onClose.listen((event) {
print('Disconnected. Attempting to reconnect...');
_reconnect();
});
_socket!.onMessage.listen((event) {
_handleIncomingMessage(event.data);
});
}
static void disconnect() {
_socket?.close();
_loginCompleter = null;
}
static void _reconnect() {
Future.delayed(Duration(seconds: 5), () {
connect();
});
}
static Future<dynamic> sendAction(String action, Map<String, dynamic> body) {
int nonce = _nextNonce++;
Completer completer = Completer();
_pendingActions[nonce] = completer;
final packet = {
'action': action,
'nonce': nonce,
'body': body,
};
_socket!.send(json.encode(packet));
return completer.future.then((response) {
_pendingActions.remove(nonce); // Clean up the mapping
return response;
});
}
static void _handleIncomingMessage(String data) {
var jsonData = json.decode(data);
print("Incoming Packet: $jsonData");
if (jsonData['nonce'] != null) {
int nonce = jsonData['nonce'];
if (_pendingActions.containsKey(nonce)) {
// Complete the response
_pendingActions[nonce]!.complete(jsonData);
} else {
print("Orphan response for action nonce $nonce");
}
} else if (jsonData['action'] == 'chat-message') {
int chatId = jsonData['chatId'];
final controller = _chatControllers[chatId];
if (controller != null && !controller.isClosed) {
controller.add(jsonData);
} else {
print("No active listeners for chatId $chatId");
}
} else {
_packetStreamController.add(jsonData);
}
}
static Stream<Map<String, dynamic>> chatMessages(int chatId) {
if (!_chatControllers.containsKey(chatId) || _chatControllers[chatId]!.isClosed) {
_chatControllers[chatId] = StreamController<Map<String, dynamic>>.broadcast();
}
return _chatControllers[chatId]!.stream;
}
static void removeChatListener(int chatId) {
final controller = _chatControllers.remove(chatId);
if (controller != null && !controller.isClosed) {
controller.close();
}
}
static void ping() {
if (_socket!= null && _socket!.readyState == WebSocket.OPEN) {
final data = json.encode({'path': 'ping'});
_socket!.send(data);
print('Ping sent: $data');
}
}
}