feat: figured out leptos networking

This commit is contained in:
Andrew Rioux
2025-01-23 21:11:25 -05:00
parent 8499c0aee9
commit 6e99dc3d70
10 changed files with 427 additions and 67 deletions

View File

@@ -5,6 +5,21 @@ use leptos_router::{
StaticSegment,
};
#[server]
pub async fn test_retrieve() -> Result<u64, ServerFnError> {
use leptos::server_fn::error::NoCustomError;
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
let start = std::time::SystemTime::now();
let since_the_epoch = start
.duration_since(std::time::UNIX_EPOCH)
.map_err(|e| ServerFnError::<NoCustomError>::ServerError(e.to_string()))?
.as_secs();
Ok(since_the_epoch)
}
pub fn shell(options: LeptosOptions) -> impl IntoView {
view! {
<!DOCTYPE html>
@@ -47,12 +62,84 @@ pub fn App() -> impl IntoView {
/// Renders the home page of your application.
#[component]
fn HomePage() -> impl IntoView {
use web_sys::WebSocket;
use std::sync::Arc;
use leptos_use::{UseWebSocketReturn, use_websocket};
// Creates a reactive value to update the button
let count = RwSignal::new(0);
let on_click = move |_| *count.write() += 1;
let loaded_time = Resource::new(|| (), |_| async move { test_retrieve().await });
let (requested_time, set_requested_time) = signal(None::<String>);
let request_time = Action::new(move |_: &()| async move {
let t = match test_retrieve().await {
Ok(t) => format!("{}", t),
Err(_) => "Error!".to_string()
};
set_requested_time(Some(t));
});
let request_time_callback = move |_| {
request_time.dispatch(());
};
let pending = request_time.pending();
let text_input = RwSignal::new("".to_owned());
let (messages, set_messages) = signal(Vec::<String>::new());
let UseWebSocketReturn { send, message, .. } = use_websocket::<String, String, codee::string::FromToStringCodec>("/ws");
Effect::new(move |_| {
message.with(move |message| {
if let Some(m) = message {
leptos::logging::log!("got update: {}", m);
set_messages.update(|messages: &mut Vec<_>| messages.push(format!("msg: {}", m)));
}
})
});
let send_message = move |_| {
send(&text_input.get());
text_input.set("".to_string());
};
view! {
<h1>"Welcome to Leptos!"</h1>
<button on:click=on_click>"Click Me: " {count}</button>
<Suspense
fallback=move || view! { <p>"Loading..."</p> }
>
<h2>"Loaded time:"</h2>
{move || {
loaded_time.get()
.map(|time| match time {
Ok(t) => view! { <p>{format!("{}", t)}</p>},
Err(_) => view! { <p>{"Error!".to_string()}</p> }
})
}}
<h2>"Requested time:"</h2>
<button on:click=request_time_callback>"Request new time"</button>
{move || pending.get().then_some("Loading...")}
{move || match requested_time.get() {
Some(t) => {
leptos::logging::log!("updating time display");
view! { <p>{t}</p> }
},
None => view! { <p>{"N/A".to_string()}</p> }
}}
</Suspense>
<h2>"Messages"</h2>
<input bind:value=text_input />
<input
on:click=send_message
type="button"
value="Send message"
/>
{move || messages
.get()
.iter()
.map(|message| view! { <p>{message.clone()}</p> })
.collect::<Vec<_>>()}
}
}

2
sparse-server/src/db.rs Normal file
View File

@@ -0,0 +1,2 @@
mod beacons;
mod users;

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@

View File

@@ -14,6 +14,9 @@ mod cli;
#[cfg(feature = "ssr")]
mod webserver;
#[cfg(feature = "ssr")]
mod db;
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() -> anyhow::Result<()> {

View File

@@ -1,3 +1,37 @@
pub async fn websocket(ws: axum::extract::ws::WebSocketUpgrade) -> axum::response::Response {
ws.on_upgrade(handle_websocket)
}
async fn handle_websocket(mut socket: axum::extract::ws::WebSocket) {
use futures_util::StreamExt;
use tracing::info;
let mut count = 0;
loop {
tokio::select! {
msg = socket.recv() => {
match msg {
None => {
break;
}
Some(msg) => {
let Ok(axum::extract::ws::Message::Text(msg)) = msg else { continue; };
info!("Received message! {}", msg);
let Ok(_) = socket.send(axum::extract::ws::Message::Text(msg)).await else { break; };
}
}
}
_ = tokio::time::sleep(tokio::time::Duration::from_secs(1)) => {
let Ok(_) = socket.send(axum::extract::ws::Message::Text(format!("{}", count))).await else { break; };
count += 1;
}
}
}
}
pub async fn serve_web(options: crate::cli::Options) -> anyhow::Result<()> {
use axum::Router;
use leptos::logging::log;
@@ -21,6 +55,7 @@ pub async fn serve_web(options: crate::cli::Options) -> anyhow::Result<()> {
let routes = generate_route_list(App);
let app = Router::new()
.route("/ws", axum::routing::any(websocket))
.leptos_routes(&leptos_options, routes, {
let leptos_options = leptos_options.clone();
move || shell(leptos_options.clone())
@@ -30,7 +65,7 @@ pub async fn serve_web(options: crate::cli::Options) -> anyhow::Result<()> {
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr);
tracing::info!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await?;
axum::serve(listener, app.into_make_service()).await?;