diff --git a/client/package-lock.json b/client/package-lock.json index e1c1016..6d34995 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -15,6 +15,8 @@ "devDependencies": { "@tauri-apps/cli": "^1.3.0", "@types/react-dom": "^18.2.4", + "sass": "^1.62.1", + "scss": "^0.2.4", "vite": "^4.3.4" } }, @@ -701,6 +703,40 @@ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/case-anything": { "version": "2.1.10", "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz", @@ -712,6 +748,33 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", @@ -778,6 +841,18 @@ "@esbuild/win32-x64": "0.17.18" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -792,6 +867,18 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -814,6 +901,54 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", + "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -853,6 +988,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-hash": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", @@ -861,12 +1005,33 @@ "node": ">= 0.10.0" } }, + "node_modules/ometa": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ometa/-/ometa-0.2.2.tgz", + "integrity": "sha512-LZuoK/yjU3FvrxPjUXUlZ1bavCfBPqauA7fsNdwi+AVhRdyk2IzgP3JRnevvjzQ6fKHdUw8YISshf53FmpHrng==", + "dev": true, + "engines": { + "node": ">= 0.2.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.4.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", @@ -986,6 +1151,18 @@ } } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", @@ -1028,6 +1205,23 @@ "fsevents": "~2.3.2" } }, + "node_modules/sass": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz", + "integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -1036,6 +1230,18 @@ "loose-envify": "^1.1.0" } }, + "node_modules/scss": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/scss/-/scss-0.2.4.tgz", + "integrity": "sha512-4u8V87F+Q/upVhUmhPnB4C1R11xojkRkWjExL2v0CX2EXTg18VrKd+9JWoeyCp2VEMdSpJsyAvVU+rVjogh51A==", + "dev": true, + "dependencies": { + "ometa": "0.2.2" + }, + "engines": { + "node": ">= 0.2.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -1045,6 +1251,18 @@ "node": ">=0.10.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/ts-poet": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.4.1.tgz", diff --git a/client/package.json b/client/package.json index ed712f8..08ca254 100644 --- a/client/package.json +++ b/client/package.json @@ -5,6 +5,8 @@ "devDependencies": { "@tauri-apps/cli": "^1.3.0", "@types/react-dom": "^18.2.4", + "sass": "^1.62.1", + "scss": "^0.2.4", "vite": "^4.3.4" }, "dependencies": { diff --git a/client/src-tauri/src/main.rs b/client/src-tauri/src/main.rs index 012a16a..e7ccce5 100644 --- a/client/src-tauri/src/main.rs +++ b/client/src-tauri/src/main.rs @@ -12,13 +12,15 @@ type MyGreeterClient = GreeterClient; #[tauri::command] async fn send_message( state: tauri::State<'_, Mutex>, + message: String, ) -> Result<(), ()> { println!("SHIET {state:?}"); let mut client = state.lock().await; client .say_hello(HelloRequest { - name: "Hellosu".into(), + message, + ..Default::default() }) .await; diff --git a/client/src/App.module.scss b/client/src/App.module.scss new file mode 100644 index 0000000..3d983fb --- /dev/null +++ b/client/src/App.module.scss @@ -0,0 +1,9 @@ +body, html { + margin: 0; +} + +.app { + display: flex; + width: 100%; + height: 100%; +} diff --git a/client/src/App.tsx b/client/src/App.tsx index 882accb..982ae61 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,28 +3,34 @@ import { Provider } from "react-redux"; import { store } from "./store"; import { useState } from "react"; +import styles from "./App.module.scss"; +import LeftSidebar from "./components/LeftSidebar"; + export default function App() { const [currentMessage, setCurrentMessage] = useState(""); const onSubmit = (e) => { e.preventDefault(); - invoke("send_message", { content: currentMessage }); + invoke("send_message", { message: currentMessage }); setCurrentMessage(""); }; return ( -

mraow chat

+
+ +

mraow chat

