fix(playlists): surface bulk-delete button in header while multi-select is active (#290)

Selecting playlists via the header toggle only exposed the delete action
through the right-click context menu. Add a visible Delete-selected button
next to Cancel that kicks off the same handleDeleteSelected flow, with a
tooltip hint when some of the selected playlists aren't deletable (foreign
owner). Button is only rendered when selection mode is on and at least one
playlist is selected.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frank Stellmacher
2026-04-24 21:03:24 +02:00
committed by GitHub
parent ef8d83955a
commit cecc59aead
9 changed files with 25 additions and 0 deletions
+1
View File
@@ -1279,6 +1279,7 @@ export const deTranslation = {
deleteSuccess: '{{count}} Playlists gelöscht', deleteSuccess: '{{count}} Playlists gelöscht',
deleteFailed: 'Fehler beim Löschen von {{name}}', deleteFailed: 'Fehler beim Löschen von {{name}}',
deleteSelected: 'Ausgewählte löschen', deleteSelected: 'Ausgewählte löschen',
deleteSelectedPartial: 'Nur {{n}} von {{total}} ausgewählten Playlists können gelöscht werden (die anderen gehören jemand anderem).',
mergeSuccess: '{{count}} Songs in {{playlist}} zusammengeführt', mergeSuccess: '{{count}} Songs in {{playlist}} zusammengeführt',
mergeNoNewSongs: 'Keine neuen Songs zum Hinzufügen', mergeNoNewSongs: 'Keine neuen Songs zum Hinzufügen',
mergeError: 'Fehler beim Zusammenführen von Playlists', mergeError: 'Fehler beim Zusammenführen von Playlists',
+1
View File
@@ -1282,6 +1282,7 @@ export const enTranslation = {
deleteSuccess: '{{count}} playlists deleted', deleteSuccess: '{{count}} playlists deleted',
deleteFailed: 'Error deleting {{name}}', deleteFailed: 'Error deleting {{name}}',
deleteSelected: 'Delete selected', deleteSelected: 'Delete selected',
deleteSelectedPartial: 'Only {{n}} of {{total}} selected playlists can be deleted (the others belong to someone else).',
mergeSuccess: '{{count}} songs merged into {{playlist}}', mergeSuccess: '{{count}} songs merged into {{playlist}}',
mergeNoNewSongs: 'No new songs to add', mergeNoNewSongs: 'No new songs to add',
mergeError: 'Error merging playlists', mergeError: 'Error merging playlists',
+1
View File
@@ -1272,6 +1272,7 @@ export const esTranslation = {
deleteSuccess: '{{count}} listas eliminadas', deleteSuccess: '{{count}} listas eliminadas',
deleteFailed: 'Error al eliminar {{name}}', deleteFailed: 'Error al eliminar {{name}}',
deleteSelected: 'Eliminar seleccionadas', deleteSelected: 'Eliminar seleccionadas',
deleteSelectedPartial: 'Solo {{n}} de {{total}} listas seleccionadas pueden eliminarse (las demás pertenecen a otro usuario).',
mergeSuccess: '{{count}} canciones unificadas en {{playlist}}', mergeSuccess: '{{count}} canciones unificadas en {{playlist}}',
mergeNoNewSongs: 'No hay canciones nuevas para agregar', mergeNoNewSongs: 'No hay canciones nuevas para agregar',
mergeError: 'Error al unificar listas', mergeError: 'Error al unificar listas',
+1
View File
@@ -1267,6 +1267,7 @@ export const frTranslation = {
deleteSuccess: '{{count}} playlists supprimées', deleteSuccess: '{{count}} playlists supprimées',
deleteFailed: 'Erreur lors de la suppression de {{name}}', deleteFailed: 'Erreur lors de la suppression de {{name}}',
deleteSelected: 'Supprimer la sélection', deleteSelected: 'Supprimer la sélection',
deleteSelectedPartial: 'Seulement {{n}} des {{total}} playlists sélectionnées peuvent être supprimées (les autres appartiennent à quelqu\'un d\'autre).',
mergeSuccess: '{{count}} morceaux fusionnés dans {{playlist}}', mergeSuccess: '{{count}} morceaux fusionnés dans {{playlist}}',
mergeNoNewSongs: 'Aucun nouveau morceau à ajouter', mergeNoNewSongs: 'Aucun nouveau morceau à ajouter',
mergeError: 'Erreur lors de la fusion des playlists', mergeError: 'Erreur lors de la fusion des playlists',
+1
View File
@@ -1266,6 +1266,7 @@ export const nbTranslation = {
deleteSuccess: '{{count}} spillelister slettet', deleteSuccess: '{{count}} spillelister slettet',
deleteFailed: 'Feil ved sletting av {{name}}', deleteFailed: 'Feil ved sletting av {{name}}',
deleteSelected: 'Slett valgte', deleteSelected: 'Slett valgte',
deleteSelectedPartial: 'Bare {{n}} av {{total}} valgte spillelister kan slettes (de andre tilhører noen andre).',
mergeSuccess: '{{count}} sanger slått sammen i {{playlist}}', mergeSuccess: '{{count}} sanger slått sammen i {{playlist}}',
mergeNoNewSongs: 'Ingen nye sanger å legge til', mergeNoNewSongs: 'Ingen nye sanger å legge til',
mergeError: 'Feil ved sammenslåing av spillelister', mergeError: 'Feil ved sammenslåing av spillelister',
+1
View File
@@ -1266,6 +1266,7 @@ export const nlTranslation = {
deleteSuccess: '{{count}} playlists verwijderd', deleteSuccess: '{{count}} playlists verwijderd',
deleteFailed: 'Fout bij verwijderen van {{name}}', deleteFailed: 'Fout bij verwijderen van {{name}}',
deleteSelected: 'Geselecteerde verwijderen', deleteSelected: 'Geselecteerde verwijderen',
deleteSelectedPartial: 'Slechts {{n}} van de {{total}} geselecteerde playlists kunnen worden verwijderd (de andere zijn van iemand anders).',
mergeSuccess: '{{count}} nummers samengevoegd in {{playlist}}', mergeSuccess: '{{count}} nummers samengevoegd in {{playlist}}',
mergeNoNewSongs: 'Geen nieuwe nummers om toe te voegen', mergeNoNewSongs: 'Geen nieuwe nummers om toe te voegen',
mergeError: 'Fout bij samenvoegen van playlists', mergeError: 'Fout bij samenvoegen van playlists',
+1
View File
@@ -1346,6 +1346,7 @@ export const ruTranslation = {
deleteSuccess: '{{count}} плейлистов удалено', deleteSuccess: '{{count}} плейлистов удалено',
deleteFailed: 'Ошибка при удалении {{name}}', deleteFailed: 'Ошибка при удалении {{name}}',
deleteSelected: 'Удалить выбранные', deleteSelected: 'Удалить выбранные',
deleteSelectedPartial: 'Можно удалить только {{n}} из {{total}} выбранных плейлистов (остальные принадлежат другим пользователям).',
mergeSuccess: '{{count}} треков объединено в {{playlist}}', mergeSuccess: '{{count}} треков объединено в {{playlist}}',
mergeNoNewSongs: 'Нет новых треков для добавления', mergeNoNewSongs: 'Нет новых треков для добавления',
mergeError: 'Ошибка при объединении плейлистов', mergeError: 'Ошибка при объединении плейлистов',
+1
View File
@@ -1261,6 +1261,7 @@ export const zhTranslation = {
deleteSuccess: '{{count}} 个播放列表已删除', deleteSuccess: '{{count}} 个播放列表已删除',
deleteFailed: '删除 {{name}} 时出错', deleteFailed: '删除 {{name}} 时出错',
deleteSelected: '删除所选', deleteSelected: '删除所选',
deleteSelectedPartial: '所选 {{total}} 个播放列表中只有 {{n}} 个可删除(其余属于他人)。',
mergeSuccess: '{{count}} 首歌曲已合并到 {{playlist}}', mergeSuccess: '{{count}} 首歌曲已合并到 {{playlist}}',
mergeNoNewSongs: '没有新歌曲可添加', mergeNoNewSongs: '没有新歌曲可添加',
mergeError: '合并播放列表时出错', mergeError: '合并播放列表时出错',
+17
View File
@@ -734,6 +734,23 @@ export default function Playlists() {
)} )}
</> </>
)} )}
{selectionMode && selectedIds.size > 0 && (() => {
const deletableCount = selectedPlaylists.filter(isPlaylistDeletable).length;
return (
<button
className="btn btn-danger"
onClick={handleDeleteSelected}
disabled={deletableCount === 0}
data-tooltip={deletableCount === selectedIds.size
? undefined
: t('playlists.deleteSelectedPartial', { n: deletableCount, total: selectedIds.size })}
data-tooltip-pos="bottom"
>
<Trash2 size={15} />
{t('playlists.deleteSelected')}
</button>
);
})()}
<button <button
className={`btn btn-surface${selectionMode ? ' btn-sort-active' : ''}`} className={`btn btn-surface${selectionMode ? ' btn-sort-active' : ''}`}
onClick={toggleSelectionMode} onClick={toggleSelectionMode}