hul ignennem

This commit is contained in:
Asger Juul Brunshøj 2023-06-11 14:36:21 +02:00
parent bfabcc0abc
commit fb1b863746
13 changed files with 189 additions and 43 deletions

35
Cargo.lock generated
View File

@ -232,9 +232,12 @@ dependencies = [
"futures",
"log",
"reqwasm",
"serde",
"serde_json",
"wasm-bindgen-futures",
"wasm-logger",
"yew",
"yew-agent",
"yew-router",
]
@ -344,8 +347,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -364,7 +369,7 @@ dependencies = [
"gloo-storage",
"gloo-timers",
"gloo-utils",
"gloo-worker",
"gloo-worker 0.2.1",
]
[[package]]
@ -519,6 +524,23 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-worker"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09110b5555bcafe508cee0fb94308af9aac7a85f980d3c88b270d117c6c6911d"
dependencies = [
"anymap2",
"bincode",
"gloo-console",
"gloo-utils",
"js-sys",
"serde",
"slab",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-worker"
version = "0.2.1"
@ -1456,6 +1478,7 @@ checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom",
"serde",
"wasm-bindgen",
]
[[package]]
@ -1688,6 +1711,16 @@ dependencies = [
"yew-macro",
]
[[package]]
name = "yew-agent"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06f7c5ed97fff22816bb00d3d82ebc0fc1119d7bbb9e07e62c0d2853f51920a"
dependencies = [
"gloo-worker 0.1.2",
"yew",
]
[[package]]
name = "yew-macro"
version = "0.20.0"

View File

@ -9,4 +9,4 @@ edition = "2021"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
common = { path = "crates/common" }
uuid = { vserion = "1.3", features = ["serde", "v4"] }
uuid = { vserion = "1.3", features = ["serde", "v4", "js"] }

View File

