189 lines
9.3 KiB
Rust
189 lines
9.3 KiB
Rust
//#![allow(unused_imports)]
|
|
//#![allow(dead_code)]
|
|
//#![allow(unused_variables)]
|
|
// Suppress leptos_router warnings about reactive signal access outside tracking context
|
|
#![allow(clippy::redundant_closure)]
|
|
//#![allow(unused_assignments)]
|
|
|
|
//use crate::defs::{NAV_LINK_CLASS, ROUTES};
|
|
use crate::auth::AuthProvider;
|
|
use crate::components::NavMenu;
|
|
use crate::i18n::{I18nProvider, ThemeProvider};
|
|
use crate::pages::{AboutPage, DaisyUIPage, FeaturesDemoPage, HomePage, UserPage};
|
|
use crate::state::*;
|
|
use crate::utils::make_popstate_effect;
|
|
use leptos::children::Children;
|
|
use leptos::prelude::*;
|
|
use leptos_meta::{MetaTags, Title, provide_meta_context};
|
|
// use regex::Regex;
|
|
use shared::{get_bundle, t};
|
|
use std::collections::HashMap;
|
|
|
|
//// Wrapper component for consistent layout.
|
|
#[component]
|
|
fn Wrapper(children: Children) -> impl IntoView {
|
|
view! { <>{children()}</> }
|
|
}
|
|
|
|
/// NotFoundPage component for 404s.
|
|
#[component]
|
|
fn NotFoundPage() -> impl IntoView {
|
|
view! { <div class="text-center">"Page not found."</div> }
|
|
}
|
|
|
|
/// Main app component with SSR path awareness and SPA routing.
|
|
#[component]
|
|
pub fn App(#[prop(default = String::new())] _initial_path: String) -> impl IntoView {
|
|
provide_meta_context();
|
|
|
|
// Always start with HOME during SSR, then route to correct page on client
|
|
let (path, set_path) = signal("/".to_string());
|
|
make_popstate_effect(set_path);
|
|
|
|
// Update path from URL after hydration (client-side redirect)
|
|
#[cfg(target_arch = "wasm32")]
|
|
{
|
|
use wasm_bindgen_futures::spawn_local;
|
|
spawn_local(async move {
|
|
if let Some(win) = web_sys::window() {
|
|
let current_path = win
|
|
.location()
|
|
.pathname()
|
|
.unwrap_or_else(|_| "/".to_string());
|
|
// If URL path is different from home, redirect to it
|
|
if current_path != "/" {
|
|
web_sys::console::log_1(
|
|
&format!("Client-side redirect to: {}", current_path).into(),
|
|
);
|
|
set_path.set(current_path);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
let (lang, _set_lang) = signal("en".to_string());
|
|
// --- Unit test placeholder for route matching ---
|
|
// #[cfg(test)]
|
|
// mod tests {
|
|
// use super::*;
|
|
// #[test]
|
|
// fn test_user_route() {
|
|
// let re = Regex::new(r"^/user/(\\d+)$").expect("Valid regex");
|
|
// assert!(re.is_match("/user/42"));
|
|
// }
|
|
// }
|
|
view! {
|
|
<GlobalStateProvider>
|
|
<ThemeProvider>
|
|
<I18nProvider>
|
|
<ToastProvider>
|
|
<AuthProvider>
|
|
<UserProvider>
|
|
<AppStateProvider>
|
|
<Title text="Welcome to Leptos"/>
|
|
<header class="absolute inset-x-0 top-2 z-90 mx-2">
|
|
<Wrapper><NavMenu set_path=set_path /></Wrapper>
|
|
</header>
|
|
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
|
|
<main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
{ let lang = lang.clone(); let path = path.clone();
|
|
move || {
|
|
let p = path.get();
|
|
let lang_val = lang.get();
|
|
let bundle = get_bundle(&lang_val).unwrap_or_else(|_| {
|
|
// Fallback to a simple bundle if loading fails
|
|
use fluent::FluentBundle;
|
|
use unic_langid::LanguageIdentifier;
|
|
let langid: LanguageIdentifier = "en".parse().unwrap_or_else(|e| {
|
|
web_sys::console::error_1(&format!("Failed to parse default language 'en': {:?}", e).into());
|
|
// This should never happen, but create a minimal fallback
|
|
LanguageIdentifier::from_parts(
|
|
unic_langid::subtags::Language::from_bytes(b"en").unwrap_or_else(|e| {
|
|
web_sys::console::error_1(&format!("Critical error: failed to create 'en' language: {:?}", e).into());
|
|
// Fallback to creating a new language identifier from scratch
|
|
match "en".parse::<unic_langid::subtags::Language>() {
|
|
Ok(lang) => lang,
|
|
Err(_) => {
|
|
// If even this fails, we'll use the default language
|
|
web_sys::console::error_1(&"Using default language as final fallback".into());
|
|
unic_langid::subtags::Language::default()
|
|
}
|
|
}
|
|
}),
|
|
None,
|
|
None,
|
|
&[],
|
|
)
|
|
});
|
|
FluentBundle::new(vec![langid])
|
|
});
|
|
let content = match p.as_str() {
|
|
"/" => t(&bundle, "main-desc", None),
|
|
"/about" => t(&bundle, "about-desc", None),
|
|
"/user" => "User Dashboard".to_string(),
|
|
"/daisyui" => "DaisyUI Components Demo".to_string(),
|
|
"/features-demo" => "New Features Demo".to_string(),
|
|
|
|
_ if p.starts_with("/user/") => {
|
|
if let Some(id) = p.strip_prefix("/user/") {
|
|
let mut args = HashMap::new();
|
|
args.insert("id", id);
|
|
t(&bundle, "user-page", Some(&args))
|
|
} else {
|
|
t(&bundle, "not-found", None)
|
|
}
|
|
},
|
|
_ => t(&bundle, "not-found", None),
|
|
};
|
|
view! {
|
|
<Wrapper>
|
|
<div>{content}</div>
|
|
{match p.as_str() {
|
|
"/" => view! { <div><HomePage /></div> }.into_any(),
|
|
"/about" => view! { <div><AboutPage /></div> }.into_any(),
|
|
"/user" => view! { <div><UserPage /></div> }.into_any(),
|
|
"/daisyui" => view! { <div><DaisyUIPage /></div> }.into_any(),
|
|
"/features-demo" => view! { <div><FeaturesDemoPage /></div> }.into_any(),
|
|
|
|
_ => view! { <div>Not found</div> }.into_any(),
|
|
}}
|
|
</Wrapper>
|
|
}
|
|
}}
|
|
</main>
|
|
</div>
|
|
</AppStateProvider>
|
|
</UserProvider>
|
|
</AuthProvider>
|
|
</ToastProvider>
|
|
</I18nProvider>
|
|
</ThemeProvider>
|
|
</GlobalStateProvider>
|
|
}
|
|
}
|
|
|
|
/// The SSR shell for Leptos/Axum integration.
|
|
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
|
shell_with_path(options, None)
|
|
}
|
|
|
|
/// The SSR shell for Leptos/Axum integration with path support.
|
|
pub fn shell_with_path(options: LeptosOptions, path: Option<String>) -> impl IntoView {
|
|
view! {
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
<AutoReload options=options.clone() />
|
|
<HydrationScripts options/>
|
|
<link rel="stylesheet" id="leptos" href="/public/website.css"/>
|
|
<link rel="shortcut icon" type="image/ico" href="/favicon.ico"/>
|
|
<MetaTags/>
|
|
</head>
|
|
<body>
|
|
<App _initial_path=path.unwrap_or_default() />
|
|
</body>
|
|
</html>
|
|
}
|
|
}
|