mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
examples: update hackernews for SSR support
This commit is contained in:
parent
f8da9e30e0
commit
e6a472b467
7 changed files with 43 additions and 52 deletions
|
@ -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"]
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)>
|
||||
>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue