Fixup SSR and links

This commit is contained in:
Jeremy Wall 2022-08-14 20:46:29 -04:00
parent 856fac5ece
commit c9bcfe220e
4 changed files with 33 additions and 98 deletions

View File

@ -73,14 +73,23 @@ where
async fn ui_assets(Path(path): Path<String>) -> impl IntoResponse { async fn ui_assets(Path(path): Path<String>) -> impl IntoResponse {
info!("Serving ui path"); info!("Serving ui path");
let mut path = path.trim_start_matches("/"); let path = path.trim_start_matches("/");
path = if path == "" { "index.html" } else { path };
debug!(path = path, "Serving transformed path"); debug!(path = path, "Serving transformed path");
let file = StaticFile(path.to_owned()); let file = StaticFile(path.to_owned());
// TODO(jwall): We need to construct the entire html page here.
// not just this split form.
if file.exists() { if file.exists() {
file.into_response() file.into_response()
} else { } else {
kitchen_wasm::render_to_string(path).into_response() let index = UiAssets::get("index.html").expect("Unexpectedly can't find index.html");
let body = boxed(Full::from(
String::from_utf8_lossy(index.data.as_ref())
.replace("%kitchen-wasm", &kitchen_wasm::render_to_string(path)),
));
Response::builder()
.header(header::CONTENT_TYPE, "text/html")
.body(body)
.unwrap()
} }
} }

View File

@ -24,7 +24,7 @@
</head> </head>
<body> <body>
<div id="main"></div> <div id="main">%kitchen-wasm</div>
<script type="module"> <script type="module">
import init, { } from './kitchen_wasm.js'; import init, { } from './kitchen_wasm.js';

View File

@ -20,14 +20,11 @@ mod web;
use router_integration::DeriveRoute; use router_integration::DeriveRoute;
use sycamore::prelude::*; use sycamore::prelude::*;
#[cfg(target_arch = "wasm32")]
use tracing_browser_subscriber; use tracing_browser_subscriber;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use web::UI; use web::UI;
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(start)] #[wasm_bindgen(start)]
pub fn main() { pub fn main() {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
@ -36,11 +33,11 @@ pub fn main() {
.unwrap() .unwrap()
.document() .document()
.unwrap() .unwrap()
.query_selector("#sycamore") .query_selector("#main")
.unwrap() .unwrap()
.unwrap(); .unwrap();
sycamore::hydrate_to(|| view! { UI() }, &root); sycamore::hydrate_to(|| view! { UI(None) }, &root);
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -48,5 +45,5 @@ pub fn render_to_string(path: &str) -> String {
use app_state::AppRoutes; use app_state::AppRoutes;
let route = <AppRoutes as DeriveRoute>::from(&(String::new(), path.to_owned(), String::new())); let route = <AppRoutes as DeriveRoute>::from(&(String::new(), path.to_owned(), String::new()));
sycamore::render_to_string(|| view! { UI(route) }) sycamore::render_to_string(|| view! { UI(Some(route)) })
} }

View File

@ -22,7 +22,6 @@ use sycamore::{
prelude::*, prelude::*,
}; };
#[cfg(target_arch = "wasm32")]
#[instrument] #[instrument]
fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> { fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
// NOTE(jwall): This needs to not be a dynamic node. The rules around // NOTE(jwall): This needs to not be a dynamic node. The rules around
@ -53,36 +52,6 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
} }
}) })
} }
#[cfg(not(target_arch = "wasm32"))]
fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
// NOTE(jwall): This needs to not be a dynamic node. The rules around
// this are somewhat unclear and underdocumented for Sycamore. But basically
// avoid conditionals in the `view!` macro calls here.
cloned!((route) => match route.get().as_ref() {
AppRoutes::Plan => view! {
PlanPage()
},
AppRoutes::Inventory => view! {
InventoryPage()
},
AppRoutes::Cook => view! {
CookPage()
},
AppRoutes::Recipe(idx) => view! {
RecipePage(RecipePageProps { recipe: Signal::new(idx.clone()) })
},
AppRoutes::NotFound => view! {
// TODO(Create a real one)
PlanPage()
},
AppRoutes::Error(ref e) => {
let e = e.clone();
view! {
"Error: " (e)
}
}
})
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn get_appservice() -> AppService<AsyncFileStore> { fn get_appservice() -> AppService<AsyncFileStore> {
@ -93,29 +62,29 @@ fn get_appservice() -> AppService<HttpStore> {
AppService::new(recipe_store::HttpStore::new("/api/v1".to_owned())) AppService::new(recipe_store::HttpStore::new("/api/v1".to_owned()))
} }
#[cfg(target_arch = "wasm32")]
#[component(RouterComponent<G>)] #[component(RouterComponent<G>)]
fn rounter_component() -> View<G> { fn router_component(route: Option<AppRoutes>) -> View<G> {
view! { match route {
Router(RouterProps{ Some(route) => {
route: AppRoutes::default(), view! {
browser_integration: BrowserIntegration::new(), StaticRouter(StaticRouterProps{route: route, route_select: route_switch})
route_select: route_switch, }
}) }
} None => {
} view! {
#[cfg(not(target_arch = "wasm32"))] Router(RouterProps{
#[component(RouterComponent<G>)] route: AppRoutes::default(),
fn rounter_component(route: AppRoutes) -> View<G> { browser_integration: BrowserIntegration::new(),
view! { route_select: route_switch,
StaticRouter(StaticRouterProps{route: route, route_select: route_switch}) })
}
}
} }
} }
#[cfg(target_arch = "wasm32")]
#[instrument] #[instrument]
#[component(UI<G>)] #[component(UI<G>)]
pub fn ui() -> View<G> { pub fn ui(route: Option<AppRoutes>) -> View<G> {
let app_service = get_appservice(); let app_service = get_appservice();
info!("Starting UI"); info!("Starting UI");
view! { view! {
@ -143,46 +112,6 @@ pub fn ui() -> View<G> {
}); });
view! { view! {
div(class="app") {
Header()
RouterComponent()
}
}
}
})
}
}
#[cfg(not(target_arch = "wasm32"))]
#[instrument]
#[component(UI<G>)]
pub fn ui(route: AppRoutes) -> View<G> {
let app_service = get_appservice();
info!("Rendering UI");
view! {
// NOTE(jwall): Set the app_service in our toplevel scope. Children will be able
// to find the service as long as they are a child of this scope.
ContextProvider(ContextProviderProps {
value: app_service.clone(),
children: || {
create_effect(move || {
spawn_local_in_scope({
let mut app_service = app_service.clone();
async move {
debug!("fetching recipes");
match app_service.fetch_recipes_from_storage() {
Ok((_, Some(recipes))) => {
app_service.set_recipes(recipes);
}
Ok((_, None)) => {
error!("No recipes to find");
}
Err(msg) => error!("Failed to get recipes {}", msg),
}
}
});
});
view!{
div(class="app") { div(class="app") {
Header() Header()
RouterComponent(route) RouterComponent(route)