@ -129,7 +129,7 @@ async fn main() {
async fn ws_handler(
ws: WebSocketUpgrade,
user_agent: Option<TypedHeader<headers::UserAgent>>,
Extension(app_state_watch_rx): Extension<tokio::sync::watch::Receiver<AppState>>,
Extension(state_watch_rx): Extension<tokio::sync::watch::Receiver<common::State>>,
) -> impl IntoResponse {
let user_agent = if let Some(TypedHeader(user_agent)) = user_agent {
user_agent.to_string()
@ -138,15 +138,15 @@ async fn ws_handler(
};
tracing::debug!("{user_agent} connected websocket.");
ws.on_upgrade(move |socket| handle_socket(socket, app_state_watch_rx))
ws.on_upgrade(move |socket| handle_socket(socket, state_watch_rx))
}
/// Websocket statemachine (one will be spawned per connection)
async fn handle_socket(mut socket: WebSocket, app_state_watch_rx: tokio::sync::watch::Receiver<AppState>) {
let mut stream = tokio_stream::wrappers::WatchStream::new(app_state_watch_rx);
loop {
let app_state = stream.next().await;
let serialized = serde_json::to_string(&app_state).expect("Failed to serialize app state to JSON");
async fn handle_socket(mut socket: WebSocket, state_watch_rx: tokio::sync::watch::Receiver<common::State>) {
let mut stream = tokio_stream::wrappers::WatchStream::new(state_watch_rx);
while let Some(state) = stream.next().await {
let state: common::WebSocketMessage = state;
let serialized = serde_json::to_string(&state).expect("Failed to serialize app state to JSON");
if socket.send(Message::Text(serialized)).await.is_err() {
tracing::debug!("Websocket client disconnected");
break;

View File

@ -1,6 +1,8 @@
use serde::Deserialize;
use serde::Serialize;
pub type WebSocketMessage = State;
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct State {
pub achievements: Vec<Achievement>,

View File

@ -13,3 +13,6 @@ common.workspace = true
futures = "0.3.28"
wasm-bindgen-futures = "0.4.36"
reqwasm = "0.5.0"
yew-agent = "0.2.0"
serde.workspace = true
serde_json = "1.0.96"

View File

@ -3,5 +3,7 @@
<head>
<meta charset="utf-8" />
<title>Achievements</title>
<link data-trunk rel="rust" href="Cargo.toml" data-bin="app" data-type="main" />
<link data-trunk rel="rust" href="Cargo.toml" data-bin="event_bus" data-type="worker" />
</head>
</html>

View File

@ -0,0 +1,4 @@
fn main() {
wasm_logger::init(wasm_logger::Config::default());
yew::Renderer::<frontend::App>::new().render();
}

View File

@ -0,0 +1,6 @@
use frontend::event_bus::EventBus;
use yew_agent::PublicWorker;
fn main() {
EventBus::register();
}

View File

@ -1,14 +1,62 @@
use crate::event_bus::EventBus;
use crate::services::websocket::WebsocketService;
use crate::AppState;
use crate::Route;
use common::Achievement;
use std::rc::Rc;
use yew::functional::*;
use yew::prelude::*;
use yew_agent::Bridge;
use yew_agent::Bridged;
use yew_router::prelude::*;
#[function_component(Root)]
pub fn root() -> Html {
let app_state = use_context::<crate::AppState>().expect("no context found");
pub struct Root {
wss: WebsocketService,
_producer: Box<dyn Bridge<EventBus>>,
achievements: Vec<Achievement>,
}
let achievements = app_state
.state
pub enum Msg {
HandleMsg(String),
}
impl Component for Root {
type Message = Msg;
type Properties = ();
fn create(ctx: &Context<Self>) -> Self {
// let ctx_link = ctx
// .link()
// .context::<AppState>(Callback::noop())
// .expect("context to be set");
let wss = WebsocketService::new();
let cb = {
let link = ctx.link().clone();
move |e| link.send_message(Msg::HandleMsg(e))
};
Self {
wss,
_producer: EventBus::bridge(Rc::new(cb)),
achievements: vec![],
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::HandleMsg(s) => {
let msg: common::WebSocketMessage = serde_json::from_str(&s).unwrap();
self.achievements = msg.achievements;
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let achievements = self
.achievements
.iter()
.map(|a| {
@ -24,3 +72,4 @@ pub fn root() -> Html {
</div>
}
}
}

View File

@ -0,0 +1,54 @@
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashSet;
use yew_agent::HandlerId;
use yew_agent::Public;
use yew_agent::WorkerLink;
pub struct EventBus {
link: WorkerLink<Self>,
subscribers: HashSet<HandlerId>,
}
#[derive(Serialize, Deserialize)]
pub enum EventBusInput {
EventBusMsg(String),
}
impl yew_agent::Worker for EventBus {
type Reach = Public<Self>;
type Input = EventBusInput;
type Output = String;
type Message = ();
fn create(link: WorkerLink<Self>) -> Self {
Self {
link,
subscribers: HashSet::new(),
}
}
fn update(&mut self, _msg: Self::Message) {}
fn handle_input(&mut self, msg: Self::Input, id: HandlerId) {
match msg {
EventBusInput::EventBusMsg(s) => {
for sub in &self.subscribers {
self.link.respond(*sub, s.clone());
}
}
}
}
fn connected(&mut self, id: HandlerId) {
self.subscribers.insert(id);
}
fn disconnected(&mut self, id: HandlerId) {
self.subscribers.remove(&id);
}
fn name_of_resource() -> &'static str {
"event_bus.js"
}
}

View File

@ -7,6 +7,7 @@ use yew_router::Routable;
use yew_router::Switch;
mod components;
pub mod event_bus;
mod services;
#[derive(Debug, Clone, Copy, PartialEq, Routable)]
@ -35,16 +36,7 @@ struct AppStateInner {
type AppState = Rc<AppStateInner>;
#[function_component]
fn App() -> Html {
// let counter = use_state(|| 0);
// let onclick = {
// let counter = counter.clone();
// move |_| {
// let value = *counter + 1;
// counter.set(value);
// }
// };
pub fn App() -> Html {
let ctx = use_state(|| Rc::new(AppStateInner::default()));
html! {
@ -57,8 +49,3 @@ fn App() -> Html {
</ContextProvider<AppState>>
}
}
fn main() {
wasm_logger::init(wasm_logger::Config::default());
yew::Renderer::<App>::new().render();
}

View File

@ -1,9 +1,12 @@
use crate::event_bus::EventBus;
use crate::event_bus::EventBusInput;
use futures::channel::mpsc::Sender;
use futures::SinkExt;
use futures::StreamExt;
use reqwasm::websocket::futures::WebSocket;
use reqwasm::websocket::Message;
use wasm_bindgen_futures::spawn_local;
use yew_agent::Dispatched;
pub struct WebsocketService {
pub tx: Sender<String>,
@ -11,11 +14,12 @@ pub struct WebsocketService {
impl WebsocketService {
pub fn new() -> Self {
let ws = WebSocket::open("ws://127.0.0.1:4000").unwrap();
let ws = WebSocket::open("ws://127.0.0.1:4000/ws").unwrap();
let (mut write, mut read) = ws.split();
let (in_tx, mut in_rx) = futures::channel::mpsc::channel::<String>(1000);
let mut event_bus = EventBus::dispatcher();
spawn_local(async move {
while let Some(s) = in_rx.next().await {
@ -29,15 +33,17 @@ impl WebsocketService {
match msg {
Ok(Message::Text(data)) => {
log::debug!("from websocket: {}", data);
event_bus.send(EventBusInput::EventBusMsg(data));
}
Ok(Message::Bytes(b)) => {
let decoded = std::str::from_utf8(&b);
if let Ok(val) = decoded {
log::debug!("from websocket: {}", val);
event_bus.send(EventBusInput::EventBusMsg(val.into()));
}
}
Err(e) => {
log::error!("ws: {:?}", e)
log::error!("ws: {:?}", e);
}
}
}

View File

@ -18,7 +18,7 @@ fmt:
taplo fmt `fd --extension=toml`
run-backend:
RUST_LOG=debug cargo run -p backend
RUST_LOG=backend=debug cargo run -p backend
# POST /create
create-gul-bus: