mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 12:30:06 +02:00
227 lines
9.9 KiB
TypeScript
227 lines
9.9 KiB
TypeScript
import { useMemo } from 'react';
|
|
|
|
import { ItemListStateItemWithRequiredProperties } from '/@/renderer/components/item-list/helpers/item-list-state';
|
|
import { DefaultItemControlProps, ItemControls } from '/@/renderer/components/item-list/types';
|
|
import { usePlayerContext } from '/@/renderer/features/player/context/player-context';
|
|
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
|
|
import { Play } from '/@/shared/types/types';
|
|
|
|
export const useDefaultItemListControls = () => {
|
|
const player = usePlayerContext();
|
|
|
|
const controls: ItemControls = useMemo(() => {
|
|
return {
|
|
onClick: ({ event, internalState, item }: DefaultItemControlProps) => {
|
|
if (!item || !internalState || !event) {
|
|
return;
|
|
}
|
|
|
|
// Extract rowId from the item
|
|
const rowId = internalState.extractRowId(item);
|
|
if (!rowId) return;
|
|
|
|
// Use the item directly (rowId is separate, used only as key in state)
|
|
const itemListItem = item as ItemListStateItemWithRequiredProperties;
|
|
|
|
// Check if ctrl/cmd key is held for multi-selection
|
|
if (event.ctrlKey || event.metaKey) {
|
|
const isCurrentlySelected = internalState.isSelected(rowId);
|
|
|
|
if (isCurrentlySelected) {
|
|
// Remove this item from selection
|
|
const currentSelected = internalState.getSelected();
|
|
const filteredSelected = currentSelected.filter(
|
|
(
|
|
selectedItem,
|
|
): selectedItem is ItemListStateItemWithRequiredProperties =>
|
|
typeof selectedItem === 'object' &&
|
|
selectedItem !== null &&
|
|
internalState.extractRowId(selectedItem) !== rowId,
|
|
);
|
|
internalState.setSelected(filteredSelected);
|
|
} else {
|
|
// Add this item to selection
|
|
const currentSelected = internalState.getSelected();
|
|
const newSelected = [
|
|
...currentSelected.filter(
|
|
(
|
|
selectedItem,
|
|
): selectedItem is ItemListStateItemWithRequiredProperties =>
|
|
typeof selectedItem === 'object' && selectedItem !== null,
|
|
),
|
|
itemListItem,
|
|
];
|
|
internalState.setSelected(newSelected);
|
|
}
|
|
}
|
|
// Check if shift key is held for range selection
|
|
else if (event.shiftKey) {
|
|
const selectedItems = internalState.getSelected();
|
|
const lastSelectedItem = selectedItems[selectedItems.length - 1];
|
|
|
|
if (
|
|
lastSelectedItem &&
|
|
typeof lastSelectedItem === 'object' &&
|
|
lastSelectedItem !== null
|
|
) {
|
|
// Get the data array from internalState
|
|
const data = internalState.getData();
|
|
// Filter out null/undefined values (e.g., header row)
|
|
const validData = data.filter((d) => d && typeof d === 'object');
|
|
|
|
// Find the indices of the last selected item and current item
|
|
const lastRowId = internalState.extractRowId(lastSelectedItem);
|
|
if (!lastRowId) return;
|
|
const lastIndex = internalState.findItemIndex(lastRowId);
|
|
const currentIndex = internalState.findItemIndex(rowId);
|
|
|
|
if (lastIndex !== -1 && currentIndex !== -1) {
|
|
// Create range selection - select ALL items in the range
|
|
const startIndex = Math.min(lastIndex, currentIndex);
|
|
const stopIndex = Math.max(lastIndex, currentIndex);
|
|
|
|
const rangeItems: ItemListStateItemWithRequiredProperties[] = [];
|
|
for (let i = startIndex; i <= stopIndex; i++) {
|
|
const rangeItem = validData[i];
|
|
if (
|
|
rangeItem &&
|
|
typeof rangeItem === 'object' &&
|
|
'_serverId' in rangeItem &&
|
|
'itemType' in rangeItem
|
|
) {
|
|
const rangeRowId = internalState.extractRowId(rangeItem);
|
|
if (rangeRowId) {
|
|
rangeItems.push(
|
|
rangeItem as ItemListStateItemWithRequiredProperties,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Merge with existing selection, avoiding duplicates
|
|
const currentSelected = internalState.getSelected();
|
|
const newSelected = [
|
|
...currentSelected.filter(
|
|
(
|
|
selectedItem,
|
|
): selectedItem is ItemListStateItemWithRequiredProperties =>
|
|
typeof selectedItem === 'object' && selectedItem !== null,
|
|
),
|
|
];
|
|
rangeItems.forEach((rangeItem) => {
|
|
const rangeRowId = internalState.extractRowId(rangeItem);
|
|
if (
|
|
rangeRowId &&
|
|
!newSelected.some(
|
|
(selected) =>
|
|
internalState.extractRowId(selected) === rangeRowId,
|
|
)
|
|
) {
|
|
newSelected.push(rangeItem);
|
|
}
|
|
});
|
|
internalState.setSelected(newSelected);
|
|
}
|
|
} else {
|
|
// No previous selection, just select this item
|
|
internalState.setSelected([itemListItem]);
|
|
}
|
|
} else {
|
|
// Regular click - deselect all others and select only this item
|
|
// If this item is already the only selected item, deselect it
|
|
const selectedItems = internalState.getSelected();
|
|
const isOnlySelected =
|
|
selectedItems.length === 1 &&
|
|
typeof selectedItems[0] === 'object' &&
|
|
selectedItems[0] !== null &&
|
|
internalState.extractRowId(selectedItems[0]) === rowId;
|
|
|
|
if (isOnlySelected) {
|
|
internalState.clearSelected();
|
|
} else {
|
|
internalState.setSelected([itemListItem]);
|
|
}
|
|
}
|
|
},
|
|
|
|
onDoubleClick: ({ item, itemType }: DefaultItemControlProps) => {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
if (itemType === LibraryItem.QUEUE_SONG) {
|
|
const queueSong = item as QueueSong;
|
|
if (queueSong._uniqueId) {
|
|
player.mediaPlay(queueSong._uniqueId);
|
|
}
|
|
}
|
|
},
|
|
|
|
onExpand: ({ internalState, item }: DefaultItemControlProps) => {
|
|
if (!item || !internalState) {
|
|
return;
|
|
}
|
|
|
|
// Extract rowId from the item
|
|
const rowId = internalState.extractRowId(item);
|
|
if (!rowId) return;
|
|
|
|
// Use the item directly (rowId is separate, used only as key in state)
|
|
const itemListItem = item as ItemListStateItemWithRequiredProperties;
|
|
|
|
return internalState?.toggleExpanded(itemListItem);
|
|
},
|
|
|
|
onFavorite: ({
|
|
favorite,
|
|
item,
|
|
itemType,
|
|
}: DefaultItemControlProps & { favorite: boolean }) => {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
player.setFavorite(item._serverId, [item.id], itemType, favorite);
|
|
},
|
|
|
|
onMore: ({ internalState, item, itemType }: DefaultItemControlProps) => {
|
|
console.log('handleItemMore', item, itemType, internalState);
|
|
},
|
|
|
|
onPlay: ({
|
|
item,
|
|
itemType,
|
|
playType,
|
|
}: DefaultItemControlProps & { playType: Play }) => {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
player.addToQueueByFetch(item._serverId, [item.id], itemType, playType);
|
|
},
|
|
|
|
onRating: ({
|
|
item,
|
|
itemType,
|
|
rating,
|
|
}: DefaultItemControlProps & { rating: number }) => {
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
const previousRating = (item as { userRating: number }).userRating || 0;
|
|
|
|
let newRating = rating;
|
|
|
|
if (previousRating === rating) {
|
|
newRating = 0;
|
|
}
|
|
|
|
player.setRating(item._serverId, [item.id], itemType, newRating);
|
|
},
|
|
};
|
|
}, [player]);
|
|
|
|
return controls;
|
|
};
|