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])
}