-
- setCurrentMessage(e.currentTarget.value)} - autoFocus - /> -
+
+ setCurrentMessage(e.currentTarget.value)} + autoFocus + /> +
+
); } diff --git a/client/src/components/CenterPanel.tsx b/client/src/components/CenterPanel.tsx new file mode 100644 index 0000000..d976e9e --- /dev/null +++ b/client/src/components/CenterPanel.tsx @@ -0,0 +1 @@ +export default function CenterPanel() {} diff --git a/client/src/components/LeftSidebar.module.scss b/client/src/components/LeftSidebar.module.scss new file mode 100644 index 0000000..56c5ed8 --- /dev/null +++ b/client/src/components/LeftSidebar.module.scss @@ -0,0 +1,3 @@ +.leftSidebar { + background-color: var(--left-sidebar-background-color); +} diff --git a/client/src/components/LeftSidebar.tsx b/client/src/components/LeftSidebar.tsx new file mode 100644 index 0000000..aece174 --- /dev/null +++ b/client/src/components/LeftSidebar.tsx @@ -0,0 +1,5 @@ +import styles from "./LeftSidebar.module.scss"; + +export default function LeftSidebar() { + return
Rooms
; +} diff --git a/client/src/main.tsx b/client/src/main.tsx index 8038389..c2895a2 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -1,5 +1,6 @@ import { createRoot } from "react-dom/client"; import App from "./App"; +import "./variables.scss"; // Render your React component instead const el = document.getElementById("app"); diff --git a/client/src/variables.scss b/client/src/variables.scss new file mode 100644 index 0000000..961b1d8 --- /dev/null +++ b/client/src/variables.scss @@ -0,0 +1,7 @@ +:root { + --main-background-color: #eee; + --left-sidebar-background-color: #ddd; + + @mixin dark-mode() { + } +} diff --git a/proto/chat.proto b/proto/chat.proto index 9dc2ed1..6850840 100644 --- a/proto/chat.proto +++ b/proto/chat.proto @@ -12,7 +12,9 @@ message ChatMessage { message Channel {} -message UserList {} +message UserList { repeated User user = 1; } + +message RoomList {} message User { string username = 1; } @@ -22,6 +24,8 @@ service Chat { rpc join(User) returns (JoinResponse) {} rpc sendMsg(ChatMessage) returns (google.protobuf.Empty) {} rpc receiveMsg(google.protobuf.Empty) returns (stream ChatMessage) {} + + rpc listRooms(google.protobuf.Empty) returns (RoomList) {} rpc getAllUsers(google.protobuf.Empty) returns (UserList) {} } @@ -34,7 +38,10 @@ service Greeter { } // The request message containing the user's name. -message HelloRequest { string name = 1; } +message HelloRequest { + string name = 1; + string message = 2; +} // The response message containing the greetings message HelloReply { string message = 1; } diff --git a/server/src/main.rs b/server/src/main.rs index 1f1d30d..28b889f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,5 +1,9 @@ +mod mux; + +use futures::Stream; use hyper::Body; use std::{ + pin::Pin, task::{Context, Poll}, time::Duration, }; @@ -7,36 +11,91 @@ use tonic::{body::BoxBody, transport::Server, Request, Response, Status}; use tower::{Layer, Service}; use mraow_common::chat_proto::{ - greeter_server::{Greeter, GreeterServer}, - HelloReply, HelloRequest, + chat_server::{Chat, ChatServer}, + ChatMessage, HelloReply, HelloRequest, JoinResponse, RoomList, User, + UserList, }; +type ResponseStream = + Pin> + Send>>; + #[derive(Default)] -pub struct MyGreeter {} +pub struct ChatImpl {} #[tonic::async_trait] -impl Greeter for MyGreeter { +impl Chat for ChatImpl { + type receiveMsgStream = ResponseStream; + + async fn join( + &self, + request: Request, + ) -> Result, Status> { + println!("Join {request:?}"); + todo!() + } + + async fn send_msg( + &self, + request: Request, + ) -> Result, Status> { + println!("Send message {request:?}"); + todo!() + } + + async fn receive_msg( + &self, + request: Request<()>, + ) -> Result, Status> { + println!("Receive message {request:?}"); + todo!() + } + + async fn list_rooms( + &self, + request: Request<()>, + ) -> Result, Status> { + println!("Get all rooms {request:?}"); + todo!() + } + + async fn get_all_users( + &self, + request: Request<()>, + ) -> Result, Status> { + println!("Get all users {request:?}"); + todo!() + } + + /* async fn say_hello( &self, request: Request, ) -> Result, Status> { - println!("Got a request from {:?}", request.remote_addr()); + let addr = request.remote_addr(); + let request = request.into_inner(); + + println!( + "Got a request from {:?}: {}", + addr, + request.message + ); let reply = HelloReply { - message: format!("Hello {}!", request.into_inner().name), + message: format!("Hello {}!", request.name), }; Ok(Response::new(reply)) } + */ } #[tokio::main] async fn main() -> Result<(), Box> { let addr = "[::1]:50051".parse().unwrap(); - let greeter = MyGreeter::default(); + let greeter = ChatImpl::default(); println!("GreeterServer listening on {}", addr); - let svc = GreeterServer::new(greeter); + let svc = ChatServer::new(greeter); // The stack of middleware that our service will be wrapped in let layer = tower::ServiceBuilder::new() diff --git a/server/src/mux.rs b/server/src/mux.rs new file mode 100644 index 0000000..70a6c58 --- /dev/null +++ b/server/src/mux.rs @@ -0,0 +1,98 @@ +use std::{ + collections::HashMap, + pin::Pin, + task::{Context, Poll}, +}; + +use anyhow::Result; +use futures::{future, Future, FutureExt, Sink, Stream}; +use tokio::{ + sync::{ + broadcast::{Receiver, Sender}, + mpsc::{self, UnboundedReceiver, UnboundedSender}, + }, + task::JoinHandle, +}; + +pub struct Mux {} + +#[derive(Clone)] +pub enum RoomMessage {} + +pub struct Room { + name: String, + tx: Sender, + rx: Receiver, +} + +impl Room { + pub fn get_handle(&self) -> RoomHandle { + RoomHandle { + name: self.name.clone(), + tx: self.tx.clone(), + rx: self.tx.subscribe(), + } + } +} + +pub struct RoomHandle { + name: String, + tx: Sender, + rx: Receiver, +} + +pub enum CompositeListenerMessage { + Room { name: String, message: RoomMessage }, + JoinRoom(String), + DropRoom(String), +} + +pub struct CompositeListener { + current_listening_rooms: HashMap, +} + +impl CompositeListener {} + +fn create_listener() -> ( + JoinHandle<()>, + UnboundedSender, + UnboundedReceiver, +) { + let (tx, rx) = mpsc::unbounded_channel(); + + /* + let join_handle = tokio::spawn(async { + let mut currently_listening_to: HashMap = + HashMap::new(); + + loop { + // Build select targets + let ouais = rx.recv().boxed(); + let mut select_targets: Vec< + Pin>>>, + > = vec![ouais]; + for (_, room_handle) in currently_listening_to.iter() { + select_targets.push( + room_handle + .rx + .recv() + .map(|f| { + f.ok().map(|m| CompositeListenerMessage::Room { + name: room_handle.name.clone(), + message: m, + }) + }) + .boxed(), + ); + } + + // Select + let result = future::select_all(select_targets).await; + } + }); + + (join_handle, tx, rx) + */ + + todo!() +}