examples: update hackernews for SSR support

This commit is contained in:
Greg Johnston 2024-06-10 21:09:16 -04:00
parent f8da9e30e0
commit e6a472b467
7 changed files with 43 additions and 52 deletions

View file

@ -17,7 +17,7 @@ actix-files = { version = "0.6", optional = true }
actix-web = { version = "4", optional = true, features = ["macros"] }
console_log = "1"
console_error_panic_hook = "0.1"
leptos = { path = "../../leptos", features = ["tracing"] }
leptos = { path = "../../leptos" }
leptos_meta = { path = "../../meta" }
leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_router = { path = "../../router" }
@ -25,15 +25,10 @@ log = "0.4"
serde = { version = "1", features = ["derive"] }
gloo-net = { version = "0.6", features = ["http"] }
reqwest = { version = "0.12", features = ["json"] }
tracing = "0.1"
# openssl = { version = "0.10", features = ["v110"] }
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
send_wrapper = "0.6.0"
tracing-subscriber = "0.3"
tracing-subscriber-wasm = "0.1"
[features]
csr = ["leptos/csr"]
hydrate = ["leptos/hydrate"]

View file

@ -16,17 +16,16 @@ pub fn fetch_api<T>(
where
T: Serialize + DeserializeOwned,
{
use leptos::prelude::on_cleanup;
use send_wrapper::SendWrapper;
SendWrapper::new(async move {
use leptos::reactive_graph::owner::Owner;
let abort_controller =
SendWrapper::new(web_sys::AbortController::new().ok());
let abort_signal = abort_controller.as_ref().map(|a| a.signal());
// abort in-flight requests if, e.g., we've navigated away from this page
Owner::on_cleanup(move || {
on_cleanup(move || {
if let Some(abort_controller) = abort_controller.take() {
abort_controller.abort()
}

View file

@ -12,7 +12,7 @@ use std::time::Duration;
#[component]
pub fn App() -> impl IntoView {
provide_meta_context();
let (is_routing, set_is_routing) = create_signal(false);
let (is_routing, set_is_routing) = signal(false);
view! {
<Stylesheet id="leptos" href="/pkg/hackernews.css"/>
@ -29,6 +29,8 @@ pub fn App() -> impl IntoView {
<Route path=(StaticSegment("users"), ParamSegment("id")) view=User/>
<Route path=(StaticSegment("stories"), ParamSegment("id")) view=Story/>
<Route path=ParamSegment("stories") view=Stories/>
// TODO allow optional params without duplication
<Route path=StaticSegment("") view=Stories/>
</FlatRoutes>
</main>
</Router>
@ -38,7 +40,6 @@ pub fn App() -> impl IntoView {
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(App);
leptos::mount::hydrate_body(App);
}

View file

@ -4,7 +4,6 @@ mod ssr_imports {
pub use actix_files::Files;
pub use actix_web::*;
pub use hackernews::App;
pub use leptos_actix::{generate_route_list, LeptosRoutes};
#[get("/style.css")]
pub async fn css() -> impl Responder {
@ -19,24 +18,44 @@ mod ssr_imports {
#[cfg(feature = "ssr")]
#[actix_web::main]
async fn main() -> std::io::Result<()> {
use leptos::get_configuration;
use leptos::prelude::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
use leptos_meta::MetaTags;
use ssr_imports::*;
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(App);
HttpServer::new(move || {
// Generate the list of routes in your Leptos App
let routes = generate_route_list(App);
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
App::new()
.service(css)
.service(favicon)
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), App)
.leptos_routes(routes, {
let leptos_options = leptos_options.clone();
move || {
use leptos::prelude::*;
view! {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<AutoReload options=leptos_options.clone() />
<HydrationScripts options=leptos_options.clone()/>
<MetaTags/>
</head>
<body>
<App/>
</body>
</html>
}
}})
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})
@ -49,21 +68,6 @@ async fn main() -> std::io::Result<()> {
#[cfg(not(feature = "ssr"))]
fn main() {
use hackernews::App;
use tracing_subscriber::fmt;
use tracing_subscriber_wasm::MakeConsoleWriter;
fmt()
.with_writer(
// To avoide trace events in the browser from showing their
// JS backtrace, which is very annoying, in my opinion
MakeConsoleWriter::default()
.map_trace_level_to(tracing::Level::DEBUG),
)
// For some reason, if we don't do this in the browser, we get
// a runtime error.
.without_time()
.init();
console_error_panic_hook::set_once();
leptos::mount::mount_to_body(App)
}

View file

@ -39,18 +39,12 @@ pub fn Stories() -> impl IntoView {
api::fetch_api::<Vec<api::Story>>(&api::story(&path)).await
},
);
let (pending, set_pending) = create_signal(false);
let (pending, set_pending) = signal(false);
let hide_more_link = move || {
Suspend(async move {
stories
.await
.as_ref()
.map(|vec| vec.len() < 28)
.unwrap_or(true)
|| pending.get()
})
};
let hide_more_link = move || match &*stories.read() {
Some(Some(stories)) => stories.len() < 28,
_ => true
} || pending.get();
view! {
<div class="news-view">
@ -76,9 +70,8 @@ pub fn Stories() -> impl IntoView {
<span>"page " {page}</span>
<Suspense>
<span class="page-link"
// TODO support Suspense in attributes
/*class:disabled=Suspend(hide_more_link)
aria-hidden=Suspend(hide_more_link)*/
class:disabled=hide_more_link
aria-hidden=hide_more_link
>
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
aria-label="Next Page"
@ -92,8 +85,7 @@ pub fn Stories() -> impl IntoView {
<div>
<Transition
fallback=move || view! { <p>"Loading..."</p> }
// TODO set_pending on Transition
//set_pending
set_pending
>
<Show when=move || stories.read().as_ref().map(Option::is_none).unwrap_or(false)>
>

View file

@ -68,7 +68,7 @@ pub fn Story() -> impl IntoView {
#[component]
pub fn Comment(comment: api::Comment) -> impl IntoView {
let (open, set_open) = create_signal(true);
let (open, set_open) = signal(true);
view! {
<li class="comment">

View file

@ -1,7 +1,7 @@
use crate::api::{self, User};
use leptos::server::Resource;
use leptos::{either::Either, prelude::*};
use leptos_router::{hooks::use_params_map, *};
use leptos_router::hooks::use_params_map;
#[component]
pub fn User() -> impl IntoView {