upd
This commit is contained in:
parent
d0d8d7acaa
commit
b5902d67ea
12 changed files with 62 additions and 132 deletions
1
.tokeignore
Normal file
1
.tokeignore
Normal file
|
@ -0,0 +1 @@
|
|||
frontend/pnpm-lock.yaml
|
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
5
frontend/src/lib/interfaces.ts
Normal file
5
frontend/src/lib/interfaces.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export interface ServerMessage {
|
||||
author: string;
|
||||
body: string;
|
||||
server_timestamp: number;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue