feat(ui): action bar is contextualizez accross sections

- [download] + [remove from recent] for recent section
  - [download] + [remove from favorite] for favorite section
  - [restore] + [permanent delete] for trash section
This commit is contained in:
Edouard Vanbelle
2026-07-20 19:07:57 +02:00
parent c6b856bfc7
commit 67579932c7
7 changed files with 77 additions and 97 deletions
+13 -34
View File
@@ -286,31 +286,10 @@
// prunes its own selection when items reload) — benches/ROUND11.md §S1.
type Selectable = FileItem | FolderItem;
function batchTargets(sel: Selectable[]) {
return sel.map((i) => ({ id: i.id, name: i.name, kind: kindOf(i) }));
}
function batchDownload(sel: Selectable[]) {
for (const i of sel) downloadItem(i);
}
async function batchDelete(sel: Selectable[]) {
const ok = await confirmDialog({
title: t('common.delete', 'Delete'),
message: t('files.confirm_delete_n', { count: sel.length }, 'Delete {{count}} item(s)?'),
confirmText: t('common.delete', 'Delete'),
danger: true
});
if (!ok) return;
try {
await Promise.all(sel.map((i) => (isFile(i) ? deleteFile(i.id) : deleteFolder(i.id))));
const removed = new Set(sel.map((i) => i.id));
raw = raw.filter((i) => !removed.has(i.resource.id));
} catch (e) {
errorToast(e);
}
}
onMount(() => load(true));
</script>
@@ -352,25 +331,25 @@
}}
>
{#snippet batchActions(sel)}
<!--
Favorites-scoped batch cluster: Download stays. Move + Delete
were destructive-to-content operations carried over from the
pre-refactor menu; on a favorites *bookmarks* view they
belong in the row's context menu (rename/move/delete via
`contextActions`), not in the batch bar. Batch "remove from
favorite" un-stars the selected rows without touching the
underlying files — mirrors the per-row favorite star.
-->
<Button
icon="download"
data-testid="favorites-batch-download-btn"
onclick={() => batchDownload(sel)}>{t('common.download', 'Download')}</Button
>
<Button
icon="arrows-alt"
data-testid="favorites-batch-move-btn"
onclick={() => {
moveTarget = null;
moveItems = batchTargets(sel);
moveOpen = true;
}}>{t('files.move', 'Move')}</Button
>
<Button
variant="danger"
icon="trash"
data-testid="favorites-batch-delete-btn"
onclick={() => batchDelete(sel)}>{t('common.delete', 'Delete')}</Button
icon="star-outline"
data-testid="favorites-batch-remove-btn"
onclick={() => sel.forEach(unfavorite)}
>{t('files.unfavorite', 'Remove favorite')}</Button
>
{/snippet}
</ResourceList>
+10 -5
View File
@@ -86,13 +86,18 @@ it('unfavorites a row via the star button', async () => {
await waitFor(() => expect(removeFavorite).toHaveBeenCalledWith('file', 'f1'));
});
it('batch-deletes selected favorites after confirmation', async () => {
it('batch-removes-from-favorite the selection', async () => {
// /favorites' batch bar was intentionally trimmed to Download +
// Remove-from-favorite. Bulk-deleting the underlying file from
// this view (previous behaviour) confused the "this is a
// bookmarks list" semantics — destructive actions belong in the
// row's context menu, not in the batch bar. This test pins the
// new shape: batch button just un-stars the selection.
withOneFile();
confirmDialog.mockResolvedValue(true);
m(deleteFile).mockResolvedValue(undefined);
m(removeFavorite).mockResolvedValue(undefined);
render(FavoritesPage);
await screen.findByText('photo.png');
await fireEvent.click(screen.getByTestId('resource-list-select-f1-checkbox'));
await fireEvent.click(await screen.findByTestId('favorites-batch-delete-btn'));
await waitFor(() => expect(deleteFile).toHaveBeenCalledWith('f1'));
await fireEvent.click(await screen.findByTestId('favorites-batch-remove-btn'));
await waitFor(() => expect(removeFavorite).toHaveBeenCalledWith('file', 'f1'));
});