More UI styling
This commit is contained in:
parent
87ccab7a4b
commit
25179a74f7
23 changed files with 270 additions and 77 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
./docker-data
|
||||
./target
|
0
.env
Normal file
0
.env
Normal file
1
.envrc
1
.envrc
|
@ -1 +1,2 @@
|
|||
dotenv .env
|
||||
use flake
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
sytest
|
||||
node_modules
|
||||
/target
|
||||
docker-data
|
||||
|
|
83
client/package-lock.json
generated
83
client/package-lock.json
generated
|
@ -10,14 +10,17 @@
|
|||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^8.0.5",
|
||||
"ts-proto": "^1.147.1"
|
||||
"ts-proto": "^1.147.1",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.3.0",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"sass": "^1.62.1",
|
||||
"scss": "^0.2.4",
|
||||
"vite": "^4.3.4"
|
||||
"vite": "^4.3.4",
|
||||
"vite-plugin-sass-dts": "^1.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
|
@ -703,6 +706,12 @@
|
|||
"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/@types/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
|
@ -737,6 +746,15 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/case-anything": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz",
|
||||
|
@ -1060,6 +1078,41 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-js": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"camelcase-css": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12 || ^14 || >= 16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4.21"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "6.11.3",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
|
||||
|
@ -1305,6 +1358,14 @@
|
|||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.4.tgz",
|
||||
|
@ -1352,6 +1413,24 @@
|
|||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-sass-dts": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-sass-dts/-/vite-plugin-sass-dts-1.3.5.tgz",
|
||||
"integrity": "sha512-yGm5uRTDq1qc17ZMJV0bQRgxNfw2a/Ejqc/T2NOILXmcR1z9zHpqqo1FwoDddyTV87GDQZYldqKZOE4O5bGBOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"postcss-js": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8",
|
||||
"prettier": "^2.7",
|
||||
"sass": "*",
|
||||
"vite": "^3 || ^4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.3.0",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"sass": "^1.62.1",
|
||||
"scss": "^0.2.4",
|
||||
"vite": "^4.3.4"
|
||||
"vite": "^4.3.4",
|
||||
"vite-plugin-sass-dts": "^1.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
|
@ -15,6 +17,7 @@
|
|||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^8.0.5",
|
||||
"ts-proto": "^1.147.1"
|
||||
"ts-proto": "^1.147.1",
|
||||
"uuid": "^9.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use anyhow::Result;
|
||||
use mraow_common::chat_proto::chat_client::ChatClient;
|
||||
use tauri::async_runtime::{Mutex, TokioHandle};
|
||||
use tonic::transport::channel::Channel;
|
||||
|
||||
use mraow_common::chat_proto::{greeter_client::GreeterClient, HelloRequest};
|
||||
|
||||
type MyGreeterClient = GreeterClient<Channel>;
|
||||
type MyGreeterClient = ChatClient<Channel>;
|
||||
|
||||
#[tauri::command]
|
||||
async fn send_message(
|
||||
|
@ -16,13 +15,13 @@ async fn send_message(
|
|||
) -> Result<(), ()> {
|
||||
println!("SHIET {state:?}");
|
||||
|
||||
let mut client = state.lock().await;
|
||||
client
|
||||
.say_hello(HelloRequest {
|
||||
message,
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
// let mut client = state.lock().await;
|
||||
/* client
|
||||
.say_hello(HelloRequest {
|
||||
message,
|
||||
..Default::default()
|
||||
})
|
||||
.await; */
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -31,12 +30,15 @@ async fn send_message(
|
|||
async fn main() -> Result<()> {
|
||||
tauri::async_runtime::set(TokioHandle::current());
|
||||
|
||||
let greeter_client = GreeterClient::connect("http://[::1]:50051").await?;
|
||||
/*
|
||||
TODO: Make sure this doesn't necessarily have to connect before starting the app
|
||||
let greeter_client = ChatClient::connect("http://[::1]:50051").await?;
|
||||
let greeter_client = Mutex::new(greeter_client);
|
||||
println!("Connected :)");
|
||||
*/
|
||||
|
||||
tauri::Builder::default()
|
||||
.manage(greeter_client)
|
||||
// .manage(greeter_client)
|
||||
.invoke_handler(tauri::generate_handler![send_message])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
|
1
client/src/.gitignore
vendored
Normal file
1
client/src/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.scss.d.ts
|
|
@ -1,35 +1,18 @@
|
|||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./store";
|
||||
import { useState } from "react";
|
||||
|
||||
import styles from "./App.module.scss";
|
||||
import LeftSidebar from "./components/LeftSidebar";
|
||||
import CenterPanel from "./components/CenterPanel";
|
||||
|
||||
export default function App() {
|
||||
const [currentMessage, setCurrentMessage] = useState("");
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
invoke("send_message", { message: currentMessage });
|
||||
setCurrentMessage("");
|
||||
};
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<div className={styles.app}>
|
||||
<LeftSidebar />
|
||||
<h1>mraow chat</h1>
|
||||
|
||||
<form onSubmit={onSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Send message..."
|
||||
value={currentMessage}
|
||||
onChange={(e) => setCurrentMessage(e.currentTarget.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</form>
|
||||
<CenterPanel />
|
||||
</div>
|
||||
</Provider>
|
||||
);
|
||||
|
|
20
client/src/components/CenterPanel.module.scss
Normal file
20
client/src/components/CenterPanel.module.scss
Normal file
|
@ -0,0 +1,20 @@
|
|||
.centerPanel {
|
||||
flex-grow: 1;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.middlePart {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 18px;
|
||||
outline: none;
|
||||
}
|
|
@ -1 +1,52 @@
|
|||
export default function CenterPanel() {}
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import styles from "./CenterPanel.module.scss";
|
||||
import { useState } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../store";
|
||||
import { messageSelectors, messageSlice } from "../store/messages";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default function CenterPanel() {
|
||||
const [currentMessage, setCurrentMessage] = useState("");
|
||||
const dispatch = useAppDispatch();
|
||||
const allMessages = useAppSelector((state) =>
|
||||
messageSelectors.selectAll(state)
|
||||
);
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
invoke("send_message", { message: currentMessage });
|
||||
|
||||
const id = uuidv4();
|
||||
const time = new Date();
|
||||
dispatch(
|
||||
messageSlice.actions.addMessage({ id, time, content: currentMessage })
|
||||
);
|
||||
setCurrentMessage("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.centerPanel}>
|
||||
<h1>mraow chat</h1>
|
||||
|
||||
<div className={styles.middlePart}>
|
||||
{allMessages.map((msg) => (
|
||||
<div key={msg.id}>
|
||||
<small>{msg.time.toISOString()}</small>
|
||||
{msg.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<form onSubmit={onSubmit} className={styles.form}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Send message..."
|
||||
value={currentMessage}
|
||||
className={styles.input}
|
||||
onChange={(e) => setCurrentMessage(e.currentTarget.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
.leftSidebar {
|
||||
width: var(--left-sidebar-width);
|
||||
background-color: var(--left-sidebar-background-color);
|
||||
}
|
||||
|
|
5
client/src/components/MessageContainer.tsx
Normal file
5
client/src/components/MessageContainer.tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { useSelector } from "react-redux";
|
||||
|
||||
export default function MessageContainer() {
|
||||
return <></>;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { configureStore } from "@reduxjs/toolkit";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
14
client/src/store/index.ts
Normal file
14
client/src/store/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { messageSlice } from "./messages";
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
messages: messageSlice.reducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
24
client/src/store/messages.ts
Normal file
24
client/src/store/messages.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { createEntityAdapter, createSlice } from "@reduxjs/toolkit";
|
||||
import { RootState } from ".";
|
||||
|
||||
export type Message = {
|
||||
id: string;
|
||||
time: Date;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export const messageAdapter = createEntityAdapter<Message>({
|
||||
selectId: (item) => item.id,
|
||||
});
|
||||
|
||||
export const messageSelectors = messageAdapter.getSelectors<RootState>(
|
||||
(state) => state.messages
|
||||
);
|
||||
|
||||
export const messageSlice = createSlice({
|
||||
name: "messages",
|
||||
initialState: messageAdapter.getInitialState(),
|
||||
reducers: {
|
||||
addMessage: messageAdapter.addOne,
|
||||
},
|
||||
});
|
|
@ -1,4 +1,6 @@
|
|||
:root {
|
||||
--left-sidebar-width: 288px;
|
||||
|
||||
--main-background-color: #eee;
|
||||
--left-sidebar-background-color: #ddd;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { defineConfig } from "vite";
|
||||
import sassDts from "vite-plugin-sass-dts";
|
||||
|
||||
export default defineConfig({
|
||||
// prevent vite from obscuring rust errors
|
||||
|
@ -22,4 +23,6 @@ export default defineConfig({
|
|||
// produce sourcemaps for debug builds
|
||||
sourcemap: !!process.env.TAURI_DEBUG,
|
||||
},
|
||||
|
||||
plugins: [sassDts({ global: { generate: true } })],
|
||||
});
|
||||
|
|
9
docker-compose.yml
Normal file
9
docker-compose.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
database:
|
||||
image: cassandra
|
||||
volumes:
|
||||
- ./docker-data/cassandra:/var/lib/cassandra
|
||||
ports:
|
||||
- "7000:7000"
|
|
@ -5,43 +5,42 @@ package chat;
|
|||
import "google/protobuf/empty.proto";
|
||||
|
||||
message ChatMessage {
|
||||
string from = 1;
|
||||
string msg = 2;
|
||||
string time = 3;
|
||||
string messageId = 1;
|
||||
string fromUserId = 2;
|
||||
string toUserId = 3;
|
||||
string toRoomId = 4;
|
||||
string content = 5;
|
||||
}
|
||||
|
||||
message Channel {}
|
||||
message Room {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message UserList { repeated User user = 1; }
|
||||
message UserList { repeated User users = 1; }
|
||||
|
||||
message RoomList {}
|
||||
message RoomList {repeated Room rooms = 1;}
|
||||
|
||||
message User { string username = 1; }
|
||||
|
||||
// RPC Messages
|
||||
|
||||
message JoinResponse {}
|
||||
|
||||
service Chat {
|
||||
rpc join(User) returns (JoinResponse) {}
|
||||
rpc sendMsg(ChatMessage) returns (google.protobuf.Empty) {}
|
||||
rpc receiveMsg(google.protobuf.Empty) returns (stream ChatMessage) {}
|
||||
message RoomAction {
|
||||
string roomId = 1;
|
||||
string userId = 2;
|
||||
string action = 3;
|
||||
}
|
||||
|
||||
service Chat {
|
||||
// Rooms
|
||||
rpc roomAction(RoomAction) returns (JoinResponse) {}
|
||||
|
||||
// Messages
|
||||
rpc sendMsg(ChatMessage) returns (google.protobuf.Empty) {}
|
||||
rpc receiveMsgs(google.protobuf.Empty) returns (stream ChatMessage) {}
|
||||
|
||||
// List
|
||||
rpc listRooms(google.protobuf.Empty) returns (RoomList) {}
|
||||
rpc getAllUsers(google.protobuf.Empty) returns (UserList) {}
|
||||
}
|
||||
|
||||
// Hello world shit
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello(HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message HelloReply { string message = 1; }
|
||||
}
|
1
server/Dockerfile
Normal file
1
server/Dockerfile
Normal file
|
@ -0,0 +1 @@
|
|||
FROM rust
|
|
@ -12,8 +12,7 @@ use tower::{Layer, Service};
|
|||
|
||||
use mraow_common::chat_proto::{
|
||||
chat_server::{Chat, ChatServer},
|
||||
ChatMessage, HelloReply, HelloRequest, JoinResponse, RoomList, User,
|
||||
UserList,
|
||||
ChatMessage, JoinResponse, RoomList, User, UserList,
|
||||
};
|
||||
|
||||
type ResponseStream =
|
||||
|
|
Loading…
Reference in a new issue