diff --git a/src/renderer/features/albums/components/album-detail-content.tsx b/src/renderer/features/albums/components/album-detail-content.tsx index adb691455..2fad55b76 100644 --- a/src/renderer/features/albums/components/album-detail-content.tsx +++ b/src/renderer/features/albums/components/album-detail-content.tsx @@ -1,3 +1,8 @@ +import type { + ItemListStateActions, + ItemListStateItemWithRequiredProperties, +} from '/@/renderer/components/item-list/helpers/item-list-state'; + import { useSuspenseQuery } from '@tanstack/react-query'; import { ReactNode, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -6,6 +11,7 @@ import { generatePath, useParams } from 'react-router'; import styles from './album-detail-content.module.css'; import { useGridCarouselContainerQuery } from '/@/renderer/components/grid-carousel/grid-carousel-v2'; +import { useItemListStateSubscription } from '/@/renderer/components/item-list/helpers/item-list-state'; import { useItemListColumnReorder } from '/@/renderer/components/item-list/helpers/use-item-list-column-reorder'; import { useItemListColumnResize } from '/@/renderer/components/item-list/helpers/use-item-list-column-resize'; import { SONG_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; @@ -400,6 +406,80 @@ interface AlbumDetailSongsTableProps { songs: Song[]; } +interface DiscGroupRowProps { + discGroup: { + discNumber: number; + discSubtitle: null | string; + }; + groupItems: unknown[]; + internalState: ItemListStateActions; + t: (key: string, options?: any) => string; +} + +const DiscGroupRow = ({ discGroup, groupItems, internalState, t }: DiscGroupRowProps) => { + const selectionVersion = useItemListStateSubscription(internalState, (state) => + state ? state.version : 0, + ); + + const selectedCount = groupItems.filter((item) => { + if (!item || typeof item !== 'object' || !('id' in item)) return false; + const rowId = internalState.extractRowId(item); + return rowId ? internalState.isSelected(rowId) : false; + }).length; + + const isAllSelected = selectedCount === groupItems.length; + + void selectionVersion; + + const handleCheckboxChange = () => { + const selectableItems = groupItems.filter( + (item): item is ItemListStateItemWithRequiredProperties => + item !== null && typeof item === 'object', + ); + + if (isAllSelected) { + // Deselect all items in the group + const currentlySelected = + internalState.getSelected() as ItemListStateItemWithRequiredProperties[]; + const groupItemIds = new Set( + selectableItems.map((item) => internalState.extractRowId(item)).filter(Boolean), + ); + const itemsToKeep = currentlySelected.filter( + (item) => !groupItemIds.has(internalState.extractRowId(item) || ''), + ); + internalState.setSelected(itemsToKeep); + } else { + // Select all items in the group (add to existing selection) + const currentlySelected = + internalState.getSelected() as ItemListStateItemWithRequiredProperties[]; + const selectedIds = new Set( + currentlySelected.map((item) => internalState.extractRowId(item)).filter(Boolean), + ); + const itemsToAdd = selectableItems.filter( + (item) => !selectedIds.has(internalState.extractRowId(item) || ''), + ); + internalState.setSelected([...currentlySelected, ...itemsToAdd]); + } + }; + + return ( + + + {t('common.disc', { postProcess: 'sentenceCase' })} {discGroup.discNumber} + {discGroup.discSubtitle && ` - ${discGroup.discSubtitle}`} + + } + onChange={handleCheckboxChange} + size="xs" + /> + + ); +}; + function AlbumDetailCarousels({ data }: { data: Album }) { const { t } = useTranslation(); @@ -568,65 +648,18 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => { data: unknown[]; groupIndex: number; index: number; - internalState: any; + internalState: ItemListStateActions; startDataIndex: number; }) => { const groupItems = data.slice(startDataIndex, startDataIndex + discGroup.itemCount); - const selectedCount = groupItems.filter((item) => { - if (!item || typeof item !== 'object' || !('id' in item)) return false; - const rowId = internalState.extractRowId(item); - return rowId ? internalState.isSelected(rowId) : false; - }).length; - - const isAllSelected = selectedCount === groupItems.length; - - const handleCheckboxChange = () => { - const selectableItems = groupItems; - - if (isAllSelected) { - // Deselect all items in the group - const currentlySelected = internalState.getSelected(); - const groupItemIds = new Set( - selectableItems - .map((item) => internalState.extractRowId(item)) - .filter(Boolean), - ); - const itemsToKeep = currentlySelected.filter( - (item) => !groupItemIds.has(internalState.extractRowId(item) || ''), - ); - internalState.setSelected(itemsToKeep); - } else { - // Select all items in the group (add to existing selection) - const currentlySelected = internalState.getSelected(); - const selectedIds = new Set( - currentlySelected - .map((item) => internalState.extractRowId(item)) - .filter(Boolean), - ); - const itemsToAdd = selectableItems.filter( - (item) => !selectedIds.has(internalState.extractRowId(item) || ''), - ); - internalState.setSelected([...currentlySelected, ...itemsToAdd]); - } - }; - return ( - - - {t('common.disc', { postProcess: 'sentenceCase' })}{' '} - {discGroup.discNumber} - {discGroup.discSubtitle && ` - ${discGroup.discSubtitle}`} - - } - onChange={handleCheckboxChange} - size="xs" - /> - + ); }, rowHeight: 40,