at main 150 lines 4.6 kB view raw
1//! Custom domain Dioxus application. 2//! 3//! Separate router for custom domain hosting (e.g., myblog.com). 4//! Uses site.standard.publication documents with path-based routing. 5 6use dioxus::prelude::*; 7use jacquard::smol_str::SmolStr; 8use jacquard::types::string::AtIdentifier; 9 10use crate::components::identity::RepositoryIndex; 11use crate::host_mode::CustomDomainContext; 12 13/// Custom domain route enum - path-based routing for publications. 14#[derive(Debug, Clone, Routable, PartialEq)] 15#[rustfmt::skip] 16pub enum CustomDomainRoute { 17 #[layout(CustomDomainLayout)] 18 /// Root/landing page. 19 #[route("/")] 20 Root {}, 21 /// Explicit index page. 22 #[route("/index")] 23 Index {}, 24 /// Entry by rkey (direct lookup). 25 #[route("/e/:rkey")] 26 EntryByRkey { rkey: SmolStr }, 27 /// Entry edit by rkey. 28 #[route("/e/:rkey/edit")] 29 EntryEdit { rkey: SmolStr }, 30 /// Profile/repository view. 31 #[route("/u/:ident")] 32 Profile { ident: AtIdentifier<'static> }, 33 /// Path-based document (catch-all, must be last). 34 #[route("/*segments")] 35 PathPage { segments: Vec<String> }, 36} 37 38/// Root component for custom domain app. 39#[component] 40pub fn CustomDomainApp() -> Element { 41 rsx! { 42 document::Link { rel: "icon", href: crate::FAVICON } 43 document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" } 44 document::Link { rel: "preconnect", href: "https://fonts.gstatic.com" } 45 document::Link { rel: "stylesheet", href: crate::THEME_DEFAULTS_CSS } 46 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" } 47 document::Link { rel: "stylesheet", href: crate::MAIN_CSS } 48 crate::components::toast::ToastProvider { 49 Router::<CustomDomainRoute> {} 50 } 51 } 52} 53 54#[component] 55fn CustomDomainLayout() -> Element { 56 let ctx = use_context::<CustomDomainContext>(); 57 rsx! { 58 nav { class: "custom-domain-nav", 59 a { href: "/", "{ctx.publication_name}" } 60 } 61 main { Outlet::<CustomDomainRoute> {} } 62 } 63} 64 65#[component] 66fn Root() -> Element { 67 rsx! { Index {} } 68} 69 70#[component] 71fn Index() -> Element { 72 let ctx = use_context::<CustomDomainContext>(); 73 74 rsx! { 75 crate::views::NotebookIndex { 76 ident: ctx.owner.clone(), 77 book_title: ctx.publication_name.clone(), 78 } 79 } 80} 81 82#[component] 83fn EntryByRkey(rkey: ReadSignal<SmolStr>) -> Element { 84 let ctx = use_context::<CustomDomainContext>(); 85 86 rsx! { 87 crate::views::NotebookEntryByRkey { 88 ident: ctx.owner.clone(), 89 book_title: ctx.publication_name.clone(), 90 rkey: rkey, 91 } 92 } 93} 94 95#[component] 96fn EntryEdit(rkey: ReadSignal<SmolStr>) -> Element { 97 let ctx = use_context::<CustomDomainContext>(); 98 99 rsx! { 100 crate::views::NotebookEntryEdit { 101 ident: ctx.owner.clone(), 102 book_title: ctx.publication_name.clone(), 103 rkey: rkey, 104 } 105 } 106} 107 108#[component] 109fn Profile(ident: AtIdentifier<'static>) -> Element { 110 rsx! { 111 RepositoryIndex { ident } 112 } 113} 114 115#[component] 116fn PathPage(segments: ReadSignal<Vec<String>>) -> Element { 117 let ctx = use_context::<CustomDomainContext>(); 118 119 let ident = use_memo(move || ctx.owner.clone()); 120 let rkey = use_memo(move || ctx.publication_rkey.clone()); 121 let path = use_memo(move || format!("/{}", segments().join("/"))); 122 123 let (doc_res, doc_data) = 124 crate::data::use_custom_domain_document_data(ident.into(), rkey.into(), path.into()); 125 126 #[cfg(feature = "fullstack-server")] 127 let _doc_res = doc_res?; 128 129 #[cfg(not(feature = "fullstack-server"))] 130 let _ = doc_res; 131 132 match &*doc_data.read() { 133 Some(data) => { 134 let title = data.document.title.as_ref(); 135 136 rsx! { 137 article { class: "document-content", 138 h1 { "{title}" } 139 div { 140 class: "rendered-content", 141 dangerous_inner_html: "{data.rendered_html}" 142 } 143 } 144 } 145 } 146 None => rsx! { 147 div { class: "loading", "Loading..." } 148 }, 149 } 150}