use leptos::prelude::*; use crate::components::ui::table::*; use crate::components::ui::badge::*; use crate::components::ui::shimmer::*; use crate::components::ui::context_menu::*; use shared::TorrentFile; #[component] pub fn TorrentFilesTab(hash: String) -> impl IntoView { let hash_clone = hash.clone(); // Fetch files resource let files_resource = Resource::new( move || hash_clone.clone(), |h| async move { shared::server_fns::torrent::get_files(h).await.unwrap_or_default() } ); // Callback to trigger a refetch — safe, doesn't destroy existing components let on_refresh = Callback::new(move |_: ()| { files_resource.refetch(); }); let stored_hash = StoredValue::new(hash); view! { }> {move || { let files = files_resource.get().unwrap_or_default(); if files.is_empty() { return view! {

"Bu torrent için dosya bulunamadı."

}.into_any(); } let files_len = files.len(); view! {
"#" "Dosya Adı" "Boyut" "Tamamlanan" "Öncelik" } }} />
{format!("Toplam {} dosya", files_len)}
}.into_any() }}
} } #[component] fn FileRow(file: TorrentFile, hash: String, on_refresh: Callback<()>) -> impl IntoView { let f_idx = file.index; let path_clone = file.path.clone(); // on_refresh is called AFTER the server responds, not before let on_refresh_stored = StoredValue::new(on_refresh); let set_priority = Action::new(move |req: &(String, u32, u8)| { let (h, idx, p) = req.clone(); async move { let res = shared::server_fns::torrent::set_file_priority(h, idx, p).await; if let Err(e) = &res { crate::store::show_toast(shared::NotificationLevel::Error, format!("Öncelik değiştirilemedi: {:?}", e)); } else { crate::store::show_toast(shared::NotificationLevel::Success, "Dosya önceliği güncellendi.".to_string()); // Refetch AFTER the server has saved the priority on_refresh_stored.get_value().run(()); } res } }); view! { {file.index} {file.path.clone()} {format_bytes(file.size)} {format_bytes(file.completed_chunks)} { let (variant, label) = match file.priority { 0 => (BadgeVariant::Destructive, "İndirme"), 2 => (BadgeVariant::Success, "Yüksek"), _ => (BadgeVariant::Secondary, "Normal"), }; view! { {label} } } } } #[component] fn FileContextMenu( children: Children, torrent_hash: String, file_index: u32, set_priority: Action<(String, u32, u8), Result<(), ServerFnError>>, ) -> impl IntoView { let hash_c1 = torrent_hash.clone(); let hash_c2 = torrent_hash.clone(); let hash_c3 = torrent_hash.clone(); view! { {children()} "Dosya Önceliği" "Yüksek" "Normal" "İndirme (Kapalı)" } } #[component] fn FilesFallback() -> impl IntoView { view! {
} } fn format_bytes(bytes: i64) -> String { const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"]; if bytes < 1024 { return format!("{} B", bytes); } let i = (bytes as f64).log2().div_euclid(10.0) as usize; format!("{:.1} {}", (bytes as f64) / 1024_f64.powi(i as i32), UNITS[i]) }