use dioxus::prelude::*;
use crate::utils::media_helpers::file_icon_for_mime;
use crate::utils::room_helpers::format_file_size;
#[derive(Clone, Debug, PartialEq)]
pub enum DownloadState {
Idle,
Downloading { bytes_received: u64 },
Completed,
Failed { error: String },
}
fn render_download_action(
state: &DownloadState,
file_size: Option<u64>,
on_start: EventHandler<()>,
) -> Element {
match state {
DownloadState::Idle => rsx! {
button {
class: "file-download__btn",
title: "Download file",
onclick: move |_| on_start.call(()),
"Download"
}
},
DownloadState::Downloading { bytes_received } => {
let progress_text = if let Some(total) = file_size {
let pct = if total > 0 {
(*bytes_received as f64 / total as f64 * 100.0) as u32
} else {
0
};
format!("{pct}%")
} else {
format_file_size(*bytes_received)
};
rsx! {
span {
class: "file-download__progress-text",
"{progress_text}"
}
}
},
DownloadState::Completed => rsx! {
span {
class: "file-download__done",
"Done"
}
},
DownloadState::Failed { error } => {
let err = error.clone();
rsx! {
span {
class: "file-download__error",
title: "{err}",
"Failed"
}
button {
class: "file-download__btn file-download__btn--retry",
onclick: move |_| on_start.call(()),
"Retry"
}
}
},
}
}
#[component]
pub fn FileDownload(
file_name: String,
file_url: String,
file_size: Option<u64>,
mimetype: Option<String>,
on_download: EventHandler<String>,
) -> Element {
let mut download_state = use_signal(|| DownloadState::Idle);
let mime = mimetype.unwrap_or_else(|| "application/octet-stream".to_string());
let icon = file_icon_for_mime(&mime);
let size_text = file_size.map(format_file_size).unwrap_or_default();
let icon_label = match icon {
"image" => "img",
"video" => "vid",
"audio" => "aud",
"pdf" => "pdf",
"archive" => "zip",
"text" => "txt",
_ => "file",
};
let on_start_download = {
let file_url = file_url.clone();
move |_: ()| {
download_state.set(DownloadState::Downloading { bytes_received: 0 });
on_download.call(file_url.clone());
}
};
let state_val = download_state.read().clone();
rsx! {
div {
class: "file-download",
div {
class: "file-download__icon file-download__icon--{icon}",
span { "{icon_label}" }
}
div {
class: "file-download__info",
span {
class: "file-download__name",
title: "{file_name}",
"{file_name}"
}
div {
class: "file-download__meta",
if !size_text.is_empty() {
span {
class: "file-download__size",
"{size_text}"
}
}
span {
class: "file-download__mime",
"{mime}"
}
}
}
div {
class: "file-download__action",
{render_download_action(&state_val, file_size, EventHandler::new(on_start_download))}
}
}
}
}