//! Custom domain Dioxus application. //! //! Separate router for custom domain hosting (e.g., myblog.com). //! Uses site.standard.publication documents with path-based routing. use dioxus::prelude::*; use jacquard::smol_str::SmolStr; use jacquard::types::string::AtIdentifier; use crate::components::identity::RepositoryIndex; use crate::host_mode::CustomDomainContext; /// Custom domain route enum - path-based routing for publications. #[derive(Debug, Clone, Routable, PartialEq)] #[rustfmt::skip] pub enum CustomDomainRoute { #[layout(CustomDomainLayout)] /// Root/landing page. #[route("/")] Root {}, /// Explicit index page. #[route("/index")] Index {}, /// Entry by rkey (direct lookup). #[route("/e/:rkey")] EntryByRkey { rkey: SmolStr }, /// Entry edit by rkey. #[route("/e/:rkey/edit")] EntryEdit { rkey: SmolStr }, /// Profile/repository view. #[route("/u/:ident")] Profile { ident: AtIdentifier<'static> }, /// Path-based document (catch-all, must be last). #[route("/*segments")] PathPage { segments: Vec }, } /// Root component for custom domain app. #[component] pub fn CustomDomainApp() -> Element { rsx! { document::Link { rel: "icon", href: crate::FAVICON } document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" } document::Link { rel: "preconnect", href: "https://fonts.gstatic.com" } document::Link { rel: "stylesheet", href: crate::THEME_DEFAULTS_CSS } document::Link { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&family=IBM+Plex+Serif:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&display=swap" } document::Link { rel: "stylesheet", href: crate::MAIN_CSS } crate::components::toast::ToastProvider { Router:: {} } } } #[component] fn CustomDomainLayout() -> Element { let ctx = use_context::(); rsx! { nav { class: "custom-domain-nav", a { href: "/", "{ctx.publication_name}" } } main { Outlet:: {} } } } #[component] fn Root() -> Element { rsx! { Index {} } } #[component] fn Index() -> Element { let ctx = use_context::(); rsx! { crate::views::NotebookIndex { ident: ctx.owner.clone(), book_title: ctx.publication_name.clone(), } } } #[component] fn EntryByRkey(rkey: ReadSignal) -> Element { let ctx = use_context::(); rsx! { crate::views::NotebookEntryByRkey { ident: ctx.owner.clone(), book_title: ctx.publication_name.clone(), rkey: rkey, } } } #[component] fn EntryEdit(rkey: ReadSignal) -> Element { let ctx = use_context::(); rsx! { crate::views::NotebookEntryEdit { ident: ctx.owner.clone(), book_title: ctx.publication_name.clone(), rkey: rkey, } } } #[component] fn Profile(ident: AtIdentifier<'static>) -> Element { rsx! { RepositoryIndex { ident } } } #[component] fn PathPage(segments: ReadSignal>) -> Element { let ctx = use_context::(); let ident = use_memo(move || ctx.owner.clone()); let rkey = use_memo(move || ctx.publication_rkey.clone()); let path = use_memo(move || format!("/{}", segments().join("/"))); let (doc_res, doc_data) = crate::data::use_custom_domain_document_data(ident.into(), rkey.into(), path.into()); #[cfg(feature = "fullstack-server")] let _doc_res = doc_res?; #[cfg(not(feature = "fullstack-server"))] let _ = doc_res; match &*doc_data.read() { Some(data) => { let title = data.document.title.as_ref(); rsx! { article { class: "document-content", h1 { "{title}" } div { class: "rendered-content", dangerous_inner_html: "{data.rendered_html}" } } } } None => rsx! { div { class: "loading", "Loading..." } }, } }