diff --git a/polo_client/example/polo_client_example.dart b/polo_client/example/polo_client_example.dart index 35a04b5..658be85 100644 --- a/polo_client/example/polo_client_example.dart +++ b/polo_client/example/polo_client_example.dart @@ -1,20 +1,73 @@ +import 'dart:io'; + import 'package:polo_client/polo_client.dart'; +import 'package:polo_client/src/polo_type.dart'; + +class UserType implements PoloType { + String? name; + int? age; + + UserType({this.name, this.age}); + + @override + UserType fromMap(Map map) { + return UserType(name: map['name'], age: map['age']); + } + + @override + Map toMap() { + return {'name': name, 'age': age}; + } +} void main() async { + stdout.write("Enter Room to join: "); + // String room = stdin.readLineSync() ?? "root"; + // Polo Client - PoloClient client = await Polo.connect("ws://127.0.0.1:3000/chat"); + PoloClient client = await Polo.connect("ws://127.0.0.1:3000/"); + + // PoloClient client = + // await Polo.connect("ws://polo-chat-server.herokuapp.com/"); + + // Future getInput() async { + // stdin.listen((msg) { + // client.send('message', String.fromCharCodes(msg).trim()); + // // client.send('messageToRoom', + // // {"room": room, "message": String.fromCharCodes(msg).trim()}); + // }); + // } client.onConnect(() { print("Client Connected to Server"); + client.send('dynamic', "Ayush"); + client.send('dynamic', 1); + client.send('dynamic', 3.14); + client.send('dynamic', true); + client.send('dynamic', [1, 2, 3]); + client.send('dynamic', { + "String": {"dynamic": true} + }); + client.send('message', "Hello from Client"); + client.send('userJoined', UserType(name: "Ayush", age: 22)); }); client.onDisconnect(() { print("Client Disconnected from Server"); }); - client.onEvent('message', (message) { - print("$message"); + client.onEvent('dynamic', (dyn) { + stdout.writeln("Dynamic: $dyn : ${dyn.runtimeType}"); }); + client.onEvent('message', (message) { + stdout.writeln("Message: $message : ${message.runtimeType}"); + }); + + client.onEvent('userJoined', (user) { + stdout.writeln("userJoined : ${user.toMap()} : ${user.runtimeType}"); + }, converter: UserType()); + client.listen(); + // getInput(); } diff --git a/polo_client/lib/src/polo_client_stub.dart b/polo_client/lib/src/polo_client_stub.dart index 76c7287..42ea631 100644 --- a/polo_client/lib/src/polo_client_stub.dart +++ b/polo_client/lib/src/polo_client_stub.dart @@ -1,30 +1,29 @@ -abstract class PoloClient { - final Map _callbacks = {}; - - void Function() _onDisconnectCallback = () {}; - void Function() _onConnectCallback = () {}; +import 'polo_type.dart'; +abstract class PoloClient { /// Sets onConnectCallback - void onConnect(void Function() callback) => _onConnectCallback = callback; + void onConnect(void Function() callback) { + throw UnsupportedError("Platform is not Supported"); + } /// Sets onDisconnectCallback - void onDisconnect(void Function() callback) => - _onDisconnectCallback = callback; + void onDisconnect(void Function() callback) { + throw UnsupportedError("Platform is not Supported"); + } /// Adds a Callback to an Event - void onEvent(String event, void Function(dynamic data) callback) => - _callbacks[event] = callback; - - void _emit(String event, dynamic data) => - _callbacks.containsKey(event) ? _callbacks[event]!(data) : () {}; + void onEvent(String event, void Function(T data) callback, + {PoloType? converter}) { + throw UnsupportedError("Platform is not Supported"); + } /// Starts listening for messages from `PoloServer` Future listen() { - return _handleEvents(); + throw UnsupportedError("Platform is not Supported"); } /// Sends message to the Server from Client - void send(String event, dynamic data) { + void send(String event, T data) { throw UnsupportedError("Platform is not Supported"); } @@ -32,10 +31,6 @@ abstract class PoloClient { Future close() async { throw UnsupportedError("Platform is not Supported"); } - - Future _handleEvents() async { - throw UnsupportedError("Platform is not Supported"); - } } abstract class Polo { diff --git a/polo_client/lib/src/polo_io_client_base.dart b/polo_client/lib/src/polo_io_client_base.dart index 4ec4435..6d49db4 100644 --- a/polo_client/lib/src/polo_io_client_base.dart +++ b/polo_client/lib/src/polo_io_client_base.dart @@ -3,7 +3,7 @@ part of 'polo_io_client_helper.dart'; /// Use `Polo.connect()` to Connect to the `PoloServer` class PoloClient implements stub.PoloClient { final io.WebSocket _webSocket; - final Map _callbacks = {}; + final Map _callbacks = {}; void Function() _onDisconnectCallback = () {}; void Function() _onConnectCallback = () {}; @@ -21,11 +21,22 @@ class PoloClient implements stub.PoloClient { /// Adds a Callback to an Event @override - void onEvent(String event, void Function(dynamic data) callback) => + void onEvent(String event, void Function(T data) callback, + {PoloType? converter}) { + if (converter != null) { + assert(converter is T); + _callbacks[event] = (data) { + T typedData = converter.fromMap(data) as T; + callback(typedData); + }; + } else { _callbacks[event] = callback; + } + } - void _emit(String event, dynamic data) => - _callbacks.containsKey(event) ? _callbacks[event]!(data) : () {}; + void _emit(String event, dynamic data) { + if (_callbacks.containsKey(event)) _callbacks[event]!(data); + } /// Starts listening for messages from `PoloServer` @override @@ -35,8 +46,12 @@ class PoloClient implements stub.PoloClient { /// Sends message to the Server from Client @override - void send(String event, dynamic data) { - _webSocket.add(jsonEncode({'event': event, 'data': data})); + void send(String event, dynamic data) { + if (data is PoloType) { + _webSocket.add(jsonEncode({'event': event, 'data': data.toMap()})); + } else { + _webSocket.add(jsonEncode({'event': event, 'data': data})); + } } /// Closes the connection to the `PoloServer` @@ -59,7 +74,7 @@ class PoloClient implements stub.PoloClient { } catch (e) { print("Error: $e"); } finally { - _webSocket.close(); + close(); } } } diff --git a/polo_client/lib/src/polo_io_client_helper.dart b/polo_client/lib/src/polo_io_client_helper.dart index 5dbef27..ddd86d2 100644 --- a/polo_client/lib/src/polo_io_client_helper.dart +++ b/polo_client/lib/src/polo_io_client_helper.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io' as io; import './polo_client_stub.dart' as stub; +import 'polo_type.dart'; part 'polo_io_client_base.dart'; diff --git a/polo_client/lib/src/polo_type.dart b/polo_client/lib/src/polo_type.dart new file mode 100644 index 0000000..2f19b0c --- /dev/null +++ b/polo_client/lib/src/polo_type.dart @@ -0,0 +1,9 @@ +abstract class PoloType { + Map toMap() { + throw UnimplementedError(); + } + + PoloType fromMap(Map map) { + throw UnimplementedError(); + } +} diff --git a/polo_client/lib/src/polo_web_client_base.dart b/polo_client/lib/src/polo_web_client_base.dart index 7870691..a315aa6 100644 --- a/polo_client/lib/src/polo_web_client_base.dart +++ b/polo_client/lib/src/polo_web_client_base.dart @@ -3,7 +3,7 @@ part of 'polo_web_client_helper.dart'; /// Use `Polo.connect()` to Connect to the `PoloServer` class PoloClient implements stub.PoloClient { final html.WebSocket _webSocket; - final Map _callbacks = {}; + final Map _callbacks = {}; void Function() _onDisconnectCallback = () {}; void Function() _onConnectCallback = () {}; @@ -21,11 +21,22 @@ class PoloClient implements stub.PoloClient { /// Adds a Callback to an Event @override - void onEvent(String event, void Function(dynamic data) callback) => + void onEvent(String event, void Function(T data) callback, + {PoloType? converter}) { + if (converter != null) { + assert(converter is T); + _callbacks[event] = (data) { + T typedData = converter.fromMap(data) as T; + callback(typedData); + }; + } else { _callbacks[event] = callback; + } + } - void _emit(String event, dynamic data) => - _callbacks.containsKey(event) ? _callbacks[event]!(data) : () {}; + void _emit(String event, dynamic data) { + if (_callbacks.containsKey(event)) _callbacks[event]!(data); + } /// Starts listening for messages from `PoloServer` @override @@ -35,8 +46,12 @@ class PoloClient implements stub.PoloClient { /// Sends message to the Server from Client @override - void send(String event, dynamic data) { - _webSocket.sendString(jsonEncode({'event': event, 'data': data})); + void send(String event, T data) { + if (data is PoloType) { + _webSocket.sendString(jsonEncode({'event': event, 'data': data.toMap()})); + } else { + _webSocket.sendString(jsonEncode({'event': event, 'data': data})); + } } /// Closes the connection to the `PoloServer` @@ -59,7 +74,7 @@ class PoloClient implements stub.PoloClient { } catch (e) { print("Error: $e"); } finally { - _webSocket.close(); + close(); } } } diff --git a/polo_client/lib/src/polo_web_client_helper.dart b/polo_client/lib/src/polo_web_client_helper.dart index c024e9c..fbe4006 100644 --- a/polo_client/lib/src/polo_web_client_helper.dart +++ b/polo_client/lib/src/polo_web_client_helper.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:html' as html; import './polo_client_stub.dart' as stub; +import 'polo_type.dart'; part 'polo_web_client_base.dart'; diff --git a/polo_server/example/polo_server_example.dart b/polo_server/example/polo_server_example.dart index b33e207..b69a481 100644 --- a/polo_server/example/polo_server_example.dart +++ b/polo_server/example/polo_server_example.dart @@ -1,4 +1,22 @@ import 'package:polo_server/polo_server.dart'; +import 'package:polo_server/src/polo_type.dart'; + +class UserType implements PoloType { + String? name; + int? age; + + UserType({this.name, this.age}); + + @override + UserType fromMap(Map map) { + return UserType(name: map['name'], age: map['age']); + } + + @override + Map toMap() { + return {'name': name, 'age': age}; + } +} void main() async { // Manager @@ -11,8 +29,40 @@ void main() async { server.onClientConnect((client) { print("Client(${client.id}) Connected!"); - client.onEvent('message', - (message) => server.broadcastFrom(client.id, 'message', message)); + client.onEvent('dynamic', (dyn) { + print("Dynamic: $dyn : ${dyn.runtimeType}"); + client.send('dynamic', dyn); + }); + + client.onEvent('message', (message) { + print("$message : ${message.runtimeType}"); + client.send('message', "Hello from Server"); + }); + + client.onEvent( + 'userJoined', + (user) { + print("userJoined : ${user.toMap()} : ${user.runtimeType}"); + client.send('userJoined', user); + }, + converter: UserType(), + ); + + // client.onEvent('message', + // (message) => server.broadcastFrom(client, 'message', message)); + + // client.onEvent( + // 'messageToRoom', + // (payload) => server.broadcastToRoom( + // client, payload['room'], 'message', payload['message'])); + + // client.onEvent('joinRoom', (room) { + // client.joinRoom(room); + // }); + + // client.onEvent('leaveRoom', (room) { + // client.leaveRoom(room); + // }); }); server.onClientDisconnect((client) { diff --git a/polo_server/lib/src/polo_client_base.dart b/polo_server/lib/src/polo_client_base.dart index fe9c9bb..0983f17 100644 --- a/polo_server/lib/src/polo_client_base.dart +++ b/polo_server/lib/src/polo_client_base.dart @@ -5,7 +5,7 @@ class PoloClient { final WebSocket _webSocket; late final String _id; String get id => _id; - Map callbacks = {}; + final Map _callbacks = {}; final Set _rooms = {}; void Function(PoloClient) _onDisconnectCallback = (poloClient) {}; @@ -19,15 +19,30 @@ class PoloClient { _onDisconnectCallback = callback; /// Adds a Callback to an Event - void onEvent(String event, void Function(dynamic data) callback) => - callbacks[event] = callback; + void onEvent(String event, void Function(T data) callback, + {PoloType? converter}) { + if (converter != null) { + assert(converter is T); + _callbacks[event] = (data) { + T typedData = converter.fromMap(data) as T; + callback(typedData); + }; + } else { + _callbacks[event] = callback; + } + } - void _emit(String event, dynamic data) => - callbacks.containsKey(event) ? callbacks[event]!(data) : () {}; + void _emit(String event, dynamic data) { + if (_callbacks.containsKey(event)) _callbacks[event]!(data); + } /// Sends message to the Client from Server - void send(String event, dynamic data) { - _webSocket.add(jsonEncode({'event': event, 'data': data})); + void send(String event, T data) { + if (data is PoloType) { + _webSocket.add(jsonEncode({'event': event, 'data': data.toMap()})); + } else { + _webSocket.add(jsonEncode({'event': event, 'data': data})); + } } /// Joins a Room @@ -41,16 +56,14 @@ class PoloClient { } /// Closes the Client - Future close() async { + Future close() async { return _webSocket.close(); } - void _handleEvents() async { + Future _handleEvents() async { _webSocket.done.then((_) { - // emit('disconnect', this); _onDisconnectCallback(this); }); - onEvent("message", (message) => print(message.toString())); try { //Listen for Messages from Client await for (dynamic message in _webSocket) { @@ -60,7 +73,7 @@ class PoloClient { } catch (e) { print("Error: $e"); } finally { - _webSocket.close(); + close(); } } } diff --git a/polo_server/lib/src/polo_server_base.dart b/polo_server/lib/src/polo_server_base.dart index 2f621b0..933b2ab 100644 --- a/polo_server/lib/src/polo_server_base.dart +++ b/polo_server/lib/src/polo_server_base.dart @@ -70,33 +70,46 @@ class PoloServer { } /// Sends message to all Clients - void send(String event, dynamic message) { + void send(String event, T data) { for (PoloClient client in _clients.values) { - client.send(event, message); + client.send(event, data); } } /// Sends message to a Client by Id - void sendToClient(String clientId, String event, dynamic message) { - if (_clients.containsKey(clientId)) { - _clients[clientId]!.send(event, message); + void sendToClient(PoloClient client, String event, T data) { + if (_clients.containsKey(client)) { + _clients[client]!.send(event, data); } } /// Sends message to a Room - void sendToRoom(String room, String event, dynamic message) { + void sendToRoom(String room, String event, T data) { for (PoloClient client in _clients.values) { if (client._rooms.contains(room)) { - client.send(event, message); + client.send(event, data); } } } /// Broadcast from a Client to all other Clients - void broadcastFrom(String clientId, String event, dynamic message) { - for (PoloClient client in _clients.values) { - if (client.id != clientId) { - client.send(event, message); + void broadcastFrom(PoloClient client, String event, T data) { + for (PoloClient clientL in _clients.values) { + if (clientL.id != client.id) { + clientL.send(event, data); + } + } + } + + /// Broadcast from a Client to Room + void broadcastToRoom( + PoloClient client, String room, String event, T data) { + // Sender Client must join the Room to Broadcast + if (!client._rooms.contains(room)) return; + + for (PoloClient clientL in _clients.values) { + if (clientL.id != client.id && clientL._rooms.contains(room)) { + clientL.send(event, data); } } } diff --git a/polo_server/lib/src/polo_server_helper.dart b/polo_server/lib/src/polo_server_helper.dart index 155e7d4..6d14ded 100644 --- a/polo_server/lib/src/polo_server_helper.dart +++ b/polo_server/lib/src/polo_server_helper.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:polo_server/src/polo_type.dart'; import 'package:uuid/uuid.dart'; part 'polo_client_base.dart'; diff --git a/polo_server/lib/src/polo_type.dart b/polo_server/lib/src/polo_type.dart new file mode 100644 index 0000000..2f19b0c --- /dev/null +++ b/polo_server/lib/src/polo_type.dart @@ -0,0 +1,9 @@ +abstract class PoloType { + Map toMap() { + throw UnimplementedError(); + } + + PoloType fromMap(Map map) { + throw UnimplementedError(); + } +}