hul ignennem
This commit is contained in:
parent
bfabcc0abc
commit
fb1b863746
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -232,9 +232,12 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
"reqwasm",
|
"reqwasm",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-logger",
|
"wasm-logger",
|
||||||
"yew",
|
"yew",
|
||||||
|
"yew-agent",
|
||||||
"yew-router",
|
"yew-router",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -344,8 +347,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -364,7 +369,7 @@ dependencies = [
|
|||||||
"gloo-storage",
|
"gloo-storage",
|
||||||
"gloo-timers",
|
"gloo-timers",
|
||||||
"gloo-utils",
|
"gloo-utils",
|
||||||
"gloo-worker",
|
"gloo-worker 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -519,6 +524,23 @@ dependencies = [
|
|||||||
"web-sys",
|
"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]]
|
[[package]]
|
||||||
name = "gloo-worker"
|
name = "gloo-worker"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1456,6 +1478,7 @@ checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"serde",
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1688,6 +1711,16 @@ dependencies = [
|
|||||||
"yew-macro",
|
"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]]
|
[[package]]
|
||||||
name = "yew-macro"
|
name = "yew-macro"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
@ -9,4 +9,4 @@ edition = "2021"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
common = { path = "crates/common" }
|
common = { path = "crates/common" }
|
||||||
uuid = { vserion = "1.3", features = ["serde", "v4"] }
|
uuid = { vserion = "1.3", features = ["serde", "v4", "js"] }
|
||||||
|
@ -129,7 +129,7 @@ async fn main() {
|
|||||||
async fn ws_handler(
|
async fn ws_handler(
|
||||||
ws: WebSocketUpgrade,
|
ws: WebSocketUpgrade,
|
||||||
user_agent: Option<TypedHeader<headers::UserAgent>>,
|
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 {
|
) -> impl IntoResponse {
|
||||||
let user_agent = if let Some(TypedHeader(user_agent)) = user_agent {
|
let user_agent = if let Some(TypedHeader(user_agent)) = user_agent {
|
||||||
user_agent.to_string()
|
user_agent.to_string()
|
||||||
@ -138,15 +138,15 @@ async fn ws_handler(
|
|||||||
};
|
};
|
||||||
tracing::debug!("{user_agent} connected websocket.");
|
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)
|
/// Websocket statemachine (one will be spawned per connection)
|
||||||
async fn handle_socket(mut socket: WebSocket, app_state_watch_rx: tokio::sync::watch::Receiver<AppState>) {
|
async fn handle_socket(mut socket: WebSocket, state_watch_rx: tokio::sync::watch::Receiver<common::State>) {
|
||||||
let mut stream = tokio_stream::wrappers::WatchStream::new(app_state_watch_rx);
|
let mut stream = tokio_stream::wrappers::WatchStream::new(state_watch_rx);
|
||||||
loop {
|
while let Some(state) = stream.next().await {
|
||||||
let app_state = stream.next().await;
|
let state: common::WebSocketMessage = state;
|
||||||
let serialized = serde_json::to_string(&app_state).expect("Failed to serialize app state to JSON");
|
let serialized = serde_json::to_string(&state).expect("Failed to serialize app state to JSON");
|
||||||
if socket.send(Message::Text(serialized)).await.is_err() {
|
if socket.send(Message::Text(serialized)).await.is_err() {
|
||||||
tracing::debug!("Websocket client disconnected");
|
tracing::debug!("Websocket client disconnected");
|
||||||
break;
|
break;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
pub type WebSocketMessage = State;
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub achievements: Vec<Achievement>,
|
pub achievements: Vec<Achievement>,
|
||||||
|
@ -13,3 +13,6 @@ common.workspace = true
|
|||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
wasm-bindgen-futures = "0.4.36"
|
wasm-bindgen-futures = "0.4.36"
|
||||||
reqwasm = "0.5.0"
|
reqwasm = "0.5.0"
|
||||||
|
yew-agent = "0.2.0"
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json = "1.0.96"
|
||||||
|
@ -3,5 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Achievements</title>
|
<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>
|
</head>
|
||||||
</html>
|
</html>
|
||||||
|
4
crates/frontend/src/bin/app.rs
Normal file
4
crates/frontend/src/bin/app.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
fn main() {
|
||||||
|
wasm_logger::init(wasm_logger::Config::default());
|
||||||
|
yew::Renderer::<frontend::App>::new().render();
|
||||||
|
}
|
6
crates/frontend/src/bin/event_bus.rs
Normal file
6
crates/frontend/src/bin/event_bus.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use frontend::event_bus::EventBus;
|
||||||
|
use yew_agent::PublicWorker;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
EventBus::register();
|
||||||
|
}
|
@ -1,26 +1,75 @@
|
|||||||
|
use crate::event_bus::EventBus;
|
||||||
|
use crate::services::websocket::WebsocketService;
|
||||||
|
use crate::AppState;
|
||||||
use crate::Route;
|
use crate::Route;
|
||||||
|
use common::Achievement;
|
||||||
|
use std::rc::Rc;
|
||||||
use yew::functional::*;
|
use yew::functional::*;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
use yew_agent::Bridge;
|
||||||
|
use yew_agent::Bridged;
|
||||||
use yew_router::prelude::*;
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
#[function_component(Root)]
|
pub struct Root {
|
||||||
pub fn root() -> Html {
|
wss: WebsocketService,
|
||||||
let app_state = use_context::<crate::AppState>().expect("no context found");
|
_producer: Box<dyn Bridge<EventBus>>,
|
||||||
|
achievements: Vec<Achievement>,
|
||||||
|
}
|
||||||
|
|
||||||
let achievements = app_state
|
pub enum Msg {
|
||||||
.state
|
HandleMsg(String),
|
||||||
.achievements
|
}
|
||||||
.iter()
|
|
||||||
.map(|a| {
|
impl Component for Root {
|
||||||
html! {
|
type Message = Msg;
|
||||||
<p>{format!("{}", a.goal)}</p>
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.collect::<Html>();
|
}
|
||||||
|
|
||||||
html! {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
<div>
|
let achievements = self
|
||||||
{achievements}
|
.achievements
|
||||||
</div>
|
.iter()
|
||||||
|
.map(|a| {
|
||||||
|
html! {
|
||||||
|
<p>{format!("{}", a.goal)}</p>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Html>();
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div>
|
||||||
|
{achievements}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
54
crates/frontend/src/event_bus.rs
Normal file
54
crates/frontend/src/event_bus.rs
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ use yew_router::Routable;
|
|||||||
use yew_router::Switch;
|
use yew_router::Switch;
|
||||||
|
|
||||||
mod components;
|
mod components;
|
||||||
|
pub mod event_bus;
|
||||||
mod services;
|
mod services;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Routable)]
|
#[derive(Debug, Clone, Copy, PartialEq, Routable)]
|
||||||
@ -35,16 +36,7 @@ struct AppStateInner {
|
|||||||
type AppState = Rc<AppStateInner>;
|
type AppState = Rc<AppStateInner>;
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
fn App() -> Html {
|
pub fn App() -> Html {
|
||||||
// let counter = use_state(|| 0);
|
|
||||||
// let onclick = {
|
|
||||||
// let counter = counter.clone();
|
|
||||||
// move |_| {
|
|
||||||
// let value = *counter + 1;
|
|
||||||
// counter.set(value);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
let ctx = use_state(|| Rc::new(AppStateInner::default()));
|
let ctx = use_state(|| Rc::new(AppStateInner::default()));
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
@ -57,8 +49,3 @@ fn App() -> Html {
|
|||||||
</ContextProvider<AppState>>
|
</ContextProvider<AppState>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
wasm_logger::init(wasm_logger::Config::default());
|
|
||||||
yew::Renderer::<App>::new().render();
|
|
||||||
}
|
|
@ -1,9 +1,12 @@
|
|||||||
|
use crate::event_bus::EventBus;
|
||||||
|
use crate::event_bus::EventBusInput;
|
||||||
use futures::channel::mpsc::Sender;
|
use futures::channel::mpsc::Sender;
|
||||||
use futures::SinkExt;
|
use futures::SinkExt;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use reqwasm::websocket::futures::WebSocket;
|
use reqwasm::websocket::futures::WebSocket;
|
||||||
use reqwasm::websocket::Message;
|
use reqwasm::websocket::Message;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
|
use yew_agent::Dispatched;
|
||||||
|
|
||||||
pub struct WebsocketService {
|
pub struct WebsocketService {
|
||||||
pub tx: Sender<String>,
|
pub tx: Sender<String>,
|
||||||
@ -11,11 +14,12 @@ pub struct WebsocketService {
|
|||||||
|
|
||||||
impl WebsocketService {
|
impl WebsocketService {
|
||||||
pub fn new() -> Self {
|
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 (mut write, mut read) = ws.split();
|
||||||
|
|
||||||
let (in_tx, mut in_rx) = futures::channel::mpsc::channel::<String>(1000);
|
let (in_tx, mut in_rx) = futures::channel::mpsc::channel::<String>(1000);
|
||||||
|
let mut event_bus = EventBus::dispatcher();
|
||||||
|
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
while let Some(s) = in_rx.next().await {
|
while let Some(s) = in_rx.next().await {
|
||||||
@ -29,15 +33,17 @@ impl WebsocketService {
|
|||||||
match msg {
|
match msg {
|
||||||
Ok(Message::Text(data)) => {
|
Ok(Message::Text(data)) => {
|
||||||
log::debug!("from websocket: {}", data);
|
log::debug!("from websocket: {}", data);
|
||||||
|
event_bus.send(EventBusInput::EventBusMsg(data));
|
||||||
}
|
}
|
||||||
Ok(Message::Bytes(b)) => {
|
Ok(Message::Bytes(b)) => {
|
||||||
let decoded = std::str::from_utf8(&b);
|
let decoded = std::str::from_utf8(&b);
|
||||||
if let Ok(val) = decoded {
|
if let Ok(val) = decoded {
|
||||||
log::debug!("from websocket: {}", val);
|
log::debug!("from websocket: {}", val);
|
||||||
|
event_bus.send(EventBusInput::EventBusMsg(val.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("ws: {:?}", e)
|
log::error!("ws: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user