Skip to content

Commit 197956b

Browse files
committed
fix(routing): 🐛 fixed link handling errors in #8
Links now behave correctly, but are still subject to #8.
1 parent 7f38ea7 commit 197956b

File tree

3 files changed

+184
-137
lines changed

3 files changed

+184
-137
lines changed

examples/cli/.perseus/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ sycamore = { version = "0.6", features = ["ssr"] }
1717
sycamore-router = "0.6"
1818
web-sys = { version = "0.3", features = ["Headers", "Request", "RequestInit", "RequestMode", "Response", "ReadableStream", "Window"] }
1919
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
20+
wasm-bindgen-futures = "0.4"
2021
serde = { version = "1", features = ["derive"] }
2122
serde_json = "1" # Possibly don't need?
2223
console_error_panic_hook = "0.1.6"

examples/cli/.perseus/src/lib.rs

+49-38
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use perseus::shell::{get_initial_state, get_render_cfg, InitialState};
55
use perseus::{app_shell, create_app_route, detect_locale, ClientTranslationsManager, DomNode};
66
use std::cell::RefCell;
77
use std::rc::Rc;
8-
use sycamore::prelude::{template, StateHandle};
8+
use sycamore::prelude::{cloned, template, NodeRef, StateHandle};
99
use sycamore_router::{HistoryIntegration, Router, RouterProps};
1010
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
1111

@@ -23,14 +23,17 @@ pub fn run() -> Result<(), JsValue> {
2323
.unwrap()
2424
.unwrap();
2525

26-
// Get the root we'll be injecting actual content into (created by the server)
26+
// Get the root that the server will have injected initial load content into
27+
// This will be moved into a reactive `<div>` by the app shell
2728
// This is an `Option<Element>` until we know we aren't doing loclae detection (in which case it wouldn't exist)
28-
let container = web_sys::window()
29+
let initial_container = web_sys::window()
2930
.unwrap()
3031
.document()
3132
.unwrap()
3233
.query_selector("#__perseus_content")
3334
.unwrap();
35+
// And create a node reference that we can use as a handle to the reactive verison
36+
let container_rx = NodeRef::new();
3437

3538
// Create a mutable translations manager to control caching
3639
let translations_manager =
@@ -51,41 +54,49 @@ pub fn run() -> Result<(), JsValue> {
5154
|| {
5255
template! {
5356
Router(RouterProps::new(HistoryIntegration::new(), move |route: StateHandle<AppRoute<DomNode>>| {
54-
match &route.get().as_ref().0 {
55-
// Perseus' custom routing system is tightly coupled to the template system, and returns exactly what we need for the app shell!
56-
// If a non-404 error occurred, it will be handled in the app shell
57-
RouteVerdict::Found(RouteInfo {
58-
path,
59-
template,
60-
locale
61-
}) => app_shell(
62-
path.clone(),
63-
template.clone(),
64-
locale.clone(),
65-
// We give the app shell a translations manager and let it get the `Rc<Translator>` itself (because it can do async safely)
66-
Rc::clone(&translations_manager),
67-
Rc::clone(&error_pages),
68-
container.unwrap().clone()
69-
),
70-
// If the user is using i18n, then they'll want to detect the locale on any paths missing a locale
71-
// Those all go to the same system that redirects to the appropriate locale
72-
// Note that `container` doesn't exist for this scenario
73-
RouteVerdict::LocaleDetection(path) => detect_locale(path.clone(), get_locales()),
74-
// We handle the 404 for the user for convenience
75-
// To get a translator here, we'd have to go async and dangerously check the URL
76-
// If this is an initial load, there'll already be an error message, so we should only proceed if the declaration is not `error`
77-
RouteVerdict::NotFound => {
78-
if let InitialState::Error(ErrorPageData { url, status, err }) = get_initial_state() {
79-
// Hydrate the error pages
80-
// Right now, we don't provide translators to any error pages that have come from the server
81-
error_pages.hydrate_page(&url, &status, &err, None, &container.unwrap());
82-
} else {
83-
get_error_pages::<DomNode>().get_template_for_page("", &404, "not found", None);
84-
}
85-
},
86-
};
87-
// Everything is based on hydration, and so we always return an empty template
88-
sycamore::template::Template::empty()
57+
wasm_bindgen_futures::spawn_local(cloned!((container_rx) => async move {
58+
let container_rx_elem = container_rx.get::<DomNode>().unchecked_into::<web_sys::Element>();
59+
match &route.get().as_ref().0 {
60+
// Perseus' custom routing system is tightly coupled to the template system, and returns exactly what we need for the app shell!
61+
// If a non-404 error occurred, it will be handled in the app shell
62+
RouteVerdict::Found(RouteInfo {
63+
path,
64+
template,
65+
locale
66+
}) => app_shell(
67+
path.clone(),
68+
template.clone(),
69+
locale.clone(),
70+
// We give the app shell a translations manager and let it get the `Rc<Translator>` itself (because it can do async safely)
71+
Rc::clone(&translations_manager),
72+
Rc::clone(&error_pages),
73+
initial_container.unwrap().clone(),
74+
container_rx_elem.clone()
75+
).await,
76+
// If the user is using i18n, then they'll want to detect the locale on any paths missing a locale
77+
// Those all go to the same system that redirects to the appropriate locale
78+
// Note that `container` doesn't exist for this scenario
79+
RouteVerdict::LocaleDetection(path) => detect_locale(path.clone(), get_locales()),
80+
// We handle the 404 for the user for convenience
81+
// To get a translator here, we'd have to go async and dangerously check the URL
82+
// If this is an initial load, there'll already be an error message, so we should only proceed if the declaration is not `error`
83+
RouteVerdict::NotFound => {
84+
if let InitialState::Error(ErrorPageData { url, status, err }) = get_initial_state() {
85+
// Hydrate the error pages
86+
// Right now, we don't provide translators to any error pages that have come from the server
87+
error_pages.hydrate_page(&url, &status, &err, None, &container_rx_elem);
88+
} else {
89+
get_error_pages::<DomNode>().get_template_for_page("", &404, "not found", None);
90+
}
91+
},
92+
};
93+
}));
94+
// This template is reactive, and will be updated as necessary
95+
// However, the server has already rendered initial load content elsewhere, so we move that into here as well in the app shell
96+
// The main reason for this is that the router only intercepts click events from its children
97+
template! {
98+
div(class="__perseus_content_rx", ref=container_rx) {}
99+
}
89100
}))
90101
}
91102
},

0 commit comments

Comments
 (0)