mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 10:23:33 +00:00
Lint all files
This commit is contained in:
parent
22af76b4d6
commit
30e52ebb54
334 changed files with 76519 additions and 75932 deletions
|
|
@ -6,32 +6,32 @@ import { Song } from '/@/renderer/api/types';
|
|||
import { PlayQueue } from '/@/renderer/features/now-playing/components/play-queue';
|
||||
|
||||
export const DrawerPlayQueue = () => {
|
||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
h="100%"
|
||||
>
|
||||
<Box
|
||||
bg="var(--main-bg)"
|
||||
sx={{ borderRadius: '10px' }}
|
||||
>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</Box>
|
||||
<Flex
|
||||
bg="var(--main-bg)"
|
||||
h="100%"
|
||||
mb="0.6rem"
|
||||
>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
h="100%"
|
||||
>
|
||||
<Box
|
||||
bg="var(--main-bg)"
|
||||
sx={{ borderRadius: '10px' }}
|
||||
>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</Box>
|
||||
<Flex
|
||||
bg="var(--main-bg)"
|
||||
h="100%"
|
||||
mb="0.6rem"
|
||||
>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import { PageHeader } from '/@/renderer/components';
|
|||
import { LibraryHeaderBar } from '/@/renderer/features/shared';
|
||||
|
||||
export const NowPlayingHeader = () => {
|
||||
// const currentSong = useCurrentSong();
|
||||
// const theme = useTheme();
|
||||
// const currentSong = useCurrentSong();
|
||||
// const theme = useTheme();
|
||||
|
||||
return (
|
||||
<PageHeader backgroundColor="var(--titlebar-bg)">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>Queue</LibraryHeaderBar.Title>
|
||||
</LibraryHeaderBar>
|
||||
</PageHeader>
|
||||
);
|
||||
return (
|
||||
<PageHeader backgroundColor="var(--titlebar-bg)">
|
||||
<LibraryHeaderBar>
|
||||
<LibraryHeaderBar.Title>Queue</LibraryHeaderBar.Title>
|
||||
</LibraryHeaderBar>
|
||||
</PageHeader>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import { Group } from '@mantine/core';
|
|||
import { Button, Popover } from '/@/renderer/components';
|
||||
import isElectron from 'is-electron';
|
||||
import {
|
||||
RiArrowDownLine,
|
||||
RiArrowUpLine,
|
||||
RiShuffleLine,
|
||||
RiDeleteBinLine,
|
||||
RiListSettingsLine,
|
||||
RiEraserLine,
|
||||
RiArrowDownLine,
|
||||
RiArrowUpLine,
|
||||
RiShuffleLine,
|
||||
RiDeleteBinLine,
|
||||
RiListSettingsLine,
|
||||
RiEraserLine,
|
||||
} from 'react-icons/ri';
|
||||
import { Song } from '/@/renderer/api/types';
|
||||
import { usePlayerControls, useQueueControls } from '/@/renderer/store';
|
||||
|
|
@ -21,156 +21,156 @@ import { TableConfigDropdown } from '/@/renderer/components/virtual-table';
|
|||
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
||||
|
||||
interface PlayQueueListOptionsProps {
|
||||
tableRef: MutableRefObject<{ grid: AgGridReactType<Song> } | null>;
|
||||
type: TableType;
|
||||
tableRef: MutableRefObject<{ grid: AgGridReactType<Song> } | null>;
|
||||
type: TableType;
|
||||
}
|
||||
|
||||
export const PlayQueueListControls = ({ type, tableRef }: PlayQueueListOptionsProps) => {
|
||||
const { clearQueue, moveToBottomOfQueue, moveToTopOfQueue, shuffleQueue, removeFromQueue } =
|
||||
useQueueControls();
|
||||
const { clearQueue, moveToBottomOfQueue, moveToTopOfQueue, shuffleQueue, removeFromQueue } =
|
||||
useQueueControls();
|
||||
|
||||
const { pause } = usePlayerControls();
|
||||
const { pause } = usePlayerControls();
|
||||
|
||||
const playerType = usePlayerType();
|
||||
const setCurrentTime = useSetCurrentTime();
|
||||
const playerType = usePlayerType();
|
||||
const setCurrentTime = useSetCurrentTime();
|
||||
|
||||
const handleMoveToBottom = () => {
|
||||
const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
|
||||
const uniqueIds = selectedRows?.map((row) => row.uniqueId);
|
||||
if (!uniqueIds?.length) return;
|
||||
const handleMoveToBottom = () => {
|
||||
const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
|
||||
const uniqueIds = selectedRows?.map((row) => row.uniqueId);
|
||||
if (!uniqueIds?.length) return;
|
||||
|
||||
const playerData = moveToBottomOfQueue(uniqueIds);
|
||||
const playerData = moveToBottomOfQueue(uniqueIds);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMoveToTop = () => {
|
||||
const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
|
||||
const uniqueIds = selectedRows?.map((row) => row.uniqueId);
|
||||
if (!uniqueIds?.length) return;
|
||||
const handleMoveToTop = () => {
|
||||
const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
|
||||
const uniqueIds = selectedRows?.map((row) => row.uniqueId);
|
||||
if (!uniqueIds?.length) return;
|
||||
|
||||
const playerData = moveToTopOfQueue(uniqueIds);
|
||||
const playerData = moveToTopOfQueue(uniqueIds);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveSelected = () => {
|
||||
const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
|
||||
const uniqueIds = selectedRows?.map((row) => row.uniqueId);
|
||||
if (!uniqueIds?.length) return;
|
||||
const handleRemoveSelected = () => {
|
||||
const selectedRows = tableRef?.current?.grid.api.getSelectedRows();
|
||||
const uniqueIds = selectedRows?.map((row) => row.uniqueId);
|
||||
if (!uniqueIds?.length) return;
|
||||
|
||||
const currentSong = usePlayerStore.getState().current.song;
|
||||
const playerData = removeFromQueue(uniqueIds);
|
||||
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong.uniqueId);
|
||||
const currentSong = usePlayerStore.getState().current.song;
|
||||
const playerData = removeFromQueue(uniqueIds);
|
||||
const isCurrentSongRemoved = currentSong && uniqueIds.includes(currentSong.uniqueId);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (isCurrentSongRemoved) {
|
||||
mpvPlayer.setQueue(playerData);
|
||||
} else {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
if (isCurrentSongRemoved) {
|
||||
mpvPlayer.setQueue(playerData);
|
||||
} else {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleClearQueue = () => {
|
||||
const playerData = clearQueue();
|
||||
const handleClearQueue = () => {
|
||||
const playerData = clearQueue();
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueue(playerData);
|
||||
mpvPlayer.pause();
|
||||
}
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueue(playerData);
|
||||
mpvPlayer.pause();
|
||||
}
|
||||
|
||||
setCurrentTime(0);
|
||||
pause();
|
||||
};
|
||||
setCurrentTime(0);
|
||||
pause();
|
||||
};
|
||||
|
||||
const handleShuffleQueue = () => {
|
||||
const playerData = shuffleQueue();
|
||||
const handleShuffleQueue = () => {
|
||||
const playerData = shuffleQueue();
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Group
|
||||
position="apart"
|
||||
px="1rem"
|
||||
py="1rem"
|
||||
sx={{ alignItems: 'center' }}
|
||||
w="100%"
|
||||
>
|
||||
<Group spacing="sm">
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Shuffle queue' }}
|
||||
variant="default"
|
||||
onClick={handleShuffleQueue}
|
||||
return (
|
||||
<Group
|
||||
position="apart"
|
||||
px="1rem"
|
||||
py="1rem"
|
||||
sx={{ alignItems: 'center' }}
|
||||
w="100%"
|
||||
>
|
||||
<RiShuffleLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Move selected to bottom' }}
|
||||
variant="default"
|
||||
onClick={handleMoveToBottom}
|
||||
>
|
||||
<RiArrowDownLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Move selected to top' }}
|
||||
variant="default"
|
||||
onClick={handleMoveToTop}
|
||||
>
|
||||
<RiArrowUpLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Remove selected' }}
|
||||
variant="default"
|
||||
onClick={handleRemoveSelected}
|
||||
>
|
||||
<RiEraserLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Clear queue' }}
|
||||
variant="default"
|
||||
onClick={handleClearQueue}
|
||||
>
|
||||
<RiDeleteBinLine size="1.1rem" />
|
||||
</Button>
|
||||
</Group>
|
||||
<Group>
|
||||
<Popover
|
||||
position="top-end"
|
||||
transitionProps={{ transition: 'fade' }}
|
||||
>
|
||||
<Popover.Target>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Configure' }}
|
||||
variant="subtle"
|
||||
>
|
||||
<RiListSettingsLine size="1.1rem" />
|
||||
</Button>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
<TableConfigDropdown type={type} />
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
</Group>
|
||||
</Group>
|
||||
);
|
||||
<Group spacing="sm">
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Shuffle queue' }}
|
||||
variant="default"
|
||||
onClick={handleShuffleQueue}
|
||||
>
|
||||
<RiShuffleLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Move selected to bottom' }}
|
||||
variant="default"
|
||||
onClick={handleMoveToBottom}
|
||||
>
|
||||
<RiArrowDownLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Move selected to top' }}
|
||||
variant="default"
|
||||
onClick={handleMoveToTop}
|
||||
>
|
||||
<RiArrowUpLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Remove selected' }}
|
||||
variant="default"
|
||||
onClick={handleRemoveSelected}
|
||||
>
|
||||
<RiEraserLine size="1.1rem" />
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Clear queue' }}
|
||||
variant="default"
|
||||
onClick={handleClearQueue}
|
||||
>
|
||||
<RiDeleteBinLine size="1.1rem" />
|
||||
</Button>
|
||||
</Group>
|
||||
<Group>
|
||||
<Popover
|
||||
position="top-end"
|
||||
transitionProps={{ transition: 'fade' }}
|
||||
>
|
||||
<Popover.Target>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
tooltip={{ label: 'Configure' }}
|
||||
variant="subtle"
|
||||
>
|
||||
<RiListSettingsLine size="1.1rem" />
|
||||
</Button>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
<TableConfigDropdown type={type} />
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
</Group>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
import type { Ref } from 'react';
|
||||
import { useState, forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
|
||||
import type {
|
||||
CellDoubleClickedEvent,
|
||||
RowClassRules,
|
||||
RowDragEvent,
|
||||
RowNode,
|
||||
CellDoubleClickedEvent,
|
||||
RowClassRules,
|
||||
RowDragEvent,
|
||||
RowNode,
|
||||
} from '@ag-grid-community/core';
|
||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||
import '@ag-grid-community/styles/ag-theme-alpine.css';
|
||||
import {
|
||||
useAppStoreActions,
|
||||
useCurrentSong,
|
||||
useDefaultQueue,
|
||||
usePlayerControls,
|
||||
usePreviousSong,
|
||||
useQueueControls,
|
||||
useAppStoreActions,
|
||||
useCurrentSong,
|
||||
useDefaultQueue,
|
||||
usePlayerControls,
|
||||
usePreviousSong,
|
||||
useQueueControls,
|
||||
} from '/@/renderer/store';
|
||||
import {
|
||||
usePlayerType,
|
||||
useSettingsStore,
|
||||
useSettingsStoreActions,
|
||||
useTableSettings,
|
||||
usePlayerType,
|
||||
useSettingsStore,
|
||||
useSettingsStoreActions,
|
||||
useTableSettings,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { useMergedRef } from '@mantine/hooks';
|
||||
import isElectron from 'is-electron';
|
||||
|
|
@ -39,194 +39,200 @@ const utils = isElectron() ? window.electron.utils : null;
|
|||
const mpris = isElectron() && utils?.isLinux() ? window.electron.mpris : null;
|
||||
|
||||
type QueueProps = {
|
||||
type: TableType;
|
||||
type: TableType;
|
||||
};
|
||||
|
||||
export const PlayQueue = forwardRef(({ type }: QueueProps, ref: Ref<any>) => {
|
||||
const tableRef = useRef<AgGridReactType | null>(null);
|
||||
const mergedRef = useMergedRef(ref, tableRef);
|
||||
const queue = useDefaultQueue();
|
||||
const { reorderQueue, setCurrentTrack } = useQueueControls();
|
||||
const currentSong = useCurrentSong();
|
||||
const previousSong = usePreviousSong();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
const { setAppStore } = useAppStoreActions();
|
||||
const tableConfig = useTableSettings(type);
|
||||
const [gridApi, setGridApi] = useState<AgGridReactType | undefined>();
|
||||
const playerType = usePlayerType();
|
||||
const { play } = usePlayerControls();
|
||||
const tableRef = useRef<AgGridReactType | null>(null);
|
||||
const mergedRef = useMergedRef(ref, tableRef);
|
||||
const queue = useDefaultQueue();
|
||||
const { reorderQueue, setCurrentTrack } = useQueueControls();
|
||||
const currentSong = useCurrentSong();
|
||||
const previousSong = usePreviousSong();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
const { setAppStore } = useAppStoreActions();
|
||||
const tableConfig = useTableSettings(type);
|
||||
const [gridApi, setGridApi] = useState<AgGridReactType | undefined>();
|
||||
const playerType = usePlayerType();
|
||||
const { play } = usePlayerControls();
|
||||
|
||||
useEffect(() => {
|
||||
if (tableRef.current) {
|
||||
setGridApi(tableRef.current);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
get grid() {
|
||||
return gridApi;
|
||||
},
|
||||
}));
|
||||
|
||||
const columnDefs = useMemo(() => getColumnDefs(tableConfig.columns), [tableConfig.columns]);
|
||||
|
||||
const handleDoubleClick = (e: CellDoubleClickedEvent) => {
|
||||
const playerData = setCurrentTrack(e.data.uniqueId);
|
||||
mpris?.updateSong({
|
||||
currentTime: 0,
|
||||
song: playerData.current.song,
|
||||
status: 'Playing',
|
||||
});
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueue(playerData);
|
||||
mpvPlayer.play();
|
||||
}
|
||||
|
||||
play();
|
||||
};
|
||||
|
||||
const handleDragStart = () => {
|
||||
if (type === 'sideDrawerQueue') {
|
||||
setAppStore({ isReorderingQueue: true });
|
||||
}
|
||||
};
|
||||
|
||||
let timeout: any;
|
||||
const handleDragEnd = (e: RowDragEvent<QueueSong>) => {
|
||||
if (!e.nodes.length) return;
|
||||
const selectedUniqueIds = e.nodes
|
||||
.map((node) => node.data?.uniqueId)
|
||||
.filter((e) => e !== undefined);
|
||||
|
||||
const playerData = reorderQueue(selectedUniqueIds as string[], e.overNode?.data?.uniqueId);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
|
||||
if (type === 'sideDrawerQueue') {
|
||||
setAppStore({ isReorderingQueue: false });
|
||||
}
|
||||
|
||||
const { api } = tableRef?.current || {};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => api?.redrawRows(), 250);
|
||||
};
|
||||
|
||||
const handleGridReady = () => {
|
||||
const { api } = tableRef?.current || {};
|
||||
|
||||
if (currentSong?.uniqueId) {
|
||||
const currentNode = api?.getRowNode(currentSong?.uniqueId);
|
||||
|
||||
if (!currentNode) return;
|
||||
api?.ensureNodeVisible(currentNode, 'middle');
|
||||
}
|
||||
};
|
||||
|
||||
const handleColumnChange = () => {
|
||||
const { columnApi } = tableRef?.current || {};
|
||||
const columnsOrder = columnApi?.getAllGridColumns();
|
||||
if (!columnsOrder) return;
|
||||
|
||||
const columnsInSettings = useSettingsStore.getState().tables[type].columns;
|
||||
|
||||
const updatedColumns = [];
|
||||
for (const column of columnsOrder) {
|
||||
const columnInSettings = columnsInSettings.find((c) => c.column === column.getColDef().colId);
|
||||
|
||||
if (columnInSettings) {
|
||||
updatedColumns.push({
|
||||
...columnInSettings,
|
||||
...(!useSettingsStore.getState().tables[type].autoFit && {
|
||||
width: column.getActualWidth(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setSettings({
|
||||
tables: {
|
||||
...useSettingsStore.getState().tables,
|
||||
[type]: {
|
||||
...useSettingsStore.getState().tables[type],
|
||||
columns: updatedColumns,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const debouncedColumnChange = debounce(handleColumnChange, 250);
|
||||
|
||||
const handleGridSizeChange = () => {
|
||||
if (tableConfig.autoFit) {
|
||||
tableRef?.current?.api.sizeColumnsToFit();
|
||||
}
|
||||
};
|
||||
|
||||
const rowClassRules = useMemo<RowClassRules | undefined>(() => {
|
||||
return {
|
||||
'current-song': (params) => {
|
||||
return params.data.uniqueId === currentSong?.uniqueId;
|
||||
},
|
||||
};
|
||||
}, [currentSong?.uniqueId]);
|
||||
|
||||
// Redraw the current song row when the previous song changes
|
||||
useEffect(() => {
|
||||
if (tableRef?.current) {
|
||||
const { api, columnApi } = tableRef?.current || {};
|
||||
if (api == null || columnApi == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentNode = currentSong?.uniqueId ? api.getRowNode(currentSong.uniqueId) : undefined;
|
||||
const previousNode = previousSong?.uniqueId
|
||||
? api.getRowNode(previousSong?.uniqueId)
|
||||
: undefined;
|
||||
|
||||
const rowNodes = [currentNode, previousNode].filter((e) => e !== undefined) as RowNode<any>[];
|
||||
|
||||
if (rowNodes) {
|
||||
api.redrawRows({ rowNodes });
|
||||
if (tableConfig.followCurrentSong) {
|
||||
if (!currentNode) return;
|
||||
api.ensureNodeVisible(currentNode, 'middle');
|
||||
useEffect(() => {
|
||||
if (tableRef.current) {
|
||||
setGridApi(tableRef.current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [currentSong, previousSong, tableConfig.followCurrentSong]);
|
||||
}, []);
|
||||
|
||||
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, QUEUE_CONTEXT_MENU_ITEMS);
|
||||
useImperativeHandle(ref, () => ({
|
||||
get grid() {
|
||||
return gridApi;
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
<VirtualGridAutoSizerContainer>
|
||||
<VirtualTable
|
||||
ref={mergedRef}
|
||||
alwaysShowHorizontalScroll
|
||||
rowDragEntireRow
|
||||
rowDragMultiRow
|
||||
autoFitColumns={tableConfig.autoFit}
|
||||
columnDefs={columnDefs}
|
||||
deselectOnClickOutside={type === 'fullScreen'}
|
||||
getRowId={(data) => data.data.uniqueId}
|
||||
rowBuffer={50}
|
||||
rowClassRules={rowClassRules}
|
||||
rowData={queue}
|
||||
rowHeight={tableConfig.rowHeight || 40}
|
||||
suppressCellFocus={type === 'fullScreen'}
|
||||
onCellContextMenu={handleContextMenu}
|
||||
onCellDoubleClicked={handleDoubleClick}
|
||||
onColumnMoved={handleColumnChange}
|
||||
onColumnResized={debouncedColumnChange}
|
||||
onDragStarted={handleDragStart}
|
||||
onGridReady={handleGridReady}
|
||||
onGridSizeChanged={handleGridSizeChange}
|
||||
onRowDragEnd={handleDragEnd}
|
||||
/>
|
||||
</VirtualGridAutoSizerContainer>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
const columnDefs = useMemo(() => getColumnDefs(tableConfig.columns), [tableConfig.columns]);
|
||||
|
||||
const handleDoubleClick = (e: CellDoubleClickedEvent) => {
|
||||
const playerData = setCurrentTrack(e.data.uniqueId);
|
||||
mpris?.updateSong({
|
||||
currentTime: 0,
|
||||
song: playerData.current.song,
|
||||
status: 'Playing',
|
||||
});
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueue(playerData);
|
||||
mpvPlayer.play();
|
||||
}
|
||||
|
||||
play();
|
||||
};
|
||||
|
||||
const handleDragStart = () => {
|
||||
if (type === 'sideDrawerQueue') {
|
||||
setAppStore({ isReorderingQueue: true });
|
||||
}
|
||||
};
|
||||
|
||||
let timeout: any;
|
||||
const handleDragEnd = (e: RowDragEvent<QueueSong>) => {
|
||||
if (!e.nodes.length) return;
|
||||
const selectedUniqueIds = e.nodes
|
||||
.map((node) => node.data?.uniqueId)
|
||||
.filter((e) => e !== undefined);
|
||||
|
||||
const playerData = reorderQueue(selectedUniqueIds as string[], e.overNode?.data?.uniqueId);
|
||||
|
||||
if (playerType === PlaybackType.LOCAL) {
|
||||
mpvPlayer.setQueueNext(playerData);
|
||||
}
|
||||
|
||||
if (type === 'sideDrawerQueue') {
|
||||
setAppStore({ isReorderingQueue: false });
|
||||
}
|
||||
|
||||
const { api } = tableRef?.current || {};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => api?.redrawRows(), 250);
|
||||
};
|
||||
|
||||
const handleGridReady = () => {
|
||||
const { api } = tableRef?.current || {};
|
||||
|
||||
if (currentSong?.uniqueId) {
|
||||
const currentNode = api?.getRowNode(currentSong?.uniqueId);
|
||||
|
||||
if (!currentNode) return;
|
||||
api?.ensureNodeVisible(currentNode, 'middle');
|
||||
}
|
||||
};
|
||||
|
||||
const handleColumnChange = () => {
|
||||
const { columnApi } = tableRef?.current || {};
|
||||
const columnsOrder = columnApi?.getAllGridColumns();
|
||||
if (!columnsOrder) return;
|
||||
|
||||
const columnsInSettings = useSettingsStore.getState().tables[type].columns;
|
||||
|
||||
const updatedColumns = [];
|
||||
for (const column of columnsOrder) {
|
||||
const columnInSettings = columnsInSettings.find(
|
||||
(c) => c.column === column.getColDef().colId,
|
||||
);
|
||||
|
||||
if (columnInSettings) {
|
||||
updatedColumns.push({
|
||||
...columnInSettings,
|
||||
...(!useSettingsStore.getState().tables[type].autoFit && {
|
||||
width: column.getActualWidth(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setSettings({
|
||||
tables: {
|
||||
...useSettingsStore.getState().tables,
|
||||
[type]: {
|
||||
...useSettingsStore.getState().tables[type],
|
||||
columns: updatedColumns,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const debouncedColumnChange = debounce(handleColumnChange, 250);
|
||||
|
||||
const handleGridSizeChange = () => {
|
||||
if (tableConfig.autoFit) {
|
||||
tableRef?.current?.api.sizeColumnsToFit();
|
||||
}
|
||||
};
|
||||
|
||||
const rowClassRules = useMemo<RowClassRules | undefined>(() => {
|
||||
return {
|
||||
'current-song': (params) => {
|
||||
return params.data.uniqueId === currentSong?.uniqueId;
|
||||
},
|
||||
};
|
||||
}, [currentSong?.uniqueId]);
|
||||
|
||||
// Redraw the current song row when the previous song changes
|
||||
useEffect(() => {
|
||||
if (tableRef?.current) {
|
||||
const { api, columnApi } = tableRef?.current || {};
|
||||
if (api == null || columnApi == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentNode = currentSong?.uniqueId
|
||||
? api.getRowNode(currentSong.uniqueId)
|
||||
: undefined;
|
||||
const previousNode = previousSong?.uniqueId
|
||||
? api.getRowNode(previousSong?.uniqueId)
|
||||
: undefined;
|
||||
|
||||
const rowNodes = [currentNode, previousNode].filter(
|
||||
(e) => e !== undefined,
|
||||
) as RowNode<any>[];
|
||||
|
||||
if (rowNodes) {
|
||||
api.redrawRows({ rowNodes });
|
||||
if (tableConfig.followCurrentSong) {
|
||||
if (!currentNode) return;
|
||||
api.ensureNodeVisible(currentNode, 'middle');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [currentSong, previousSong, tableConfig.followCurrentSong]);
|
||||
|
||||
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, QUEUE_CONTEXT_MENU_ITEMS);
|
||||
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
<VirtualGridAutoSizerContainer>
|
||||
<VirtualTable
|
||||
ref={mergedRef}
|
||||
alwaysShowHorizontalScroll
|
||||
rowDragEntireRow
|
||||
rowDragMultiRow
|
||||
autoFitColumns={tableConfig.autoFit}
|
||||
columnDefs={columnDefs}
|
||||
deselectOnClickOutside={type === 'fullScreen'}
|
||||
getRowId={(data) => data.data.uniqueId}
|
||||
rowBuffer={50}
|
||||
rowClassRules={rowClassRules}
|
||||
rowData={queue}
|
||||
rowHeight={tableConfig.rowHeight || 40}
|
||||
suppressCellFocus={type === 'fullScreen'}
|
||||
onCellContextMenu={handleContextMenu}
|
||||
onCellDoubleClicked={handleDoubleClick}
|
||||
onColumnMoved={handleColumnChange}
|
||||
onColumnResized={debouncedColumnChange}
|
||||
onDragStarted={handleDragStart}
|
||||
onGridReady={handleGridReady}
|
||||
onGridSizeChanged={handleGridSizeChange}
|
||||
onRowDragEnd={handleDragEnd}
|
||||
/>
|
||||
</VirtualGridAutoSizerContainer>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,30 +10,30 @@ import { Platform } from '/@/renderer/types';
|
|||
import { VirtualGridContainer } from '/@/renderer/components/virtual-grid';
|
||||
|
||||
export const SidebarPlayQueue = () => {
|
||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
|
||||
const isWeb = windowBarStyle === Platform.WEB;
|
||||
return (
|
||||
<VirtualGridContainer>
|
||||
{isWeb && (
|
||||
<Stack mr={isWeb ? '130px' : undefined}>
|
||||
<PageHeader backgroundColor="var(--titlebar-bg)" />
|
||||
</Stack>
|
||||
)}
|
||||
<Paper
|
||||
display={!isWeb ? 'flex' : undefined}
|
||||
h={!isWeb ? '65px' : undefined}
|
||||
>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</Paper>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</VirtualGridContainer>
|
||||
);
|
||||
const isWeb = windowBarStyle === Platform.WEB;
|
||||
return (
|
||||
<VirtualGridContainer>
|
||||
{isWeb && (
|
||||
<Stack mr={isWeb ? '130px' : undefined}>
|
||||
<PageHeader backgroundColor="var(--titlebar-bg)" />
|
||||
</Stack>
|
||||
)}
|
||||
<Paper
|
||||
display={!isWeb ? 'flex' : undefined}
|
||||
h={!isWeb ? '65px' : undefined}
|
||||
>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</Paper>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="sideQueue"
|
||||
/>
|
||||
</VirtualGridContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,25 +9,25 @@ import { PlayQueueListControls } from '/@/renderer/features/now-playing/componen
|
|||
import { VirtualGridContainer } from '/@/renderer/components/virtual-grid';
|
||||
|
||||
const NowPlayingRoute = () => {
|
||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||
const queueRef = useRef<{ grid: AgGridReactType<Song> } | null>(null);
|
||||
|
||||
return (
|
||||
<AnimatedPage>
|
||||
<VirtualGridContainer>
|
||||
<NowPlayingHeader />
|
||||
<Paper sx={{ borderTop: '1px solid var(--generic-border-color)' }}>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="nowPlaying"
|
||||
/>
|
||||
</Paper>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="nowPlaying"
|
||||
/>
|
||||
</VirtualGridContainer>
|
||||
</AnimatedPage>
|
||||
);
|
||||
return (
|
||||
<AnimatedPage>
|
||||
<VirtualGridContainer>
|
||||
<NowPlayingHeader />
|
||||
<Paper sx={{ borderTop: '1px solid var(--generic-border-color)' }}>
|
||||
<PlayQueueListControls
|
||||
tableRef={queueRef}
|
||||
type="nowPlaying"
|
||||
/>
|
||||
</Paper>
|
||||
<PlayQueue
|
||||
ref={queueRef}
|
||||
type="nowPlaying"
|
||||
/>
|
||||
</VirtualGridContainer>
|
||||
</AnimatedPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default NowPlayingRoute;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue