mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-06 01:17:48 +00:00
fix(ui): harden queue controls and floating menus
This commit is contained in:
+31
-1
@@ -53,6 +53,36 @@ fn reorder_selected_queue_tasks(
|
||||
Some(reordered)
|
||||
}
|
||||
|
||||
fn reorder_selected_queue_tasks_in_order(
|
||||
queue_tasks: &[QueuedTask],
|
||||
ids: &[String],
|
||||
target_index: usize,
|
||||
) -> Option<Vec<QueuedTask>> {
|
||||
let selected_ids = ids.iter().collect::<HashSet<_>>();
|
||||
let mut seen_ids = HashSet::new();
|
||||
let selected_tasks = ids
|
||||
.iter()
|
||||
.filter(|id| seen_ids.insert(*id))
|
||||
.filter_map(|id| queue_tasks.iter().find(|task| task.id == *id))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if selected_tasks.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let unselected_tasks = queue_tasks
|
||||
.iter()
|
||||
.filter(|task| !selected_ids.contains(&task.id))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let insert_index = target_index.min(unselected_tasks.len());
|
||||
let mut reordered = Vec::with_capacity(queue_tasks.len());
|
||||
reordered.extend_from_slice(&unselected_tasks[..insert_index]);
|
||||
reordered.extend(selected_tasks);
|
||||
reordered.extend_from_slice(&unselected_tasks[insert_index..]);
|
||||
Some(reordered)
|
||||
}
|
||||
|
||||
type Aria2ControlLocks = Arc<StdMutex<HashMap<String, Arc<Mutex<()>>>>>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -2293,7 +2323,7 @@ impl<R: tauri::Runtime> QueueManager<R> {
|
||||
.map(|index| pending[*index].clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if let Some(reordered) = reorder_selected_queue_tasks(&queue_tasks, ids, target_index) {
|
||||
if let Some(reordered) = reorder_selected_queue_tasks_in_order(&queue_tasks, ids, target_index) {
|
||||
for (queue_index, pending_index) in queue_positions.iter().enumerate() {
|
||||
pending[*pending_index] = reordered[queue_index].clone();
|
||||
}
|
||||
|
||||
@@ -2032,6 +2032,23 @@ async fn multi_move_reorders_selected_items_as_one_atomic_block() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn direction_move_keeps_the_queue_order_for_an_unsorted_selection() {
|
||||
use firelink_lib::ipc::QueueDirection;
|
||||
|
||||
let (mgr, _spawner) = make_manager(3);
|
||||
for id in ["a", "b", "c", "d", "e"] {
|
||||
mgr.push(sample_task(id)).await.unwrap();
|
||||
}
|
||||
|
||||
let selected = vec!["d".to_string(), "b".to_string()];
|
||||
assert_eq!(
|
||||
mgr.move_many_in_queue(&selected, "main", QueueDirection::Up)
|
||||
.await,
|
||||
vec!["b", "d", "a", "c", "e"]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn target_move_reorders_a_selected_block_and_clamps_the_target() {
|
||||
use firelink_lib::ipc::QueueDirection;
|
||||
@@ -2059,6 +2076,35 @@ async fn target_move_reorders_a_selected_block_and_clamps_the_target() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn target_move_preserves_the_explicit_selection_order() {
|
||||
let (mgr, _spawner) = make_manager(3);
|
||||
for id in ["a", "b", "c", "d", "e"] {
|
||||
mgr.push(sample_task(id)).await.unwrap();
|
||||
}
|
||||
|
||||
let selected = vec!["d".to_string(), "b".to_string()];
|
||||
assert_eq!(
|
||||
mgr.move_many_in_queue_to(&selected, "main", 0).await,
|
||||
vec!["d", "b", "a", "c", "e"]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn target_move_deduplicates_ids_without_dropping_pending_tasks() {
|
||||
let (mgr, _spawner) = make_manager(3);
|
||||
for id in ["a", "b", "c", "d"] {
|
||||
mgr.push(sample_task(id)).await.unwrap();
|
||||
}
|
||||
|
||||
let selected = vec!["c".to_string(), "c".to_string(), "b".to_string()];
|
||||
assert_eq!(
|
||||
mgr.move_many_in_queue_to(&selected, "main", 0).await,
|
||||
vec!["c", "b", "a", "d"]
|
||||
);
|
||||
assert_eq!(mgr.pending_order(None).await.len(), 4);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn moving_one_queue_does_not_reorder_another_queue() {
|
||||
use firelink_lib::ipc::QueueDirection;
|
||||
|
||||
Reference in New Issue
Block a user