This commit is contained in:
Michael Zhang 2023-12-25 16:39:01 -05:00
parent d0d8d7acaa
commit b5902d67ea
12 changed files with 62 additions and 132 deletions

1
.tokeignore Normal file
View file

@ -0,0 +1 @@
frontend/pnpm-lock.yaml

3
Cargo.lock generated
View file

@ -261,6 +261,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"chrono",
"common",
"dashmap",
"ed25519-compact",
@ -788,6 +789,7 @@ dependencies = [
"anyhow",
"capnp",
"capnpc",
"chrono",
"serde",
]
@ -1675,6 +1677,7 @@ dependencies = [
name = "frontend"
version = "0.0.0"
dependencies = [
"chrono",
"common",
"futures",
"reqwest",

View file

@ -2,8 +2,11 @@
## Roadmap
- [ ] send messages from one client to another
- [x] send messages from one client to another
- [ ] save messages to database
- [ ] get rid of unwraps
- [ ] handle disconnect correctly
- [ ] user accounts
- [ ] retrieve history
- [ ] notifications
- [ ] multiple clients

View file

@ -19,3 +19,4 @@ ring = "0.17.7"
serde = "1.0.193"
serde_json = "1.0.108"
tokio = { version = "1.35.1", features = ["full"] }
chrono = { version = "0.4.31", features = ["serde"] }

View file

@ -3,7 +3,7 @@ extern crate serde;
mod prisma;
use std::{future::Future, sync::Arc};
use std::sync::Arc;
use anyhow::Result;
use axum::{
@ -11,13 +11,13 @@ use axum::{
ws::{Message as WsMessage, WebSocket},
Query, State, WebSocketUpgrade,
},
response::{sse::Event, Response},
response::Response,
routing::{get, post},
Json, Router,
};
use common::Message;
use chrono::Utc;
use common::{ClientMessage, Message};
use dashmap::DashMap;
use futures::FutureExt;
use prisma::PrismaClient;
use serde_json::{json, Value};
use tokio::sync::broadcast::{self, Sender};
@ -56,14 +56,18 @@ async fn main() -> Result<()> {
#[derive(Debug, Deserialize)]
struct SendMessageRequest {
#[serde(flatten)]
message: Message,
message: ClientMessage,
}
async fn send_message(
State(state): State<AppState>,
Json(request): Json<SendMessageRequest>,
) -> Json<Value> {
state.room_tx.send(request.message).unwrap();
let wrapped_message = Message {
inner: request.message,
server_timestamp: Utc::now(),
};
state.room_tx.send(wrapped_message).unwrap();
println!("Got message from client, forwarding to room...");
Json(json!({}))
@ -95,6 +99,11 @@ async fn event_stream(
let result = room_rx.recv().await.unwrap();
println!("Received message: {result:?}");
let payload = serde_json::to_string(&result).unwrap();
socket.send(WsMessage::Text(payload)).await.unwrap();
match socket.send(WsMessage::Text(payload)).await {
Ok(_) => {}
Err(err) => {
eprintln!("Error: {err}")
}
}
}
}

View file

@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
anyhow = { version = "1.0.76", features = ["backtrace"] }
capnp = "0.18.10"
chrono = { version = "0.4.31", features = ["serde"] }
serde = { version = "1.0.193", features = ["derive"] }
[build-dependencies]

View file

@ -1,3 +1,5 @@
use chrono::{DateTime, Utc};
#[macro_use]
extern crate serde;
@ -6,7 +8,14 @@ pub mod clientserver_capnp {
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub struct ClientMessage {
pub author: String,
pub body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
#[serde(flatten)]
pub inner: ClientMessage,
pub server_timestamp: DateTime<Utc>,
}

View file

@ -23,6 +23,7 @@ tokio = { version = "1.35.1", features = ["full"] }
reqwest = { version = "0.11.23", features = ["json"] }
futures = "0.3.30"
url = "2.5.0"
chrono = { version = "0.4.31", features = ["serde"] }
[features]
# this feature is used for production builds or when `devPath` points to the filesystem

View file

@ -1,7 +1,8 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use common::Message;
use chrono::Utc;
use common::{ClientMessage, Message};
use futures::{stream::SplitSink, SinkExt, StreamExt};
use tauri::{State, Window};
use tokio::{net::TcpStream, sync::RwLock};
@ -46,7 +47,8 @@ async fn connect(
// &format!("/v1/events?name={username}"));
println!("connecting to {url}");
let (socket, response) = tokio_tungstenite::connect_async(url).await.unwrap();
let (socket, _response) =
tokio_tungstenite::connect_async(url).await.unwrap();
let (socket_write, mut socket_read) = socket.split();
@ -88,7 +90,7 @@ async fn send_message(
match *state_ref {
Connection::Unconnected => return Err(format!("L")),
Connection::Connected(ref state) => {
let message = Message {
let message = ClientMessage {
author: state.username.clone(),
body: content,
};

View file

@ -3,6 +3,7 @@ import { invoke } from "@tauri-apps/api/tauri";
import "./App.css";
import { listen } from "@tauri-apps/api/event";
import { createStore } from "solid-js/store";
import { ServerMessage } from "./lib/interfaces";
const initialRandomNumber = Math.floor(Math.random() * 9000) + 1000;
@ -15,7 +16,7 @@ function App() {
status: "unconnected",
});
const [messageContent, setMessageContent] = createSignal("");
const [messages, setMessages] = createStore([]);
const [messages, setMessages] = createStore<ServerMessage[]>([]);
// const [greetMsg, setGreetMsg] = createSignal("");
// const [name, setName] = createSignal("");
@ -35,13 +36,6 @@ function App() {
setConnectionStatus({ status: "connected" });
};
const sendMessage = async (evt) => {
evt.preventDefault();
await invoke("send_message", { content: messageContent() });
console.log("SHIET");
};
const action = () => {
switch (connectionStatus().status) {
case "unconnected":
@ -55,7 +49,7 @@ function App() {
const disabled = () => connectionStatus().status === "connecting";
createEffect(async () => {
const unlisten = await listen("new_message", (event) => {
const unlisten = await listen<ServerMessage>("new_message", (event) => {
console.log("new message", event);
setMessages([...messages, event.payload]);
});
@ -71,11 +65,13 @@ function App() {
<input
placeholder="Server address..."
value={serverAddress()}
onChange={(evt) => setServerAddress(evt.currentTarget.value)}
disabled={disabled()}
/>
<input
placeholder="Username..."
value={username()}
onChange={(evt) => setUsername(evt.currentTarget.value)}
disabled={disabled()}
/>
@ -93,11 +89,19 @@ function App() {
</div>
<div>
<form onSubmit={sendMessage}>
<form
onSubmit={async (evt) => {
evt.preventDefault();
await invoke("send_message", { content: messageContent() });
setMessageContent("");
console.log("SHIET");
}}
>
<input
placeholder="Send a message..."
value={messageContent()}
onChange={(evt) => setMessageContent(evt.target.value)}
onChange={(evt) => setMessageContent(evt.currentTarget.value)}
disabled={connectionStatus().status !== "connected"}
/>
</form>
</div>

View file

@ -0,0 +1,5 @@
export interface ServerMessage {
author: string;
body: string;
server_timestamp: number;
}

View file

@ -1,109 +0,0 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
.container {
margin: 0;
padding-top: 10vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: 0.75s;
}
.logo.tauri:hover {
filter: drop-shadow(0 0 2em #24c8db);
}
.row {
display: flex;
justify-content: center;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
h1 {
text-align: center;
}
input,
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
color: #0f0f0f;
background-color: #ffffff;
transition: border-color 0.25s;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
}
button {
cursor: pointer;
}
button:hover {
border-color: #396cd8;
}
button:active {
border-color: #396cd8;
background-color: #e8e8e8;
}
input,
button {
outline: none;
}
#greet-input {
margin-right: 5px;
}
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
background-color: #2f2f2f;
}
a:hover {
color: #24c8db;
}
input,
button {
color: #ffffff;
background-color: #0f0f0f98;
}
button:active {
background-color: #0f0f0f69;
}
}