@@ -5,7 +5,7 @@ use perseus::shell::{get_initial_state, get_render_cfg, InitialState};
5
5
use perseus:: { app_shell, create_app_route, detect_locale, ClientTranslationsManager , DomNode } ;
6
6
use std:: cell:: RefCell ;
7
7
use std:: rc:: Rc ;
8
- use sycamore:: prelude:: { template, StateHandle } ;
8
+ use sycamore:: prelude:: { cloned , template, NodeRef , StateHandle } ;
9
9
use sycamore_router:: { HistoryIntegration , Router , RouterProps } ;
10
10
use wasm_bindgen:: { prelude:: wasm_bindgen, JsValue } ;
11
11
@@ -23,14 +23,17 @@ pub fn run() -> Result<(), JsValue> {
23
23
. unwrap ( )
24
24
. unwrap ( ) ;
25
25
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
27
28
// 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 ( )
29
30
. unwrap ( )
30
31
. document ( )
31
32
. unwrap ( )
32
33
. query_selector ( "#__perseus_content" )
33
34
. unwrap ( ) ;
35
+ // And create a node reference that we can use as a handle to the reactive verison
36
+ let container_rx = NodeRef :: new ( ) ;
34
37
35
38
// Create a mutable translations manager to control caching
36
39
let translations_manager =
@@ -51,41 +54,49 @@ pub fn run() -> Result<(), JsValue> {
51
54
|| {
52
55
template ! {
53
56
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
+ }
89
100
} ) )
90
101
}
91
102
} ,
0 commit comments