fix: centralize sidenav state in store and resolve all component property issues
Some checks failed
Build MIPS Binary / build (push) Failing after 45s

This commit is contained in:
spinline
2026-02-14 01:45:13 +03:00
parent df41e12413
commit 9018bce348
3 changed files with 63 additions and 25 deletions

View File

@@ -1,23 +1,20 @@
use leptos::prelude::*; use leptos::prelude::*;
use crate::components::layout::sidebar::Sidebar; use crate::components::layout::sidebar::Sidebar;
use crate::components::layout::toolbar::Toolbar; use crate::components::layout::toolbar::Toolbar;
use crate::components::ui::sidenav::{SidenavWrapper, Sidenav, SidenavInset, SidenavState, SidenavCollapsible}; use crate::components::ui::sidenav::{SidenavWrapper, Sidenav, SidenavInset};
#[component] #[component]
pub fn ProtectedLayout(children: Children) -> impl IntoView { pub fn ProtectedLayout(children: Children) -> impl IntoView {
let sidenav_state = RwSignal::new(SidenavState::Expanded); let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
view! { view! {
<SidenavWrapper attr:style="--sidenav-width:16rem; --sidenav-width-icon:3.5rem;"> <SidenavWrapper attr:style="--sidenav-width:16rem; --sidenav-width-icon:4rem;">
<Sidenav <Sidenav data_state=Signal::from(store.sidenav_state)>
data_state=Signal::from(sidenav_state)
data_collapsible=SidenavCollapsible::Icon
>
<Sidebar /> <Sidebar />
</Sidenav> </Sidenav>
<SidenavInset class="flex flex-col h-screen overflow-hidden"> <SidenavInset class="flex flex-col h-screen overflow-hidden">
<Toolbar sidenav_state=sidenav_state /> <Toolbar sidenav_state=store.sidenav_state />
<main class="flex-1 overflow-auto bg-muted/30"> <main class="flex-1 overflow-auto bg-muted/30">
{children()} {children()}
</main> </main>

View File

@@ -109,9 +109,48 @@ pub fn Sidebar() -> impl IntoView {
icon="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" icon="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
label="Tümü" label="Tümü"
count=Signal::derive(total_count) count=Signal::derive(total_count)
is_collapsed=is_collapsed.into() is_collapsed=is_collapsed
/>
<SidebarItem
active=Signal::derive(move || is_active(crate::store::FilterStatus::Downloading))
on_click=move |_| set_filter(crate::store::FilterStatus::Downloading)
icon="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3"
label="İndirilenler"
count=Signal::derive(downloading_count)
is_collapsed=is_collapsed
/>
<SidebarItem
active=Signal::derive(move || is_active(crate::store::FilterStatus::Seeding))
on_click=move |_| set_filter(crate::store::FilterStatus::Seeding)
icon="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"
label="Gönderilenler"
count=Signal::derive(seeding_count)
is_collapsed=is_collapsed
/>
<SidebarItem
active=Signal::derive(move || is_active(crate::store::FilterStatus::Completed))
on_click=move |_| set_filter(crate::store::FilterStatus::Completed)
icon="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
label="Tamamlananlar"
count=Signal::derive(completed_count)
is_collapsed=is_collapsed
/>
<SidebarItem
active=Signal::derive(move || is_active(crate::store::FilterStatus::Paused))
on_click=move |_| set_filter(crate::store::FilterStatus::Paused)
icon="M15.75 5.25v13.5m-7.5-13.5v13.5"
label="Durdurulanlar"
count=Signal::derive(paused_count)
is_collapsed=is_collapsed
/>
<SidebarItem
active=Signal::derive(move || is_active(crate::store::FilterStatus::Inactive))
on_click=move |_| set_filter(crate::store::FilterStatus::Inactive)
icon="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"
label="Pasif"
count=Signal::derive(inactive_count)
is_collapsed=is_collapsed
/> />
// ... (aynı mantık diğer itemler için de geçerli olacak)
</SidenavMenu> </SidenavMenu>
</SidenavGroupContent> </SidenavGroupContent>
</SidenavGroup> </SidenavGroup>
@@ -119,7 +158,6 @@ pub fn Sidebar() -> impl IntoView {
<SidenavFooter> <SidenavFooter>
<div class="flex flex-col gap-4 p-2"> <div class="flex flex-col gap-4 p-2">
// Push Toggle - Hide text when collapsed
<div class="flex items-center justify-between px-2 py-1 bg-muted/20 rounded-md border border-border/50"> <div class="flex items-center justify-between px-2 py-1 bg-muted/20 rounded-md border border-border/50">
<div class="flex flex-col gap-0.5" class:hidden=is_collapsed> <div class="flex flex-col gap-0.5" class:hidden=is_collapsed>
<span class="text-[10px] font-bold uppercase tracking-wider text-foreground/70">"Bildirimler"</span> <span class="text-[10px] font-bold uppercase tracking-wider text-foreground/70">"Bildirimler"</span>
@@ -169,15 +207,18 @@ pub fn Sidebar() -> impl IntoView {
} }
#[component] #[component]
fn SidebarItem( fn SidebarItem<F>(
active: Signal<bool>, active: Signal<bool>,
on_click: impl Fn(web_sys::MouseEvent) + 'static + Send, on_click: F,
#[prop(into)] icon: String, #[prop(into)] icon: String,
#[prop(into)] label: &'static str, #[prop(into)] label: &'static str,
count: Signal<usize>, count: Signal<usize>,
) -> impl IntoView { #[prop(into)] is_collapsed: Signal<bool>,
) -> impl IntoView
where F: Fn(web_sys::MouseEvent) + 'static + Send
{
let variant = move || if active.get() { SidenavMenuButtonVariant::Outline } else { SidenavMenuButtonVariant::Default }; let variant = move || if active.get() { SidenavMenuButtonVariant::Outline } else { SidenavMenuButtonVariant::Default };
let class = move || if active.get() { "bg-accent/50 border-accent text-foreground".to_string() } else { "text-muted-foreground hover:text-foreground".to_string() }; let class = move || if active.get() { "bg-accent/50 border-accent text-foreground font-bold".to_string() } else { "text-muted-foreground hover:text-foreground".to_string() };
view! { view! {
<SidenavMenuItem> <SidenavMenuItem>
@@ -189,8 +230,8 @@ fn SidebarItem(
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 shrink-0"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d=icon.clone() /> <path stroke-linecap="round" stroke-linejoin="round" d=icon.clone() />
</svg> </svg>
<span class="flex-1 truncate">{label}</span> <span class="flex-1 truncate" class:hidden=is_collapsed>{label}</span>
<span class="text-[10px] font-mono opacity-50">{count}</span> <span class="text-[10px] font-mono opacity-50" class:hidden=is_collapsed>{count}</span>
</SidenavMenuButton> </SidenavMenuButton>
</SidenavMenuItem> </SidenavMenuItem>
} }

View File

@@ -10,6 +10,7 @@ use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use crate::components::ui::toast::{ToastType, toast}; use crate::components::ui::toast::{ToastType, toast};
use crate::components::ui::sidenav::SidenavState;
pub fn show_toast(level: NotificationLevel, message: impl Into<String>) { pub fn show_toast(level: NotificationLevel, message: impl Into<String>) {
let msg = message.into(); let msg = message.into();
@@ -55,6 +56,7 @@ pub struct TorrentStore {
pub user: RwSignal<Option<String>>, pub user: RwSignal<Option<String>>,
pub selected_torrent: RwSignal<Option<String>>, pub selected_torrent: RwSignal<Option<String>>,
pub push_enabled: RwSignal<bool>, pub push_enabled: RwSignal<bool>,
pub sidenav_state: RwSignal<SidenavState>,
} }
pub fn provide_torrent_store() { pub fn provide_torrent_store() {
@@ -65,10 +67,11 @@ pub fn provide_torrent_store() {
let user = RwSignal::new(Option::<String>::None); let user = RwSignal::new(Option::<String>::None);
let selected_torrent = RwSignal::new(Option::<String>::None); let selected_torrent = RwSignal::new(Option::<String>::None);
let push_enabled = RwSignal::new(false); let push_enabled = RwSignal::new(false);
let sidenav_state = RwSignal::new(SidenavState::Expanded);
let show_browser_notification = crate::utils::notification::use_app_notification(); let show_browser_notification = crate::utils::notification::use_app_notification();
let store = TorrentStore { torrents, filter, search_query, global_stats, user, selected_torrent, push_enabled }; let store = TorrentStore { torrents, filter, search_query, global_stats, user, selected_torrent, push_enabled, sidenav_state };
provide_context(store); provide_context(store);
// Initial check for push status // Initial check for push status
@@ -134,7 +137,7 @@ pub fn provide_torrent_store() {
} }
if was_connected && !disconnect_notified { if was_connected && !disconnect_notified {
show_toast(NotificationLevel::Warning, "Sunucu bağlantısı kesildi, yeniden bağlanılıyor..."); show_toast(NotificationLevel::Warning, "Sunucu bağlantısı kesildi, yeniden bağlanılıyor...");
disconnect_notified = true; disconnect_notified = false;
} }
} }
} }
@@ -153,8 +156,7 @@ pub fn provide_torrent_store() {
pub async fn is_push_subscribed() -> Result<bool, String> { pub async fn is_push_subscribed() -> Result<bool, String> {
let window = web_sys::window().ok_or("no window")?; let window = web_sys::window().ok_or("no window")?;
let navigator = window.navigator(); let sw_container = window.navigator().service_worker();
let sw_container = navigator.service_worker();
let registration = wasm_bindgen_futures::JsFuture::from(sw_container.ready().map_err(|e| format!("{:?}", e))?) let registration = wasm_bindgen_futures::JsFuture::from(sw_container.ready().map_err(|e| format!("{:?}", e))?)
.await .await
@@ -172,8 +174,7 @@ pub async fn is_push_subscribed() -> Result<bool, String> {
pub async fn subscribe_to_push_notifications() { pub async fn subscribe_to_push_notifications() {
let window = web_sys::window().expect("no window"); let window = web_sys::window().expect("no window");
let navigator = window.navigator(); let sw_container = window.navigator().service_worker();
let sw_container = navigator.service_worker();
let registration = match wasm_bindgen_futures::JsFuture::from(sw_container.ready().expect("sw not ready")).await { let registration = match wasm_bindgen_futures::JsFuture::from(sw_container.ready().expect("sw not ready")).await {
Ok(reg) => reg.dyn_into::<web_sys::ServiceWorkerRegistration>().expect("not a reg"), Ok(reg) => reg.dyn_into::<web_sys::ServiceWorkerRegistration>().expect("not a reg"),
@@ -181,8 +182,7 @@ pub async fn subscribe_to_push_notifications() {
}; };
// 1. Get Public Key from Backend // 1. Get Public Key from Backend
let public_key_res: Result<String, _> = shared::server_fns::push::get_public_key().await; let public_key = match shared::server_fns::push::get_public_key().await {
let public_key = match public_key_res {
Ok(key) => key, Ok(key) => key,
Err(e) => { log::error!("Failed to get public key: {:?}", e); return; } Err(e) => { log::error!("Failed to get public key: {:?}", e); return; }
}; };