修复 admin 路由切换闪烁,全局替换原生导航为客户端路由
- 新增全局 UserContext,将用户认证状态提升到 App 级别缓存 - 将 /admin 和 /admin/write 改为嵌套路由,AdminLayout 作为共享父布局 - AdminLayout 使用 Outlet 渲染子页面,避免路由切换时重复挂载 - 修复所有原生 <a> 标签导致的整页刷新问题: - Header 导航栏 Logo 和 NavItem - 首页文章卡片、分页按钮 - 归档页文章条目 - 标签页标签云、文章卡片、标签链接 - 登录/注册页面链接 - Dashboard 快捷操作按钮 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5d523fbfc7
commit
61376f6ba9
@ -3,16 +3,36 @@ use dioxus::prelude::*;
|
|||||||
use crate::api::auth::{get_current_user, logout};
|
use crate::api::auth::{get_current_user, logout};
|
||||||
use crate::components::header::{Header, NavItemConfig};
|
use crate::components::header::{Header, NavItemConfig};
|
||||||
use crate::components::footer::Footer;
|
use crate::components::footer::Footer;
|
||||||
|
use crate::context::UserContext;
|
||||||
use crate::router::Route;
|
use crate::router::Route;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn AdminLayout(children: Element) -> Element {
|
pub fn AdminLayout() -> Element {
|
||||||
let user_resource =
|
let mut ctx: UserContext = use_context();
|
||||||
use_resource(|| async move { get_current_user().await.ok().and_then(|r| r.user) });
|
|
||||||
|
|
||||||
let navigator = dioxus::router::navigator();
|
let navigator = dioxus::router::navigator();
|
||||||
let route = use_route::<Route>();
|
let route = use_route::<Route>();
|
||||||
|
|
||||||
|
// 只在首次挂载时加载用户数据
|
||||||
|
use_effect(move || {
|
||||||
|
if !(ctx.checked)() {
|
||||||
|
(ctx.checked).set(true);
|
||||||
|
spawn(async move {
|
||||||
|
match get_current_user().await {
|
||||||
|
Ok(response) => {
|
||||||
|
if let Some(user) = response.user {
|
||||||
|
ctx.user.set(Some(std::sync::Arc::new(user)));
|
||||||
|
} else {
|
||||||
|
let _ = navigator.push("/login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let _ = navigator.push("/login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let admin_nav_items = vec![
|
let admin_nav_items = vec![
|
||||||
NavItemConfig {
|
NavItemConfig {
|
||||||
href: "/admin",
|
href: "/admin",
|
||||||
@ -31,12 +51,12 @@ pub fn AdminLayout(children: Element) -> Element {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let nav = navigator;
|
let nav = navigator.clone();
|
||||||
let logout_button = rsx! {
|
let logout_button = rsx! {
|
||||||
button {
|
button {
|
||||||
class: "text-sm text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] transition-colors",
|
class: "text-sm text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb] transition-colors",
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
let nav = nav;
|
let nav = nav.clone();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let _ = logout().await;
|
let _ = logout().await;
|
||||||
let _ = nav.push("/login");
|
let _ = nav.push("/login");
|
||||||
@ -46,29 +66,26 @@ pub fn AdminLayout(children: Element) -> Element {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_data = user_resource.read().clone();
|
match ((ctx.checked)(), (ctx.user)()) {
|
||||||
|
(true, Some(_)) => {
|
||||||
let should_redirect = matches!(user_data.as_ref(), Some(None));
|
|
||||||
|
|
||||||
use_effect(move || {
|
|
||||||
if should_redirect {
|
|
||||||
navigator.push("/login");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match user_data.as_ref() {
|
|
||||||
Some(Some(_user)) => {
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div { class: "min-h-screen flex flex-col bg-white dark:bg-[#1d1e20]",
|
div { class: "min-h-screen flex flex-col bg-white dark:bg-[#1d1e20]",
|
||||||
Header { nav_items: admin_nav_items, right_content: logout_button }
|
Header { nav_items: admin_nav_items, right_content: logout_button }
|
||||||
main { class: "flex-1 w-full max-w-5xl mx-auto px-6 py-8",
|
main { class: "flex-1 w-full max-w-5xl mx-auto px-6 py-8",
|
||||||
{children}
|
Outlet::<Route> {}
|
||||||
}
|
}
|
||||||
Footer {}
|
Footer {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
(true, None) => {
|
||||||
|
rsx! {
|
||||||
|
div { class: "min-h-screen flex items-center justify-center bg-white dark:bg-[#1d1e20]",
|
||||||
|
p { class: "text-gray-600 dark:text-[#9b9c9d]", "未登录,正在跳转..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(false, _) => {
|
||||||
rsx! {
|
rsx! {
|
||||||
div { class: "min-h-screen flex items-center justify-center bg-white dark:bg-[#1d1e20]",
|
div { class: "min-h-screen flex items-center justify-center bg-white dark:bg-[#1d1e20]",
|
||||||
p { class: "text-gray-600 dark:text-[#9b9c9d]", "加载中..." }
|
p { class: "text-gray-600 dark:text-[#9b9c9d]", "加载中..." }
|
||||||
|
|||||||
@ -15,6 +15,10 @@ pub fn Header(nav_items: Vec<NavItemConfig>, right_content: Element) -> Element
|
|||||||
a {
|
a {
|
||||||
class: "text-2xl font-bold text-gray-900 dark:text-[#dadadb] hover:opacity-80 transition-opacity",
|
class: "text-2xl font-bold text-gray-900 dark:text-[#dadadb] hover:opacity-80 transition-opacity",
|
||||||
href: "/",
|
href: "/",
|
||||||
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push("/");
|
||||||
|
},
|
||||||
"Yggdrasil"
|
"Yggdrasil"
|
||||||
}
|
}
|
||||||
div { class: "flex items-center gap-2",
|
div { class: "flex items-center gap-2",
|
||||||
@ -43,9 +47,18 @@ fn NavItem(href: &'static str, label: &'static str, is_active: bool) -> Element
|
|||||||
format!("{} text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb]", base_class)
|
format!("{} text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb]", base_class)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let href = href;
|
||||||
rsx! {
|
rsx! {
|
||||||
li {
|
li {
|
||||||
a { class: "{class_str}", href: "{href}", "{label}" }
|
a {
|
||||||
|
class: "{class_str}",
|
||||||
|
href: "{href}",
|
||||||
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(href);
|
||||||
|
},
|
||||||
|
"{label}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/context.rs
Normal file
10
src/context.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::models::user::User;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct UserContext {
|
||||||
|
pub user: Signal<Option<Arc<User>>>,
|
||||||
|
pub checked: Signal<bool>,
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
mod api;
|
mod api;
|
||||||
mod auth;
|
mod auth;
|
||||||
mod components;
|
mod components;
|
||||||
|
mod context;
|
||||||
mod db;
|
mod db;
|
||||||
mod models;
|
mod models;
|
||||||
mod pages;
|
mod pages;
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use crate::components::admin_layout::AdminLayout;
|
|
||||||
use crate::pages::home::{Post, POSTS};
|
use crate::pages::home::{Post, POSTS};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn AdminPage() -> Element {
|
pub fn AdminPage() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
AdminLayout {
|
|
||||||
div { class: "space-y-8",
|
div { class: "space-y-8",
|
||||||
// 统计卡片
|
// 统计卡片
|
||||||
div { class: "grid grid-cols-1 md:grid-cols-3 gap-6",
|
div { class: "grid grid-cols-1 md:grid-cols-3 gap-6",
|
||||||
@ -17,9 +15,11 @@ pub fn AdminPage() -> Element {
|
|||||||
|
|
||||||
// 快捷操作
|
// 快捷操作
|
||||||
div { class: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
div { class: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
||||||
a {
|
button {
|
||||||
class: "bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full px-6 py-3 text-center font-medium hover:opacity-80 transition-opacity",
|
class: "bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full px-6 py-3 text-center font-medium hover:opacity-80 transition-opacity cursor-pointer",
|
||||||
href: "/admin/write",
|
onclick: move |_| {
|
||||||
|
dioxus::router::navigator().push("/admin/write");
|
||||||
|
},
|
||||||
"写文章"
|
"写文章"
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
@ -45,7 +45,6 @@ pub fn AdminPage() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use crate::components::admin_layout::AdminLayout;
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn WritePage() -> Element {
|
pub fn WritePage() -> Element {
|
||||||
let mut title = use_signal(|| "".to_string());
|
let mut title = use_signal(|| "".to_string());
|
||||||
@ -36,7 +34,6 @@ pub fn WritePage() -> Element {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
AdminLayout {
|
|
||||||
div { class: "space-y-4",
|
div { class: "space-y-4",
|
||||||
// 标题输入
|
// 标题输入
|
||||||
input {
|
input {
|
||||||
@ -76,5 +73,4 @@ pub fn WritePage() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,6 +211,10 @@ fn ArchiveEntry(post: Post) -> Element {
|
|||||||
class: "entry-link absolute inset-0 z-10",
|
class: "entry-link absolute inset-0 z-10",
|
||||||
aria_label: "post link to {post.title}",
|
aria_label: "post link to {post.title}",
|
||||||
href: "/post/{post.slug}",
|
href: "/post/{post.slug}",
|
||||||
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(format!("/post/{}", post.slug).as_str());
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,9 +103,16 @@ fn HomeInfo() -> Element {
|
|||||||
fn PostEntry(post: Post) -> Element {
|
fn PostEntry(post: Post) -> Element {
|
||||||
let tag_items = post.tags.to_vec();
|
let tag_items = post.tags.to_vec();
|
||||||
|
|
||||||
|
let post_slug = post.slug;
|
||||||
rsx! {
|
rsx! {
|
||||||
article { class: "relative mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] hover:-translate-y-0.5 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-250",
|
article { class: "relative mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] hover:-translate-y-0.5 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-250",
|
||||||
a { class: "block group", href: "/post/{post.slug}",
|
a {
|
||||||
|
class: "block group",
|
||||||
|
href: "/post/{post_slug}",
|
||||||
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(format!("/post/{}", post_slug).as_str());
|
||||||
|
},
|
||||||
h2 { class: "text-2xl font-bold leading-tight text-gray-900 dark:text-[#dadadb] group-hover:opacity-80 transition-opacity",
|
h2 { class: "text-2xl font-bold leading-tight text-gray-900 dark:text-[#dadadb] group-hover:opacity-80 transition-opacity",
|
||||||
"{post.title}"
|
"{post.title}"
|
||||||
}
|
}
|
||||||
@ -131,9 +138,9 @@ fn PostEntry(post: Post) -> Element {
|
|||||||
fn Pagination() -> Element {
|
fn Pagination() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
nav { class: "flex mt-10 mb-6",
|
nav { class: "flex mt-10 mb-6",
|
||||||
a {
|
button {
|
||||||
class: "ml-auto inline-flex items-center px-4 py-2 text-sm text-white bg-gray-900 dark:bg-[#dadadb] dark:text-gray-900 rounded-full hover:opacity-80 transition-opacity",
|
class: "ml-auto inline-flex items-center px-4 py-2 text-sm text-white bg-gray-900 dark:bg-[#dadadb] dark:text-gray-900 rounded-full hover:opacity-80 transition-opacity cursor-pointer",
|
||||||
href: "/page/2",
|
onclick: move |_| { dioxus::router::navigator().push("/page/2"); },
|
||||||
"下一页"
|
"下一页"
|
||||||
span { class: "ml-1", "»" }
|
span { class: "ml-1", "»" }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,9 +89,9 @@ pub fn LoginPage() -> Element {
|
|||||||
onclick: move |_| on_submit(()),
|
onclick: move |_| on_submit(()),
|
||||||
"登录"
|
"登录"
|
||||||
}
|
}
|
||||||
a {
|
button {
|
||||||
class: "block w-full py-2 px-4 text-center text-gray-500 dark:text-[#9b9c9d] hover:text-gray-700 dark:hover:text-[#dadadb] font-medium rounded-lg transition-colors",
|
class: "block w-full py-2 px-4 text-center text-gray-500 dark:text-[#9b9c9d] hover:text-gray-700 dark:hover:text-[#dadadb] font-medium rounded-lg transition-colors cursor-pointer",
|
||||||
href: "/register",
|
onclick: move |_| { dioxus::router::navigator().push("/register"); },
|
||||||
"还没有账号?去注册"
|
"还没有账号?去注册"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,8 @@ pub fn RegisterPage() -> Element {
|
|||||||
if success() {
|
if success() {
|
||||||
div { class: "mb-4 p-3 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 rounded-lg text-center",
|
div { class: "mb-4 p-3 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 rounded-lg text-center",
|
||||||
"注册成功!"
|
"注册成功!"
|
||||||
a { class: "block mt-2 text-gray-700 dark:text-[#dadadb] hover:underline", href: "/login",
|
button { class: "block mt-2 text-gray-700 dark:text-[#dadadb] hover:underline cursor-pointer",
|
||||||
|
onclick: move |_| { dioxus::router::navigator().push("/login"); },
|
||||||
"去登录"
|
"去登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +126,8 @@ pub fn RegisterPage() -> Element {
|
|||||||
}
|
}
|
||||||
p { class: "mt-4 text-center text-sm text-gray-500 dark:text-[#9b9c9d]",
|
p { class: "mt-4 text-center text-sm text-gray-500 dark:text-[#9b9c9d]",
|
||||||
"已有账号?"
|
"已有账号?"
|
||||||
a { class: "text-gray-700 dark:text-[#dadadb] hover:underline", href: "/login",
|
button { class: "text-gray-700 dark:text-[#dadadb] hover:underline cursor-pointer",
|
||||||
|
onclick: move |_| { dioxus::router::navigator().push("/login"); },
|
||||||
"去登录"
|
"去登录"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,13 +70,17 @@ pub fn TagsPage() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ul { class: "flex flex-wrap gap-4 mt-6",
|
ul { class: "flex flex-wrap gap-4 mt-6",
|
||||||
for tag in tags.iter() {
|
for (name, count) in tags.into_iter().map(|t| (t.name, t.count)) {
|
||||||
li {
|
li {
|
||||||
a {
|
a {
|
||||||
class: "inline-flex items-center px-3 py-1.5 text-base font-medium bg-gray-100 dark:bg-[#2e2e33] text-gray-700 dark:text-[#9b9c9d] rounded-lg hover:bg-gray-200 dark:hover:bg-[#333] transition-colors",
|
class: "inline-flex items-center px-3 py-1.5 text-base font-medium bg-gray-100 dark:bg-[#2e2e33] text-gray-700 dark:text-[#9b9c9d] rounded-lg hover:bg-gray-200 dark:hover:bg-[#333] transition-colors",
|
||||||
href: "/tags/{tag.name}",
|
href: "/tags/{name}",
|
||||||
"{tag.name}"
|
onclick: move |evt| {
|
||||||
sup { class: "ml-1 text-sm text-gray-500 dark:text-[#9b9c9d]", "{tag.count}" }
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(format!("/tags/{}", name).as_str());
|
||||||
|
},
|
||||||
|
"{name}"
|
||||||
|
sup { class: "ml-1 text-sm text-gray-500 dark:text-[#9b9c9d]", "{count}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,10 +130,17 @@ pub fn TagDetailPage(tag: String) -> Element {
|
|||||||
#[component]
|
#[component]
|
||||||
fn TagPostEntry(post: Post) -> Element {
|
fn TagPostEntry(post: Post) -> Element {
|
||||||
let tag_items = post.tags.to_vec();
|
let tag_items = post.tags.to_vec();
|
||||||
|
let post_slug = post.slug;
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
article { class: "relative mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] hover:-translate-y-0.5 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-250",
|
article { class: "relative mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] hover:-translate-y-0.5 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-250",
|
||||||
a { class: "block group", href: "/post/{post.slug}",
|
a {
|
||||||
|
class: "block group",
|
||||||
|
href: "/post/{post_slug}",
|
||||||
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(format!("/post/{}", post_slug).as_str());
|
||||||
|
},
|
||||||
h2 { class: "text-2xl font-bold leading-tight text-gray-900 dark:text-[#dadadb] group-hover:opacity-80 transition-opacity",
|
h2 { class: "text-2xl font-bold leading-tight text-gray-900 dark:text-[#dadadb] group-hover:opacity-80 transition-opacity",
|
||||||
"{post.title}"
|
"{post.title}"
|
||||||
}
|
}
|
||||||
@ -139,15 +150,19 @@ fn TagPostEntry(post: Post) -> Element {
|
|||||||
div { class: "mt-3 flex items-center gap-3 text-[13px] text-gray-400 dark:text-[#9b9c9d]",
|
div { class: "mt-3 flex items-center gap-3 text-[13px] text-gray-400 dark:text-[#9b9c9d]",
|
||||||
span { "{post.date}" }
|
span { "{post.date}" }
|
||||||
span { "·" }
|
span { "·" }
|
||||||
for (i, t) in tag_items.iter().enumerate() {
|
for (i, tag_name) in tag_items.into_iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
span { "," }
|
span { "," }
|
||||||
}
|
}
|
||||||
span {
|
span {
|
||||||
a {
|
a {
|
||||||
class: "hover:text-gray-600 dark:hover:text-[#dadadb] transition-colors",
|
class: "hover:text-gray-600 dark:hover:text-[#dadadb] transition-colors",
|
||||||
href: "/tags/{t}",
|
href: "/tags/{tag_name}",
|
||||||
"{t}"
|
onclick: move |evt| {
|
||||||
|
evt.prevent_default();
|
||||||
|
dioxus::router::navigator().push(format!("/tags/{}", tag_name).as_str());
|
||||||
|
},
|
||||||
|
"{tag_name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::components::admin_layout::AdminLayout;
|
||||||
|
use crate::context::UserContext;
|
||||||
use crate::pages::admin::{AdminPage, WritePage};
|
use crate::pages::admin::{AdminPage, WritePage};
|
||||||
use crate::pages::archives::ArchivesPage;
|
use crate::pages::archives::ArchivesPage;
|
||||||
use crate::pages::home::HomePage;
|
use crate::pages::home::HomePage;
|
||||||
@ -9,6 +12,7 @@ use crate::pages::tags::{TagsPage, TagDetailPage};
|
|||||||
use crate::theme::{Theme, ThemePreload, use_theme_provider};
|
use crate::theme::{Theme, ThemePreload, use_theme_provider};
|
||||||
|
|
||||||
#[derive(Clone, Routable, Debug, PartialEq)]
|
#[derive(Clone, Routable, Debug, PartialEq)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Route {
|
pub enum Route {
|
||||||
#[route("/")]
|
#[route("/")]
|
||||||
HomePage {},
|
HomePage {},
|
||||||
@ -16,10 +20,16 @@ pub enum Route {
|
|||||||
LoginPage {},
|
LoginPage {},
|
||||||
#[route("/register")]
|
#[route("/register")]
|
||||||
RegisterPage {},
|
RegisterPage {},
|
||||||
#[route("/admin")]
|
|
||||||
|
#[nest("/admin")]
|
||||||
|
#[layout(AdminLayout)]
|
||||||
|
#[route("/")]
|
||||||
AdminPage {},
|
AdminPage {},
|
||||||
#[route("/admin/write")]
|
#[route("/write")]
|
||||||
WritePage {},
|
WritePage {},
|
||||||
|
#[end_layout]
|
||||||
|
#[end_nest]
|
||||||
|
|
||||||
#[route("/archives")]
|
#[route("/archives")]
|
||||||
ArchivesPage {},
|
ArchivesPage {},
|
||||||
#[route("/tags")]
|
#[route("/tags")]
|
||||||
@ -40,6 +50,10 @@ pub fn AppRouter() -> Element {
|
|||||||
Theme::Light => "",
|
Theme::Light => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let user = use_signal(|| None::<Arc<crate::models::user::User>>);
|
||||||
|
let checked = use_signal(|| false);
|
||||||
|
use_context_provider(|| UserContext { user, checked });
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "{theme_class}",
|
class: "{theme_class}",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user