More UI styling

This commit is contained in:
Michael Zhang 2023-05-09 04:31:46 -05:00
parent 87ccab7a4b
commit 25179a74f7
23 changed files with 270 additions and 77 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
./docker-data
./target

0
.env Normal file
View file

1
.envrc
View file

@ -1 +1,2 @@
dotenv .env
use flake

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
sytest
node_modules
/target
docker-data

View file

@ -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"
}
}
}
}

View file

@ -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"
}
}

View file

@ -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
View file

@ -0,0 +1 @@
*.scss.d.ts

View file

@ -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>
);

View 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;
}

View file

@ -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>
);
}

View file

@ -1,3 +1,4 @@
.leftSidebar {
width: var(--left-sidebar-width);
background-color: var(--left-sidebar-background-color);
}

View file

@ -0,0 +1,5 @@
import { useSelector } from "react-redux";
export default function MessageContainer() {
return <></>;
}

View file

@ -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
View 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;

View 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,
},
});

View file

@ -1,4 +1,6 @@
:root {
--left-sidebar-width: 288px;
--main-background-color: #eee;
--left-sidebar-background-color: #ddd;

View file

@ -1,5 +1,6 @@
{
"compilerOptions": {
"jsx": "react-jsx"
}
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react-jsx"
}
}

View file

@ -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
View file

@ -0,0 +1,9 @@
version: "3"
services:
database:
image: cassandra
volumes:
- ./docker-data/cassandra:/var/lib/cassandra
ports:
- "7000:7000"

View file

@ -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
View file

@ -0,0 +1 @@
FROM rust

View file

@ -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 =