feat: Implement client-side sorting for torrent table
This commit is contained in:
@@ -933,6 +933,9 @@
|
|||||||
.mb-6 {
|
.mb-6 {
|
||||||
margin-bottom: calc(var(--spacing) * 6);
|
margin-bottom: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
|
.ml-1 {
|
||||||
|
margin-left: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
.ml-auto {
|
.ml-auto {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
@@ -1403,6 +1406,9 @@
|
|||||||
.uppercase {
|
.uppercase {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
.opacity-0 {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
.opacity-70 {
|
.opacity-70 {
|
||||||
opacity: 70%;
|
opacity: 70%;
|
||||||
}
|
}
|
||||||
@@ -1514,6 +1520,13 @@
|
|||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
.group-hover\:opacity-50 {
|
||||||
|
&:is(:where(.group):hover *) {
|
||||||
|
@media (hover: hover) {
|
||||||
|
opacity: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.hover\:bg-accent {
|
.hover\:bg-accent {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -1521,6 +1534,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.hover\:bg-base-300 {
|
||||||
|
&:hover {
|
||||||
|
@media (hover: hover) {
|
||||||
|
background-color: var(--color-base-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.hover\:bg-primary\/90 {
|
.hover\:bg-primary\/90 {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
|
|||||||
@@ -19,12 +19,32 @@ fn format_speed(bytes_per_sec: i64) -> String {
|
|||||||
format!("{}/s", format_bytes(bytes_per_sec))
|
format!("{}/s", format_bytes(bytes_per_sec))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum SortColumn {
|
||||||
|
Name,
|
||||||
|
Size,
|
||||||
|
Progress,
|
||||||
|
Status,
|
||||||
|
DownSpeed,
|
||||||
|
UpSpeed,
|
||||||
|
ETA,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum SortDirection {
|
||||||
|
Ascending,
|
||||||
|
Descending,
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn TorrentTable() -> impl IntoView {
|
pub fn TorrentTable() -> impl IntoView {
|
||||||
let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
|
let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
|
||||||
|
|
||||||
|
let sort_col = create_rw_signal(SortColumn::Name);
|
||||||
|
let sort_dir = create_rw_signal(SortDirection::Ascending);
|
||||||
|
|
||||||
let filtered_torrents = move || {
|
let filtered_torrents = move || {
|
||||||
store.torrents.get().into_iter().filter(|t| {
|
let mut torrents = store.torrents.get().into_iter().filter(|t| {
|
||||||
let filter = store.filter.get();
|
let filter = store.filter.get();
|
||||||
match filter {
|
match filter {
|
||||||
crate::store::FilterStatus::All => true,
|
crate::store::FilterStatus::All => true,
|
||||||
@@ -34,7 +54,56 @@ pub fn TorrentTable() -> impl IntoView {
|
|||||||
crate::store::FilterStatus::Inactive => t.status == shared::TorrentStatus::Paused || t.status == shared::TorrentStatus::Error,
|
crate::store::FilterStatus::Inactive => t.status == shared::TorrentStatus::Paused || t.status == shared::TorrentStatus::Error,
|
||||||
_ => true
|
_ => true
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>()
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
torrents.sort_by(|a, b| {
|
||||||
|
let col = sort_col.get();
|
||||||
|
let dir = sort_dir.get();
|
||||||
|
let cmp = match col {
|
||||||
|
SortColumn::Name => a.name.to_lowercase().cmp(&b.name.to_lowercase()),
|
||||||
|
SortColumn::Size => a.size.cmp(&b.size),
|
||||||
|
SortColumn::Progress => a.percent_complete.partial_cmp(&b.percent_complete).unwrap_or(std::cmp::Ordering::Equal),
|
||||||
|
SortColumn::Status => format!("{:?}", a.status).cmp(&format!("{:?}", b.status)),
|
||||||
|
SortColumn::DownSpeed => a.down_rate.cmp(&b.down_rate),
|
||||||
|
SortColumn::UpSpeed => a.up_rate.cmp(&b.up_rate),
|
||||||
|
SortColumn::ETA => {
|
||||||
|
// ETA 0 means infinity usually, so we need to handle it.
|
||||||
|
// But for simple sorting, maybe just treat is numeric?
|
||||||
|
// Let's treat 0 as MAX for ascending, MIN for descending? Or just as is?
|
||||||
|
// Usually negative or 0 means unknown/inf.
|
||||||
|
// Let's handle 0 as very large number for sorting purposes if we want it at the end of ascending
|
||||||
|
let a_eta = if a.eta <= 0 { i64::MAX } else { a.eta };
|
||||||
|
let b_eta = if b.eta <= 0 { i64::MAX } else { b.eta };
|
||||||
|
a_eta.cmp(&b_eta)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if dir == SortDirection::Descending { cmp.reverse() } else { cmp }
|
||||||
|
});
|
||||||
|
|
||||||
|
torrents
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle_sort = move |col: SortColumn| {
|
||||||
|
if sort_col.get() == col {
|
||||||
|
sort_dir.update(|d| *d = match d {
|
||||||
|
SortDirection::Ascending => SortDirection::Descending,
|
||||||
|
SortDirection::Descending => SortDirection::Ascending,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sort_col.set(col);
|
||||||
|
sort_dir.set(SortDirection::Ascending);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let sort_arrow = move |col: SortColumn| {
|
||||||
|
if sort_col.get() == col {
|
||||||
|
match sort_dir.get() {
|
||||||
|
SortDirection::Ascending => view!{ <span class="ml-1 text-xs">"▲"</span> }.into_view(),
|
||||||
|
SortDirection::Descending => view!{ <span class="ml-1 text-xs">"▼"</span> }.into_view(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view!{ <span class="ml-1 text-xs opacity-0 group-hover:opacity-50">"▲"</span> }.into_view()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
@@ -47,15 +116,29 @@ pub fn TorrentTable() -> impl IntoView {
|
|||||||
<input type="checkbox" class="checkbox checkbox-xs rounded-none" />
|
<input type="checkbox" class="checkbox checkbox-xs rounded-none" />
|
||||||
</label>
|
</label>
|
||||||
</th>
|
</th>
|
||||||
<th>"Name"</th>
|
<th class="cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::Name)>
|
||||||
<th class="w-24">"Size"</th>
|
<div class="flex items-center">"Name" {move || sort_arrow(SortColumn::Name)}</div>
|
||||||
<th class="w-48">"Progress"</th>
|
</th>
|
||||||
<th class="w-24">"Status"</th>
|
<th class="w-24 cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::Size)>
|
||||||
|
<div class="flex items-center">"Size" {move || sort_arrow(SortColumn::Size)}</div>
|
||||||
|
</th>
|
||||||
|
<th class="w-48 cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::Progress)>
|
||||||
|
<div class="flex items-center">"Progress" {move || sort_arrow(SortColumn::Progress)}</div>
|
||||||
|
</th>
|
||||||
|
<th class="w-24 cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::Status)>
|
||||||
|
<div class="flex items-center">"Status" {move || sort_arrow(SortColumn::Status)}</div>
|
||||||
|
</th>
|
||||||
// <th class="w-20">"Seeds"</th> // Not available in shared::Torrent
|
// <th class="w-20">"Seeds"</th> // Not available in shared::Torrent
|
||||||
// <th class="w-20">"Peers"</th> // Not available in shared::Torrent
|
// <th class="w-20">"Peers"</th> // Not available in shared::Torrent
|
||||||
<th class="w-24">"Down Speed"</th>
|
<th class="w-24 cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::DownSpeed)>
|
||||||
<th class="w-24">"Up Speed"</th>
|
<div class="flex items-center">"Down Speed" {move || sort_arrow(SortColumn::DownSpeed)}</div>
|
||||||
<th class="w-24">"ETA"</th>
|
</th>
|
||||||
|
<th class="w-24 cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::UpSpeed)>
|
||||||
|
<div class="flex items-center">"Up Speed" {move || sort_arrow(SortColumn::UpSpeed)}</div>
|
||||||
|
</th>
|
||||||
|
<th class="w-24 cursor-pointer hover:bg-base-300 transition-colors group select-none" on:click=move |_| handle_sort(SortColumn::ETA)>
|
||||||
|
<div class="flex items-center">"ETA" {move || sort_arrow(SortColumn::ETA)}</div>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user