diff --git a/src/renderer/components/card/card-controls.module.css b/src/renderer/components/card/card-controls.module.css deleted file mode 100644 index 6701a475f..000000000 --- a/src/renderer/components/card/card-controls.module.css +++ /dev/null @@ -1,71 +0,0 @@ -.play-button { - display: flex; - align-items: center; - justify-content: center; - width: 50px; - height: 50px; - background-color: rgb(255 255 255); - border: none; - border-radius: 50%; - opacity: 0.8; - transition: opacity 0.2s ease-in-out; - transition: scale 0.2s linear; - - &:hover { - opacity: 1; - scale: 1.1; - } - - &:active { - opacity: 1; - scale: 1; - } - - svg { - fill: rgb(0 0 0); - stroke: rgb(0 0 0); - } -} - -.secondary-button { - opacity: 0.8; - transition: opacity 0.2s ease-in-out; - transition: scale 0.2s linear; - - &:hover { - opacity: 1; - scale: 1.1; - } - - &:active { - opacity: 1; - scale: 1; - } -} - -.grid-card-controls-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; -} - -.controls-row { - width: 100%; - height: calc(100% / 3); -} - -.bottom-controls { - display: flex; - align-items: flex-end; - justify-content: space-between; - padding: 1rem 0.5rem; -} - -.favorite-wrapper { - svg { - fill: var(--theme-colors-primary-filled); - } -} diff --git a/src/renderer/components/card/card-controls.tsx b/src/renderer/components/card/card-controls.tsx deleted file mode 100644 index c01ac30da..000000000 --- a/src/renderer/components/card/card-controls.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import type { PlayQueueAddOptions } from '/@/shared/types/types'; -import type { MouseEvent } from 'react'; - -import styles from './card-controls.module.css'; - -import { - ALBUM_CONTEXT_MENU_ITEMS, - ARTIST_CONTEXT_MENU_ITEMS, -} from '/@/renderer/features/context-menu/context-menu-items'; -import { useHandleGeneralContextMenu } from '/@/renderer/features/context-menu/hooks/use-handle-context-menu'; -import { usePlayButtonBehavior } from '/@/renderer/store/settings.store'; -import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; -import { Button } from '/@/shared/components/button/button'; -import { Group } from '/@/shared/components/group/group'; -import { Icon } from '/@/shared/components/icon/icon'; -import { LibraryItem } from '/@/shared/types/domain-types'; -import { Play } from '/@/shared/types/types'; - -export const CardControls = ({ - handlePlayQueueAdd, - itemData, - itemType, -}: { - handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void; - itemData: any; - itemType: LibraryItem; -}) => { - const playButtonBehavior = usePlayButtonBehavior(); - - const handlePlay = (e: MouseEvent, playType?: Play) => { - e.preventDefault(); - e.stopPropagation(); - handlePlayQueueAdd?.({ - byItemType: { - id: [itemData.id], - type: itemType, - }, - playType: playType || playButtonBehavior, - }); - }; - - const handleContextMenu = useHandleGeneralContextMenu( - itemType, - itemType === LibraryItem.ALBUM ? ALBUM_CONTEXT_MENU_ITEMS : ARTIST_CONTEXT_MENU_ITEMS, - ); - - return ( -
-
- - - - { - e.preventDefault(); - e.stopPropagation(); - handleContextMenu(e, [itemData]); - }} - p={5} - style={{ svg: { fill: 'white !important' } }} - variant="subtle" - > - - - -
-
- ); -}; diff --git a/src/renderer/components/card/card-rows.module.css b/src/renderer/components/card/card-rows.module.css deleted file mode 100644 index 525472514..000000000 --- a/src/renderer/components/card/card-rows.module.css +++ /dev/null @@ -1,15 +0,0 @@ -.row { - width: 100%; - max-width: 100%; - height: 22px; - padding: 0 0.2rem; - overflow: hidden; - text-overflow: ellipsis; - color: var(--theme-colors-foreground); - white-space: nowrap; - user-select: none; -} - -.row.secondary { - color: var(--theme-colors-foreground-muted); -} diff --git a/src/renderer/components/card/card-rows.tsx b/src/renderer/components/card/card-rows.tsx deleted file mode 100644 index c04eefd57..000000000 --- a/src/renderer/components/card/card-rows.tsx +++ /dev/null @@ -1,350 +0,0 @@ -import clsx from 'clsx'; -import formatDuration from 'format-duration'; -import React from 'react'; -import { TFunction, useTranslation } from 'react-i18next'; -import { generatePath } from 'react-router'; -import { Link } from 'react-router'; - -import styles from './card-rows.module.css'; - -import { AppRoute } from '/@/renderer/router/routes'; -import { formatDateAbsolute, formatDateRelative, formatRating } from '/@/renderer/utils/format'; -import { Text } from '/@/shared/components/text/text'; -import { - Album, - AlbumArtist, - Artist, - ExplicitStatus, - Playlist, - Song, -} from '/@/shared/types/domain-types'; -import { CardRow } from '/@/shared/types/types'; - -interface CardRowsProps { - data: any; - rows: CardRow[] | CardRow[] | CardRow[]; -} - -export const CardRows = ({ data, rows }: CardRowsProps) => { - const { t } = useTranslation(); - - return ( - <> - {rows.map((row, index: number) => { - if (row.arrayProperty && row.route) { - return ( -
0, - })} - key={`row-${row.property}-${index}`} - > - {data[row.property].map((item: any, itemIndex: number) => ( - - {itemIndex > 0 && ( - - , - - )}{' '} - 0} - isNoSelect - onClick={(e) => e.stopPropagation()} - overflow="hidden" - size={index > 0 ? 'sm' : 'md'} - to={generatePath( - row.route!.route, - row.route!.slugs?.reduce((acc, slug) => { - return { - ...acc, - [slug.slugProperty]: - data[row.property][itemIndex][ - slug.idProperty - ], - }; - }, {}), - )} - > - {row.arrayProperty && - (row.format - ? row.format(item, t) - : item[row.arrayProperty])} - - - ))} -
- ); - } - - if (row.arrayProperty) { - return ( -
0, - })} - key={`row-${row.property}`} - > - {data[row.property].map((item: any) => ( - 0} - isNoSelect - key={`${data.id}-${item.id}`} - overflow="hidden" - size={index > 0 ? 'sm' : 'md'} - > - {row.arrayProperty && - (row.format - ? row.format(item, t) - : item[row.arrayProperty])} - - ))} -
- ); - } - - return ( -
0, - })} - key={`row-${row.property}`} - > - {row.route ? ( - e.stopPropagation()} - overflow="hidden" - to={generatePath( - row.route.route, - row.route.slugs?.reduce((acc, slug) => { - return { - ...acc, - [slug.slugProperty]: data[slug.idProperty], - }; - }, {}), - )} - > - {data && (row.format ? row.format(data, t) : data[row.property])} - - ) : ( - 0} - isNoSelect - overflow="hidden" - size={index > 0 ? 'sm' : 'md'} - > - {data && (row.format ? row.format(data, t) : data[row.property])} - - )} -
- ); - })} - - ); -}; - -export const ALBUM_CARD_ROWS: { [key: string]: CardRow } = { - albumArtists: { - arrayProperty: 'name', - property: 'albumArtists', - route: { - route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }], - }, - }, - artists: { - arrayProperty: 'name', - property: 'artists', - route: { - route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }], - }, - }, - createdAt: { - format: (song) => formatDateAbsolute(song.createdAt), - property: 'createdAt', - }, - duration: { - format: (album) => (album.duration === null ? null : formatDuration(album.duration)), - property: 'duration', - }, - explicitStatus: { - format: (album, t: TFunction) => - album.explicitStatus === ExplicitStatus.EXPLICIT - ? t('common.explicit', { postProcess: 'sentenceCase' }) - : album.explicitStatus === ExplicitStatus.CLEAN - ? t('common.clean', { postProcess: 'sentenceCase' }) - : null, - property: 'explicitStatus', - }, - lastPlayedAt: { - format: (album) => formatDateRelative(album.lastPlayedAt), - property: 'lastPlayedAt', - }, - name: { - property: 'name', - route: { - route: AppRoute.LIBRARY_ALBUMS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumId' }], - }, - }, - playCount: { - property: 'playCount', - }, - rating: { - format: (album) => formatRating(album), - property: 'userRating', - }, - releaseDate: { - property: 'releaseDate', - }, - releaseYear: { - property: 'releaseYear', - }, - songCount: { - property: 'songCount', - }, -}; - -export const SONG_CARD_ROWS: { [key: string]: CardRow } = { - album: { - property: 'album', - route: { - route: AppRoute.LIBRARY_ALBUMS_DETAIL, - slugs: [{ idProperty: 'albumId', slugProperty: 'albumId' }], - }, - }, - albumArtists: { - arrayProperty: 'name', - property: 'albumArtists', - route: { - route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }], - }, - }, - artists: { - arrayProperty: 'name', - property: 'artists', - route: { - route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }], - }, - }, - createdAt: { - format: (song) => formatDateAbsolute(song.createdAt), - property: 'createdAt', - }, - duration: { - format: (song) => (song.duration === null ? null : formatDuration(song.duration)), - property: 'duration', - }, - explicitStatus: { - format: (song, t: TFunction) => - song.explicitStatus === ExplicitStatus.EXPLICIT - ? t('common.explicit', { postProcess: 'sentenceCase' }) - : song.explicitStatus === ExplicitStatus.CLEAN - ? t('common.clean', { postProcess: 'sentenceCase' }) - : null, - property: 'explicitStatus', - }, - lastPlayedAt: { - format: (song) => formatDateRelative(song.lastPlayedAt), - property: 'lastPlayedAt', - }, - name: { - property: 'name', - route: { - route: AppRoute.LIBRARY_ALBUMS_DETAIL, - slugs: [{ idProperty: 'albumId', slugProperty: 'albumId' }], - }, - }, - playCount: { - property: 'playCount', - }, - rating: { - format: (song) => formatRating(song), - property: 'userRating', - }, - releaseDate: { - property: 'releaseDate', - }, - releaseYear: { - property: 'releaseYear', - }, -}; - -export const ALBUMARTIST_CARD_ROWS: { [key: string]: CardRow } = { - albumCount: { - property: 'albumCount', - }, - duration: { - format: (artist) => (artist.duration === null ? null : formatDuration(artist.duration)), - property: 'duration', - }, - genres: { - property: 'genres', - }, - lastPlayedAt: { - format: (artist) => formatDateRelative(artist.lastPlayedAt), - property: 'lastPlayedAt', - }, - name: { - property: 'name', - route: { - route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, - slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }], - }, - }, - playCount: { - property: 'playCount', - }, - rating: { - format: (artist) => formatRating(artist), - property: 'userRating', - }, - songCount: { - property: 'songCount', - }, -}; - -export const PLAYLIST_CARD_ROWS: { [key: string]: CardRow } = { - duration: { - format: (playlist) => - playlist.duration === null ? null : formatDuration(playlist.duration), - property: 'duration', - }, - name: { - property: 'name', - route: { - route: AppRoute.PLAYLISTS_DETAIL_SONGS, - slugs: [{ idProperty: 'id', slugProperty: 'playlistId' }], - }, - }, - nameFull: { - property: 'name', - route: { - route: AppRoute.PLAYLISTS_DETAIL_SONGS, - slugs: [{ idProperty: 'id', slugProperty: 'playlistId' }], - }, - }, - owner: { - property: 'owner', - }, - public: { - property: 'public', - }, - songCount: { - property: 'songCount', - }, -}; diff --git a/src/renderer/components/card/poster-card.module.css b/src/renderer/components/card/poster-card.module.css deleted file mode 100644 index 224e72cf0..000000000 --- a/src/renderer/components/card/poster-card.module.css +++ /dev/null @@ -1,67 +0,0 @@ -.container { - display: flex; - flex-direction: column; - width: 100%; - height: 100%; - overflow: hidden; - pointer-events: auto; - - &:global(.card-controls) { - opacity: 0; - } -} - -.container.hidden { - opacity: 0; -} - -.image-container { - position: relative; - display: flex; - align-items: center; - aspect-ratio: 1/1; - overflow: hidden; - background: var(--theme-card-default-bg); - border-radius: var(--theme-card-poster-radius); - - &::before { - position: absolute; - top: 0; - left: 0; - z-index: 1; - width: 100%; - height: 100%; - user-select: none; - content: ''; - background: linear-gradient(0deg, rgb(0 0 0 / 100%) 35%, rgb(0 0 0 / 0%) 100%); - opacity: 0; - transition: all 0.2s ease-in-out; - } - - &:hover { - &::before { - opacity: 0.5; - } - } - - &:hover:global(.card-controls) { - opacity: 1; - } -} - -.image { - width: 100%; - max-width: 100%; - height: 100% !important; - max-height: 100%; - border: 0; - - img { - height: 100%; - object-fit: var(--theme-image-fit); - } -} - -.detail-container { - margin-top: 0.5rem; -} diff --git a/src/renderer/components/card/poster-card.tsx b/src/renderer/components/card/poster-card.tsx deleted file mode 100644 index 67d4351c8..000000000 --- a/src/renderer/components/card/poster-card.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { useState } from 'react'; -import { generatePath, Link } from 'react-router'; - -import styles from './poster-card.module.css'; - -import { CardRows } from '/@/renderer/components/card/card-rows'; -import { Image } from '/@/shared/components/image/image'; -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Stack } from '/@/shared/components/stack/stack'; -import { Album, AlbumArtist, Artist, LibraryItem } from '/@/shared/types/domain-types'; -import { CardRoute, CardRow, Play, PlayQueueAddOptions } from '/@/shared/types/types'; - -interface BaseGridCardProps { - controls: { - cardRows: CardRow[] | CardRow[] | CardRow[]; - handleFavorite: (options: { - id: string[]; - isFavorite: boolean; - itemType: LibraryItem; - serverId: string; - }) => void; - handlePlayQueueAdd: ((options: PlayQueueAddOptions) => void) | undefined; - itemType: LibraryItem; - playButtonBehavior: Play; - route: CardRoute; - }; - data: any; - isLoading?: boolean; -} - -export const PosterCard = ({ - controls, - data, - isLoading, - uniqueId, -}: BaseGridCardProps & { uniqueId: string }) => { - const [isHovered, setIsHovered] = useState(false); - - if (!isLoading) { - const path = generatePath( - controls.route.route as string, - controls.route.slugs?.reduce((acc, slug) => { - return { - ...acc, - [slug.slugProperty]: data[slug.idProperty], - }; - }, {}), - ); - - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > - - - {/* */} - -
- -
-
- ); - } - - return ( -
-
- -
-
- - {(controls?.cardRows || []).map((row, index) => ( - - ))} - -
-
- ); -}; diff --git a/src/renderer/components/virtual-table/cells/actions-cell.tsx b/src/renderer/components/virtual-table/cells/actions-cell.tsx deleted file mode 100644 index fabb3239a..000000000 --- a/src/renderer/components/virtual-table/cells/actions-cell.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; - -export const ActionsCell = ({ api, context }: ICellRendererParams) => { - return ( - - { - e.stopPropagation(); - e.preventDefault(); - context.onCellContextMenu(undefined, api, e); - }} - size="sm" - variant="subtle" - /> - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/album-artist-cell.tsx b/src/renderer/components/virtual-table/cells/album-artist-cell.tsx deleted file mode 100644 index 179760c97..000000000 --- a/src/renderer/components/virtual-table/cells/album-artist-cell.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { AlbumArtist, Artist } from '/@/shared/types/domain-types'; -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import React from 'react'; -import { generatePath } from 'react-router'; -import { Link } from 'react-router'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { AppRoute } from '/@/renderer/router/routes'; -import { Separator } from '/@/shared/components/separator/separator'; -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Text } from '/@/shared/components/text/text'; - -export const AlbumArtistCell = ({ data, value }: ICellRendererParams) => { - if (value === undefined) { - return ( - - - - ); - } - - return ( - - - {value?.map((item: AlbumArtist | Artist, index: number) => ( - - {index > 0 && } - {item.id ? ( - - {item.name || '—'} - - ) : ( - - {item.name || '—'} - - )} - - ))} - - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/artist-cell.tsx b/src/renderer/components/virtual-table/cells/artist-cell.tsx deleted file mode 100644 index 982ebb79e..000000000 --- a/src/renderer/components/virtual-table/cells/artist-cell.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { AlbumArtist, Artist } from '/@/shared/types/domain-types'; -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import React from 'react'; -import { generatePath } from 'react-router'; -import { Link } from 'react-router'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { AppRoute } from '/@/renderer/router/routes'; -import { Separator } from '/@/shared/components/separator/separator'; -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Text } from '/@/shared/components/text/text'; - -export const ArtistCell = ({ data, value }: ICellRendererParams) => { - if (value === undefined) { - return ( - - - - ); - } - - return ( - - - {value?.map((item: AlbumArtist | Artist, index: number) => ( - - {index > 0 && } - {item.id ? ( - - {item.name || '—'} - - ) : ( - - {item.name || '—'} - - )} - - ))} - - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/combined-title-cell-controls.module.css b/src/renderer/components/virtual-table/cells/combined-title-cell-controls.module.css deleted file mode 100644 index 8b5071ba0..000000000 --- a/src/renderer/components/virtual-table/cells/combined-title-cell-controls.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.play-button { - position: absolute; - width: 30px; - height: 30px; - background: var(--theme-colors-white); - border: none; - border-radius: 50%; - opacity: 0.7; - transition: scale 0.1s ease-in-out; - - svg { - color: var(--theme-colors-black); - fill: var(--theme-colors-black); - } - - &:hover { - background-color: var(--theme-colors-white); - opacity: 1; - } - - &:active { - opacity: 1; - } -} - -.list-controls-container { - position: absolute; - z-index: 100; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; -} diff --git a/src/renderer/components/virtual-table/cells/combined-title-cell-controls.tsx b/src/renderer/components/virtual-table/cells/combined-title-cell-controls.tsx deleted file mode 100644 index 6cf373de0..000000000 --- a/src/renderer/components/virtual-table/cells/combined-title-cell-controls.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import clsx from 'clsx'; - -import styles from './combined-title-cell-controls.module.css'; - -import { usePlayQueueAdd } from '/@/renderer/features/player/hooks/use-playqueue-add'; -import { usePlayButtonBehavior } from '/@/renderer/store/settings.store'; -import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; -import { LibraryItem } from '/@/shared/types/domain-types'; -import { Play } from '/@/shared/types/types'; - -export const ListCoverControls = ({ - className, - context, - itemData, - itemType, - uniqueId, -}: { - className?: string; - context: Record; - itemData: any; - itemType: LibraryItem; - uniqueId?: string; -}) => { - const playButtonBehavior = usePlayButtonBehavior(); - const handlePlayQueueAdd = usePlayQueueAdd(); - const isQueue = Boolean(context?.isQueue); - - const handlePlay = async (e: React.MouseEvent, playType?: Play) => { - e.preventDefault(); - e.stopPropagation(); - - handlePlayQueueAdd?.({ - byItemType: { - id: [itemData.id], - type: itemType, - }, - playType: playType || playButtonBehavior, - }); - }; - - const handlePlayFromQueue = () => { - context.handleDoubleClick({ - data: { - uniqueId, - }, - }); - }; - - return ( -
- -
- ); -}; diff --git a/src/renderer/components/virtual-table/cells/combined-title-cell.module.css b/src/renderer/components/virtual-table/cells/combined-title-cell.module.css deleted file mode 100644 index 08eba185f..000000000 --- a/src/renderer/components/virtual-table/cells/combined-title-cell.module.css +++ /dev/null @@ -1,50 +0,0 @@ -.cell-container { - display: grid; - grid-template-areas: 'image info'; - grid-template-rows: 1fr; - grid-auto-columns: 1fr; - gap: 0.5rem; - align-items: center; - justify-items: center; - width: 100%; - max-width: 100%; - height: 100%; - letter-spacing: 0.5px; - - &:hover { - .play-button { - opacity: 1; - } - } -} - -.play-button { - opacity: 0; -} - -.image-wrapper { - position: relative; - display: flex; - grid-area: image; - align-items: center; - justify-content: center; - height: 100%; -} - -.metadata-wrapper { - display: flex; - flex-direction: column; - grid-area: info; - justify-content: center; - width: 100%; -} - -.metadata-wrapper > div { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.skeleton-metadata { - height: var(--theme-font-size-md); -} diff --git a/src/renderer/components/virtual-table/cells/combined-title-cell.tsx b/src/renderer/components/virtual-table/cells/combined-title-cell.tsx deleted file mode 100644 index 2e536c554..000000000 --- a/src/renderer/components/virtual-table/cells/combined-title-cell.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import React, { useMemo } from 'react'; -import { generatePath } from 'react-router'; -import { Link } from 'react-router'; - -import styles from './combined-title-cell.module.css'; - -import { ListCoverControls } from '/@/renderer/components/virtual-table/cells/combined-title-cell-controls'; -import { AppRoute } from '/@/renderer/router/routes'; -import { SEPARATOR_STRING } from '/@/shared/api/utils'; -import { Image } from '/@/shared/components/image/image'; -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Text } from '/@/shared/components/text/text'; -import { AlbumArtist, Artist } from '/@/shared/types/domain-types'; - -export const CombinedTitleCell = ({ - context, - data, - node, - rowIndex, - value, -}: ICellRendererParams) => { - const artists = useMemo(() => { - if (!value) return null; - return value.artists?.length ? value.artists : value.albumArtists; - }, [value]); - - if (value === undefined) { - return ( -
-
- -
- -
- ); - } - - return ( -
-
- cover - - -
-
- - {value.name} - - - {artists?.length ? ( - artists.map((artist: AlbumArtist | Artist, index: number) => ( - - {index > 0 ? SEPARATOR_STRING : null} - {artist.id ? ( - - {artist.name} - - ) : ( - - {artist.name} - - )} - - )) - ) : ( - - )} - -
-
- ); -}; diff --git a/src/renderer/components/virtual-table/cells/favorite-cell.tsx b/src/renderer/components/virtual-table/cells/favorite-cell.tsx deleted file mode 100644 index 6213d3b15..000000000 --- a/src/renderer/components/virtual-table/cells/favorite-cell.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation'; -import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation'; -import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; - -export const FavoriteCell = ({ data, node, value }: ICellRendererParams) => { - const createMutation = useCreateFavorite({}); - const deleteMutation = useDeleteFavorite({}); - - const handleToggleFavorite = () => { - const newFavoriteValue = !value; - - if (newFavoriteValue) { - createMutation.mutate( - { - apiClientProps: { serverId: data.serverId }, - query: { - id: [data.id], - type: data.itemType, - }, - }, - { - onSuccess: () => { - node.setData({ ...data, userFavorite: newFavoriteValue }); - }, - }, - ); - } else { - deleteMutation.mutate( - { - apiClientProps: { serverId: data.serverId }, - query: { - id: [data.id], - type: data.itemType, - }, - }, - { - onSuccess: () => { - node.setData({ ...data, userFavorite: newFavoriteValue }); - }, - }, - ); - } - }; - - return ( - - - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/full-width-disc-cell.module.css b/src/renderer/components/virtual-table/cells/full-width-disc-cell.module.css deleted file mode 100644 index eeb8914ba..000000000 --- a/src/renderer/components/virtual-table/cells/full-width-disc-cell.module.css +++ /dev/null @@ -1,6 +0,0 @@ -.container { - display: flex; - height: 100%; - padding: 0.5rem 1rem; - border: 1px solid transparent; -} diff --git a/src/renderer/components/virtual-table/cells/full-width-disc-cell.tsx b/src/renderer/components/virtual-table/cells/full-width-disc-cell.tsx deleted file mode 100644 index 0a64c03db..000000000 --- a/src/renderer/components/virtual-table/cells/full-width-disc-cell.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { ICellRendererParams } from '@ag-grid-community/core'; -import { useState } from 'react'; - -import styles from './full-width-disc-cell.module.css'; - -import { getNodesByDiscNumber, setNodeSelection } from '/@/renderer/components/virtual-table/utils'; -import { Button } from '/@/shared/components/button/button'; -import { Group } from '/@/shared/components/group/group'; -import { Icon } from '/@/shared/components/icon/icon'; - -export const FullWidthDiscCell = ({ api, data, node }: ICellRendererParams) => { - const [isSelected, setIsSelected] = useState(false); - - const handleToggleDiscNodes = () => { - if (!data) return; - const split: string[] = node.data.id.split('-'); - const discNumber = Number(split[1]); - // the subtitle could have '-' in it; make sure to have all remaining items - const subtitle = split.length === 3 ? split.slice(2).join('-') : null; - const nodes = getNodesByDiscNumber({ api, discNumber, subtitle }); - - setNodeSelection({ isSelected: !isSelected, nodes }); - setIsSelected((prev) => !prev); - }; - - return ( -
- - - -
- ); -}; diff --git a/src/renderer/components/virtual-table/cells/generic-cell.module.css b/src/renderer/components/virtual-table/cells/generic-cell.module.css deleted file mode 100644 index 3e983db86..000000000 --- a/src/renderer/components/virtual-table/cells/generic-cell.module.css +++ /dev/null @@ -1,19 +0,0 @@ -.cell-container { - display: flex; - align-items: center; - width: 100%; - height: 100%; - letter-spacing: 0.5px; -} - -.cell-container.right { - justify-content: flex-end; -} - -.cell-container.center { - justify-content: center; -} - -.cell-container.left { - justify-content: flex-start; -} diff --git a/src/renderer/components/virtual-table/cells/generic-cell.tsx b/src/renderer/components/virtual-table/cells/generic-cell.tsx deleted file mode 100644 index c014380b0..000000000 --- a/src/renderer/components/virtual-table/cells/generic-cell.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import clsx from 'clsx'; -import { Link } from 'react-router'; - -import styles from './generic-cell.module.css'; - -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Text } from '/@/shared/components/text/text'; - -type Options = { - array?: boolean; - isArray?: boolean; - isLink?: boolean; - position?: 'center' | 'left' | 'right'; - primary?: boolean; -}; - -export const GenericCell = ({ value, valueFormatted }: ICellRendererParams, options?: Options) => { - const { isLink, position, primary } = options || {}; - const displayedValue = valueFormatted || value; - - if (value === undefined) { - return ( - - - - ); - } - - return ( - - {isLink ? ( - - {isLink ? displayedValue.value : displayedValue} - - ) : ( - - {displayedValue} - - )} - - ); -}; - -export const CellContainer = ({ - children, - position, -}: { - children: React.ReactNode; - position: 'center' | 'left' | 'right'; -}) => { - return ( -
- {children} -
- ); -}; diff --git a/src/renderer/components/virtual-table/cells/genre-cell.tsx b/src/renderer/components/virtual-table/cells/genre-cell.tsx deleted file mode 100644 index 9d3d987e6..000000000 --- a/src/renderer/components/virtual-table/cells/genre-cell.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { AlbumArtist, Artist } from '/@/shared/types/domain-types'; -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import React from 'react'; -import { generatePath, Link } from 'react-router'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { useGenreRoute } from '/@/renderer/hooks/use-genre-route'; -import { Separator } from '/@/shared/components/separator/separator'; -import { Text } from '/@/shared/components/text/text'; - -export const GenreCell = ({ data, value }: ICellRendererParams) => { - const genrePath = useGenreRoute(); - return ( - - - {value?.map((item: AlbumArtist | Artist, index: number) => ( - - {index > 0 && } - - {item.name || '—'} - - - ))} - - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/note-cell.tsx b/src/renderer/components/virtual-table/cells/note-cell.tsx deleted file mode 100644 index a094c41ae..000000000 --- a/src/renderer/components/virtual-table/cells/note-cell.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import { useMemo } from 'react'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { replaceURLWithHTMLLinks } from '/@/renderer/utils/linkify'; -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Text } from '/@/shared/components/text/text'; - -export const NoteCell = ({ value }: ICellRendererParams) => { - const formattedValue = useMemo(() => { - if (!value) { - return ''; - } - - return replaceURLWithHTMLLinks(value); - }, [value]); - - if (value === undefined) { - return ( - - - - ); - } - - return ( - - - {formattedValue} - - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/rating-cell.tsx b/src/renderer/components/virtual-table/cells/rating-cell.tsx deleted file mode 100644 index 4d82ff6be..000000000 --- a/src/renderer/components/virtual-table/cells/rating-cell.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { useSetRating } from '/@/renderer/features/shared/mutations/set-rating-mutation'; -import { Rating } from '/@/shared/components/rating/rating'; - -export const RatingCell = ({ node, value }: ICellRendererParams) => { - const updateRatingMutation = useSetRating({}); - - const handleUpdateRating = (rating: number) => { - updateRatingMutation.mutate( - { - apiClientProps: { serverId: value?.serverId || '' }, - query: { - item: [value], - rating, - }, - }, - { - onSuccess: () => { - node.setData({ ...node.data, userRating: rating }); - }, - }, - ); - }; - - return ( - - - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/row-index-cell.tsx b/src/renderer/components/virtual-table/cells/row-index-cell.tsx deleted file mode 100644 index 6e9080d21..000000000 --- a/src/renderer/components/virtual-table/cells/row-index-cell.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { Icon } from '/@/shared/components/icon/icon'; -import { Text } from '/@/shared/components/text/text'; - -// const AnimatedSvg = () => { -// return ( -//
-// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -//
-// ); -// }; - -// const StaticSvg = () => { -// return ( -//
-// -// -// -// -// -// -//
-// ); -// }; - -export const RowIndexCell = ({ eGridCell, value }: ICellRendererParams) => { - const classList = eGridCell.classList; - // const isFocused = classList.contains('focused'); - const isPlaying = classList.contains('playing'); - const isCurrentSong = - classList.contains('current-song-cell') || classList.contains('current-playlist-song-cell'); - - return ( - - {isPlaying && isCurrentSong ? ( - - ) : isCurrentSong ? ( - - ) : ( - - {value} - - )} - - ); -}; diff --git a/src/renderer/components/virtual-table/cells/title-cell.tsx b/src/renderer/components/virtual-table/cells/title-cell.tsx deleted file mode 100644 index dfc39305b..000000000 --- a/src/renderer/components/virtual-table/cells/title-cell.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import type { ICellRendererParams } from '@ag-grid-community/core'; - -import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { Skeleton } from '/@/shared/components/skeleton/skeleton'; -import { Text } from '/@/shared/components/text/text'; - -export const TitleCell = ({ value }: ICellRendererParams) => { - if (value === undefined) { - return ( - - - - ); - } - - return ( - - - {value} - - - ); -}; diff --git a/src/renderer/components/virtual-table/headers/duration-header.tsx b/src/renderer/components/virtual-table/headers/duration-header.tsx deleted file mode 100644 index 0b72d7920..000000000 --- a/src/renderer/components/virtual-table/headers/duration-header.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type { IHeaderParams } from '@ag-grid-community/core'; - -import { Icon } from '/@/shared/components/icon/icon'; - -export interface ICustomHeaderParams extends IHeaderParams { - menuIcon: string; -} - -export const DurationHeader = () => { - return ; -}; diff --git a/src/renderer/components/virtual-table/headers/generic-table-header.module.css b/src/renderer/components/virtual-table/headers/generic-table-header.module.css deleted file mode 100644 index b5f012ca8..000000000 --- a/src/renderer/components/virtual-table/headers/generic-table-header.module.css +++ /dev/null @@ -1,38 +0,0 @@ -.header-wrapper { - display: flex; - width: 100%; - text-transform: uppercase; -} - -.header-wrapper.right { - justify-content: flex-end; -} - -.header-wrapper.center { - justify-content: center; -} - -.header-wrapper.left { - justify-content: flex-start; -} - -.header-text { - width: 100%; - height: 100%; - font-weight: 500; - line-height: inherit; - color: var(--theme-colors-foreground); - text-transform: uppercase; -} - -.header-text.right { - text-align: flex-end; -} - -.header-text.center { - text-align: center; -} - -.header-text.left { - text-align: flex-start; -} diff --git a/src/renderer/components/virtual-table/headers/generic-table-header.tsx b/src/renderer/components/virtual-table/headers/generic-table-header.tsx deleted file mode 100644 index 27ea385ef..000000000 --- a/src/renderer/components/virtual-table/headers/generic-table-header.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { IHeaderParams } from '@ag-grid-community/core'; -import type { ReactNode } from 'react'; - -import clsx from 'clsx'; - -import styles from './generic-table-header.module.css'; - -import { Icon } from '/@/shared/components/icon/icon'; - -type Options = { - children?: ReactNode; - position?: 'center' | 'left' | 'right'; - preset?: Presets; -}; - -type Presets = 'actions' | 'duration' | 'rowIndex' | 'userFavorite' | 'userRating'; - -const headerPresets = { - actions: , - duration: , - rowIndex: , - userFavorite: , - userRating: , -}; - -export const GenericTableHeader = ( - { displayName }: IHeaderParams, - { children, position, preset }: Options, -) => { - if (preset) { - return ( -
- {headerPresets[preset]} -
- ); - } - - return ( -
-
- {children || displayName} -
-
- ); -}; diff --git a/src/renderer/components/virtual-table/hooks/use-click-outside-deselect.tsx b/src/renderer/components/virtual-table/hooks/use-click-outside-deselect.tsx deleted file mode 100644 index b67b20f92..000000000 --- a/src/renderer/components/virtual-table/hooks/use-click-outside-deselect.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; - -import { useClickOutside } from '@mantine/hooks'; -import { MutableRefObject } from 'react'; - -export const useClickOutsideDeselect = (tableRef: MutableRefObject) => { - const handleDeselect = () => { - if (tableRef.current) { - tableRef.current.api.deselectAll(); - } - }; - - const ref = useClickOutside(handleDeselect); - - return ref; -}; diff --git a/src/renderer/components/virtual-table/hooks/use-current-song-row-styles.ts b/src/renderer/components/virtual-table/hooks/use-current-song-row-styles.ts deleted file mode 100644 index 04e67a4fe..000000000 --- a/src/renderer/components/virtual-table/hooks/use-current-song-row-styles.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; - -import { RowClassRules, RowNode } from '@ag-grid-community/core'; -import { MutableRefObject, useEffect, useMemo, useRef } from 'react'; - -import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events'; -import { useAppFocus } from '/@/renderer/hooks'; -import { usePlayerSong } from '/@/renderer/store'; -import { Song } from '/@/shared/types/domain-types'; - -interface UseCurrentSongRowStylesProps { - tableRef: MutableRefObject; -} - -export const useCurrentSongRowStyles = ({ tableRef }: UseCurrentSongRowStylesProps) => { - const currentSong = usePlayerSong(); - const isFocused = useAppFocus(); - const isFocusedRef = useRef(isFocused); - - useEffect(() => { - // Redraw rows if the app focus changes - if (isFocusedRef.current !== isFocused) { - isFocusedRef.current = isFocused; - if (tableRef?.current) { - const { api, columnApi } = tableRef?.current || {}; - if (api == null || columnApi == null) { - return; - } - - const currentNode = currentSong?.id ? api.getRowNode(currentSong.id) : undefined; - - const rowNodes = [currentNode].filter((e) => e !== undefined) as RowNode[]; - - if (rowNodes) { - api.redrawRows({ rowNodes }); - } - } - } - }, [currentSong?.id, isFocused, tableRef]); - - const rowClassRules = useMemo | undefined>(() => { - return { - 'current-song': (params) => { - return ( - currentSong?.id !== undefined && - params?.data?.id === currentSong?.id && - params?.data?.albumId === currentSong?.albumId - ); - }, - }; - }, [currentSong?.albumId, currentSong?.id]); - - usePlayerEvents( - { - onCurrentSongChange: (properties, prev) => { - const song = properties.song; - const previousSong = prev.song; - - if (tableRef?.current) { - const { api, columnApi } = tableRef?.current || {}; - if (api == null || columnApi == null) { - return; - } - - const currentNode = song?.id ? api.getRowNode(song.id) : undefined; - - const previousNode = previousSong?.id - ? api.getRowNode(previousSong?.id) - : undefined; - - const rowNodes = [currentNode, previousNode].filter( - (e) => e !== undefined, - ) as RowNode[]; - - api.redrawRows({ rowNodes }); - } - }, - onPlayerStatus: () => { - const song = currentSong; - - if (tableRef?.current) { - const { api, columnApi } = tableRef?.current || {}; - if (api == null || columnApi == null) { - return; - } - - const currentNode = song?.id ? api.getRowNode(song.id) : undefined; - const rowNodes = [currentNode].filter((e) => e !== undefined) as RowNode[]; - - api.redrawRows({ rowNodes }); - } - }, - }, - [tableRef], - ); - - return { - rowClassRules, - }; -}; diff --git a/src/renderer/components/virtual-table/hooks/use-virtual-table.ts b/src/renderer/components/virtual-table/hooks/use-virtual-table.ts deleted file mode 100644 index 8322fc653..000000000 --- a/src/renderer/components/virtual-table/hooks/use-virtual-table.ts +++ /dev/null @@ -1,428 +0,0 @@ -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; - -import { - BodyScrollEvent, - ColDef, - GetRowIdParams, - GridReadyEvent, - IDatasource, - PaginationChangedEvent, - RowDoubleClickedEvent, -} from '@ag-grid-community/core'; -import { QueryKey, useQueryClient } from '@tanstack/react-query'; -import debounce from 'lodash/debounce'; -import orderBy from 'lodash/orderBy'; -import { MutableRefObject, useCallback, useMemo } from 'react'; -import { generatePath, useNavigate } from 'react-router'; -import { useSearchParams } from 'react-router'; - -import { api } from '/@/renderer/api'; -import { queryKeys, QueryPagination } from '/@/renderer/api/query-keys'; -import { getColumnDefs, VirtualTableProps } from '/@/renderer/components/virtual-table'; -import { SetContextMenuItems } from '/@/renderer/features/context-menu/events'; -import { useHandleTableContextMenu } from '/@/renderer/features/context-menu/hooks/use-handle-context-menu'; -import { AppRoute } from '/@/renderer/router/routes'; -import { PersistedTableColumn, useListStoreActions } from '/@/renderer/store'; -import { ListKey, useListStoreByKey } from '/@/renderer/store/list.store'; -import { - BasePaginatedResponse, - BaseQuery, - LibraryItem, - ServerListItem, -} from '/@/shared/types/domain-types'; -import { ListDisplayType, ListPagination } from '/@/shared/types/types'; - -export type AgGridFetchFn = ( - args: { filter: TFilter; limit: number; startIndex: number }, - signal?: AbortSignal, -) => Promise; - -interface UseAgGridProps { - columnType?: 'albumDetail' | 'generic'; - contextMenu: SetContextMenuItems; - customFilters?: Partial; - isClientSide?: boolean; - isClientSideSort?: boolean; - isSearchParams?: boolean; - itemCount?: number; - itemType: LibraryItem; - pageKey: string; - server: null | ServerListItem; - tableRef: MutableRefObject; -} - -const BLOCK_SIZE = 500; - -export const useVirtualTable = >({ - columnType, - contextMenu, - customFilters, - isClientSide, - isClientSideSort, - isSearchParams, - itemCount, - itemType, - pageKey, - server, - tableRef, -}: UseAgGridProps) => { - const queryClient = useQueryClient(); - const navigate = useNavigate(); - const { setTable, setTablePagination } = useListStoreActions(); - const properties = useListStoreByKey({ filter: customFilters, key: pageKey }); - const [searchParams, setSearchParams] = useSearchParams(); - - const scrollOffset = searchParams.get('scrollOffset'); - const pagination = useMemo(() => { - return { - currentPage: Number(searchParams.get('currentPage')), - itemsPerPage: Number(searchParams.get('itemsPerPage')), - totalItems: Number(searchParams.get('totalItems')), - totalPages: Number(searchParams.get('totalPages')), - }; - }, [searchParams]); - - const initialTableIndex = - Number(isSearchParams ? scrollOffset : properties.table.scrollOffset) || 0; - - const isPaginationEnabled = properties.display === ListDisplayType.TABLE_PAGINATED; - - const columnDefs: ColDef[] = useMemo(() => { - return getColumnDefs(properties.table.columns, true, columnType); - }, [columnType, properties.table.columns]); - - const defaultColumnDefs: ColDef = useMemo(() => { - return { - lockPinned: true, - lockVisible: true, - resizable: true, - }; - }, []); - - const onGridSizeChange = () => { - if (properties.table.autoFit) { - tableRef?.current?.api.sizeColumnsToFit(); - } - }; - - const queryKeyFn: - | ((serverId: string, query: Record, pagination: QueryPagination) => QueryKey) - | null = useMemo(() => { - switch (itemType) { - case LibraryItem.ALBUM: - return queryKeys.albums.list; - case LibraryItem.ALBUM_ARTIST: - return queryKeys.albumArtists.list; - case LibraryItem.ARTIST: - return queryKeys.artists.list; - case LibraryItem.GENRE: - return queryKeys.genres.list; - case LibraryItem.PLAYLIST: - return queryKeys.playlists.list; - case LibraryItem.SONG: - return queryKeys.songs.list; - default: - return null; - } - }, [itemType]); - - const queryFn: ((args: any) => Promise | null | undefined>) | null = - useMemo(() => { - switch (itemType) { - case LibraryItem.ALBUM: - return api.controller.getAlbumList; - case LibraryItem.ALBUM_ARTIST: - return api.controller.getAlbumArtistList; - case LibraryItem.ARTIST: - return api.controller.getArtistList; - case LibraryItem.GENRE: - return api.controller.getGenreList; - case LibraryItem.PLAYLIST: - return api.controller.getPlaylistList; - case LibraryItem.SONG: - return api.controller.getSongList; - default: - return null; - } - }, [itemType]); - - const onGridReady = useCallback( - (params: GridReadyEvent) => { - const dataSource: IDatasource = { - getRows: async (params) => { - const limit = params.endRow - params.startRow; - const startIndex = params.startRow; - - const queryKey = queryKeyFn!( - server?.id || '', - { - ...(properties.filter as any), - }, - { - limit, - startIndex, - }, - ); - - const results = (await queryClient.fetchQuery({ - queryFn: async ({ signal }) => { - const res = await queryFn!({ - apiClientProps: { - serverId: server?.id || '', - signal, - }, - query: { - ...properties.filter, - limit, - startIndex, - }, - }); - - return res; - }, - queryKey, - })) as BasePaginatedResponse; - - if (isClientSideSort && results?.items) { - const sortedResults = orderBy( - results.items, - [(item) => String(item[properties.filter.sortBy]).toLowerCase()], - properties.filter.sortOrder === 'DESC' ? ['desc'] : ['asc'], - ); - - params.successCallback(sortedResults || [], results?.totalRecordCount || 0); - return; - } - - if (results.totalRecordCount === null) { - const hasMoreRows = results?.items?.length === BLOCK_SIZE; - const lastRowIndex = hasMoreRows - ? undefined - : params.startRow + results.items.length; - - params.successCallback( - results?.items || [], - hasMoreRows ? undefined : lastRowIndex, - ); - return; - } - - params.successCallback(results?.items || [], results?.totalRecordCount || 0); - }, - rowCount: undefined, - }; - - params.api.setDatasource(dataSource); - params.api.ensureIndexVisible(initialTableIndex, 'top'); - }, - [ - initialTableIndex, - queryKeyFn, - server, - properties.filter, - queryClient, - isClientSideSort, - queryFn, - ], - ); - - const setParamsTablePagination = useCallback( - (args: { data: Partial; key: ListKey }) => { - const { data } = args; - - setSearchParams( - (params) => { - if (data.currentPage) params.set('currentPage', String(data.currentPage)); - if (data.itemsPerPage) params.set('itemsPerPage', String(data.itemsPerPage)); - if (data.totalItems) params.set('totalItems', String(data.totalItems)); - if (data.totalPages) params.set('totalPages', String(data.totalPages)); - return params; - }, - { replace: true }, - ); - }, - [setSearchParams], - ); - - const onPaginationChanged = useCallback( - (event: PaginationChangedEvent) => { - if (!isPaginationEnabled || !event.api) return; - - try { - // Scroll to top of page on pagination change - const currentPageStartIndex = - properties.table.pagination.currentPage * - properties.table.pagination.itemsPerPage; - event.api?.ensureIndexVisible(currentPageStartIndex, 'top'); - } catch (err) { - console.error(err); - } - - if (isSearchParams) { - setSearchParams( - (params) => { - params.set('currentPage', String(event.api.paginationGetCurrentPage())); - params.set('itemsPerPage', String(event.api.paginationGetPageSize())); - params.set('totalItems', String(event.api.paginationGetRowCount())); - params.set('totalPages', String(event.api.paginationGetTotalPages() + 1)); - return params; - }, - { replace: true }, - ); - } else { - setTablePagination({ - data: { - itemsPerPage: event.api.paginationGetPageSize(), - totalItems: event.api.paginationGetRowCount(), - totalPages: event.api.paginationGetTotalPages() + 1, - }, - key: pageKey, - }); - } - }, - [ - isPaginationEnabled, - isSearchParams, - properties.table.pagination.currentPage, - properties.table.pagination.itemsPerPage, - setSearchParams, - setTablePagination, - pageKey, - ], - ); - - const onColumnMoved = useCallback(() => { - const { columnApi } = tableRef?.current || {}; - const columnsOrder = columnApi?.getAllGridColumns(); - - if (!columnsOrder) return; - - const columnsInSettings = properties.table.columns; - const updatedColumns: PersistedTableColumn[] = []; - for (const column of columnsOrder) { - const columnInSettings = columnsInSettings.find( - (c) => c.column === column.getColDef().colId, - ); - - if (columnInSettings) { - updatedColumns.push({ - ...columnInSettings, - ...(!properties.table.autoFit && { - width: column.getActualWidth(), - }), - }); - } - } - - setTable({ data: { columns: updatedColumns }, key: pageKey }); - }, [pageKey, properties.table.autoFit, properties.table.columns, setTable, tableRef]); - - const onColumnResized = debounce(onColumnMoved, 200); - - const onBodyScrollEnd = (e: BodyScrollEvent) => { - const scrollOffset = Number((e.top / properties.table.rowHeight).toFixed(0)); - - if (isSearchParams) { - setSearchParams( - (params) => { - params.set('scrollOffset', String(scrollOffset)); - return params; - }, - { replace: true }, - ); - } else { - setTable({ data: { scrollOffset }, key: pageKey }); - } - }; - - const onCellContextMenu = useHandleTableContextMenu(itemType, contextMenu); - - const context = { - itemType, - onCellContextMenu, - }; - - const defaultTableProps: Partial = useMemo(() => { - return { - alwaysShowHorizontalScroll: true, - autoFitColumns: properties.table.autoFit, - blockLoadDebounceMillis: 200, - cacheBlockSize: BLOCK_SIZE, - getRowId: (data: GetRowIdParams) => data.data.id, - infiniteInitialRowCount: itemCount || 100, - pagination: isPaginationEnabled, - paginationAutoPageSize: isPaginationEnabled, - paginationPageSize: properties.table.pagination.itemsPerPage || 100, - paginationProps: isPaginationEnabled - ? { - pageKey, - pagination: isSearchParams ? pagination : properties.table.pagination, - setPagination: isSearchParams ? setParamsTablePagination : setTablePagination, - } - : undefined, - rowBuffer: 20, - rowHeight: properties.table.rowHeight || 40, - rowModelType: isClientSide ? 'clientSide' : 'infinite', - suppressRowDrag: true, - }; - }, [ - isClientSide, - isPaginationEnabled, - isSearchParams, - itemCount, - pageKey, - pagination, - properties.table.autoFit, - properties.table.pagination, - properties.table.rowHeight, - setParamsTablePagination, - setTablePagination, - ]); - - const onRowDoubleClicked = useCallback( - (e: RowDoubleClickedEvent) => { - switch (itemType) { - case LibraryItem.ALBUM: - navigate(generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { albumId: e.data.id })); - break; - case LibraryItem.ALBUM_ARTIST: - navigate( - generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, { - albumArtistId: e.data.id, - }), - ); - break; - case LibraryItem.ARTIST: - navigate( - generatePath(AppRoute.LIBRARY_ARTISTS_DETAIL, { - artistId: e.data.id, - }), - ); - break; - case LibraryItem.PLAYLIST: - navigate( - generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: e.data.id }), - ); - break; - default: - break; - } - }, - [itemType, navigate], - ); - - return { - columnDefs, - context, - defaultColumnDefs, - onBodyScrollEnd, - onCellContextMenu, - onColumnMoved, - onColumnResized, - onGridReady, - onGridSizeChange, - onPaginationChanged, - onRowDoubleClicked, - ...defaultTableProps, - }; -}; diff --git a/src/renderer/components/virtual-table/index.tsx b/src/renderer/components/virtual-table/index.tsx deleted file mode 100644 index 23999304f..000000000 --- a/src/renderer/components/virtual-table/index.tsx +++ /dev/null @@ -1,645 +0,0 @@ -import type { - ColDef, - ColumnMovedEvent, - GridReadyEvent, - GridSizeChangedEvent, - ICellRendererParams, - IHeaderParams, - ModelUpdatedEvent, - NewColumnsLoadedEvent, - ValueFormatterParams, - ValueGetterParams, -} from '@ag-grid-community/core'; -import type { AgGridReactProps } from '@ag-grid-community/react'; -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; - -import { AgGridReact } from '@ag-grid-community/react'; -import { useClickOutside, useMergedRef } from '@mantine/hooks'; -import clsx from 'clsx'; -import formatDuration from 'format-duration'; -import { AnimatePresence } from 'motion/react'; -import { forwardRef, Ref, useCallback, useEffect, useMemo, useRef } from 'react'; -import { generatePath } from 'react-router'; - -import styles from './virtual-table.module.css'; - -import i18n from '/@/i18n/i18n'; -import { ActionsCell } from '/@/renderer/components/virtual-table/cells/actions-cell'; -import { AlbumArtistCell } from '/@/renderer/components/virtual-table/cells/album-artist-cell'; -import { ArtistCell } from '/@/renderer/components/virtual-table/cells/artist-cell'; -import { CombinedTitleCell } from '/@/renderer/components/virtual-table/cells/combined-title-cell'; -import { FavoriteCell } from '/@/renderer/components/virtual-table/cells/favorite-cell'; -import { GenericCell } from '/@/renderer/components/virtual-table/cells/generic-cell'; -import { GenreCell } from '/@/renderer/components/virtual-table/cells/genre-cell'; -import { NoteCell } from '/@/renderer/components/virtual-table/cells/note-cell'; -import { RatingCell } from '/@/renderer/components/virtual-table/cells/rating-cell'; -import { RowIndexCell } from '/@/renderer/components/virtual-table/cells/row-index-cell'; -import { TitleCell } from '/@/renderer/components/virtual-table/cells/title-cell'; -import { GenericTableHeader } from '/@/renderer/components/virtual-table/headers/generic-table-header'; -import { useFixedTableHeader } from '/@/renderer/components/virtual-table/hooks/use-fixed-table-header'; -import { TablePagination } from '/@/renderer/components/virtual-table/table-pagination'; -import { useTableChange } from '/@/renderer/hooks/use-song-change'; -import { AppRoute } from '/@/renderer/router/routes'; -import { PersistedTableColumn } from '/@/renderer/store/settings.store'; -import { - formatDateAbsolute, - formatDateAbsoluteUTC, - formatDateRelative, - formatSizeString, -} from '/@/renderer/utils/format'; -import { - PlayerStatus, - TableColumn, - ListPagination as TablePaginationType, -} from '/@/shared/types/types'; - -export * from './hooks/use-click-outside-deselect'; -export * from './hooks/use-fixed-table-header'; -export * from './table-config-dropdown'; -export * from './table-pagination'; -export * from './utils'; - -// const TableWrapper = styled.div` -// position: relative; -// display: flex; -// flex-direction: column; -// width: 100%; -// height: 100%; -// `; - -// const DummyHeader = styled.div<{ height?: number }>` -// position: absolute; -// height: ${({ height }) => height || 36}px; -// `; - -const tableColumns: { [key: string]: ColDef } = { - actions: { - cellClass: 'ag-cell-favorite', - cellRenderer: (params: ICellRendererParams) => ActionsCell(params), - colId: TableColumn.ACTIONS, - headerComponent: () => <>, - suppressSizeToFit: true, - width: 25, - }, - album: { - cellRenderer: (params: ICellRendererParams) => - GenericCell(params, { isLink: true, position: 'left' }), - colId: TableColumn.ALBUM, - headerName: i18n.t('table.column.album'), - valueGetter: (params: ValueGetterParams) => - params.data - ? { - link: generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, { - albumId: params.data?.albumId || '', - }), - value: params.data?.album, - } - : undefined, - width: 200, - }, - albumArtist: { - cellRenderer: AlbumArtistCell, - colId: TableColumn.ALBUM_ARTIST, - headerName: i18n.t('table.column.albumArtist'), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.albumArtists : undefined, - width: 150, - }, - albumCount: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.ALBUM_COUNT, - field: 'albumCount', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.albumCount'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.albumCount : undefined, - width: 80, - }, - artist: { - cellRenderer: ArtistCell, - colId: TableColumn.ARTIST, - headerName: i18n.t('table.column.artist'), - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.artists : undefined), - width: 150, - }, - biography: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }), - colId: TableColumn.BIOGRAPHY, - field: 'biography', - headerName: i18n.t('table.column.biography'), - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.biography : ''), - width: 200, - }, - bitRate: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.BIT_RATE, - field: 'bitRate', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.bitrate'), - suppressSizeToFit: true, - valueFormatter: (params: ValueFormatterParams) => `${params.value} kbps`, - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.bitRate : undefined), - width: 90, - }, - bpm: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.BPM, - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.bpm'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.bpm : undefined), - width: 60, - }, - channels: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.CHANNELS, - field: 'channels', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.channels'), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.channels : undefined, - width: 100, - }, - codec: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.CODEC, - headerName: i18n.t('table.column.codec'), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.container : undefined, - width: 60, - }, - comment: { - cellRenderer: NoteCell, - colId: TableColumn.COMMENT, - headerName: i18n.t('table.column.comment'), - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.comment : undefined), - width: 150, - }, - dateAdded: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.DATE_ADDED, - field: 'createdAt', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.dateAdded'), - suppressSizeToFit: true, - valueFormatter: (params: ValueFormatterParams) => formatDateAbsolute(params.value), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.createdAt : undefined, - width: 130, - }, - discNumber: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.DISC_NUMBER, - field: 'discNumber', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.discNumber'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.discNumber : undefined, - width: 60, - }, - duration: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.DURATION, - field: 'duration', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center', preset: 'duration' }), - suppressSizeToFit: true, - valueFormatter: (params: ValueFormatterParams) => formatDuration(Number(params.value)), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.duration : undefined, - width: 70, - }, - genre: { - cellRenderer: GenreCell, - colId: TableColumn.GENRE, - headerName: i18n.t('table.column.genre'), - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.genres : undefined), - width: 100, - }, - lastPlayedAt: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.LAST_PLAYED, - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.lastPlayed'), - valueFormatter: (params: ValueFormatterParams) => formatDateRelative(params.value), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.lastPlayedAt : undefined, - width: 130, - }, - path: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'left' }), - colId: TableColumn.PATH, - headerName: i18n.t('table.column.path'), - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.path : undefined), - width: 200, - }, - playCount: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.PLAY_COUNT, - field: 'playCount', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.playCount'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.playCount : undefined, - width: 90, - }, - releaseDate: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.RELEASE_DATE, - field: 'releaseDate', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.releaseDate'), - suppressSizeToFit: true, - valueFormatter: (params: ValueFormatterParams) => formatDateAbsoluteUTC(params.value), - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.releaseDate : undefined, - width: 130, - }, - releaseYear: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.YEAR, - field: 'releaseYear', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.releaseYear'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.releaseYear : undefined, - width: 80, - }, - rowIndex: { - cellClass: 'row-index', - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }), - colId: TableColumn.ROW_INDEX, - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'right', preset: 'rowIndex' }), - suppressSizeToFit: true, - valueGetter: (params) => { - return (params.node?.rowIndex || 0) + 1; - }, - width: 65, - }, - rowIndexGeneric: { - cellClass: 'row-index', - cellClassRules: { - 'current-playlist-song-cell': (params) => { - return ( - params.context?.currentSong?.uniqueId !== undefined && - params.data?.uniqueId === params.context?.currentSong?.uniqueId - ); - }, - 'current-song-cell': (params) => { - return ( - params.context?.currentSong?.id !== undefined && - params.data?.id === params.context?.currentSong?.id && - params.data?.albumId === params.context?.currentSong?.albumId - ); - }, - focused: (params) => { - return params.context?.isFocused; - }, - playing: (params) => { - return params.context?.status === PlayerStatus.PLAYING; - }, - }, - cellRenderer: RowIndexCell, - colId: TableColumn.ROW_INDEX, - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'right', preset: 'rowIndex' }), - suppressSizeToFit: true, - valueGetter: (params) => { - return (params.node?.rowIndex || 0) + 1; - }, - width: 65, - }, - size: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.SIZE, - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.size'), - valueGetter: (params: ValueGetterParams) => - params.data ? formatSizeString(params.data.size) : undefined, - width: 80, - }, - songCount: { - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'center' }), - colId: TableColumn.SONG_COUNT, - field: 'songCount', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.songCount'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.songCount : undefined, - width: 80, - }, - title: { - cellRenderer: TitleCell, - colId: TableColumn.TITLE, - field: 'name', - headerName: i18n.t('table.column.title'), - valueGetter: (params: ValueGetterParams) => (params.data ? params.data.name : undefined), - width: 250, - }, - titleCombined: { - cellRenderer: CombinedTitleCell, - colId: TableColumn.TITLE_COMBINED, - headerName: i18n.t('table.column.title'), - initialWidth: 500, - minWidth: 150, - valueGetter: (params: ValueGetterParams) => - params.data - ? { - albumArtists: params.data?.albumArtists, - artists: params.data?.artists, - id: params.data?.id, - imagePlaceholderUrl: params.data?.imagePlaceholderUrl, - imageUrl: params.data?.imageUrl, - name: params.data?.name, - rowHeight: params.node?.rowHeight, - type: params.data?.serverType, - } - : undefined, - width: 250, - }, - trackNumber: { - cellClass: 'track-number', - cellRenderer: (params: ICellRendererParams) => GenericCell(params, { position: 'right' }), - colId: TableColumn.TRACK_NUMBER, - field: 'trackNumber', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.trackNumber'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.trackNumber : undefined, - width: 80, - }, - trackNumberDetail: { - cellClass: 'row-index', - cellClassRules: { - 'current-song-cell': (params) => { - return ( - params.context?.currentSong?.id !== undefined && - params.data?.id === params.context?.currentSong?.id && - params.data?.albumId === params.context?.currentSong?.albumId - ); - }, - focused: (params) => { - return params.context?.isFocused; - }, - playing: (params) => { - return params.context?.status === PlayerStatus.PLAYING; - }, - }, - cellRenderer: RowIndexCell, - colId: TableColumn.TRACK_NUMBER, - field: 'trackNumber', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center' }), - headerName: i18n.t('table.column.trackNumber'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.trackNumber : undefined, - width: 80, - }, - userFavorite: { - cellClass: (params) => (params.value ? 'visible ag-cell-favorite' : 'ag-cell-favorite'), - cellRenderer: FavoriteCell, - colId: TableColumn.USER_FAVORITE, - field: 'userFavorite', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center', preset: 'userFavorite' }), - headerName: i18n.t('table.column.favorite'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => - params.data ? params.data.userFavorite : undefined, - width: 50, - }, - userRating: { - cellClass: (params) => - params.value?.userRating ? 'visible ag-cell-rating' : 'ag-cell-rating', - cellRenderer: RatingCell, - colId: TableColumn.USER_RATING, - field: 'userRating', - headerComponent: (params: IHeaderParams) => - GenericTableHeader(params, { position: 'center', preset: 'userRating' }), - headerName: i18n.t('table.column.rating'), - suppressSizeToFit: true, - valueGetter: (params: ValueGetterParams) => (params.data ? params.data : undefined), - width: 95, - }, -}; - -export const getColumnDef = (column: TableColumn) => { - return tableColumns[column as keyof typeof tableColumns]; -}; - -export const getColumnDefs = ( - columns: PersistedTableColumn[], - useWidth?: boolean, - type?: 'albumDetail' | 'generic', -) => { - const columnDefs: ColDef[] = []; - for (const column of columns) { - let presetColumn = tableColumns[column.column as keyof typeof tableColumns]; - - if (column.column === TableColumn.TRACK_NUMBER && type === 'albumDetail') { - presetColumn = tableColumns['trackNumberDetail' as keyof typeof tableColumns]; - } - - if (column.column === TableColumn.ROW_INDEX && type === 'generic') { - presetColumn = tableColumns['rowIndexGeneric' as keyof typeof tableColumns]; - } - - if (presetColumn) { - columnDefs.push({ - ...presetColumn, - [useWidth ? 'width' : 'initialWidth']: column.width, - ...column.extraProps, - }); - } - } - - return columnDefs; -}; - -export interface VirtualTableProps extends AgGridReactProps { - autoFitColumns?: boolean; - autoHeight?: boolean; - deselectOnClickOutside?: boolean; - paginationProps?: { - pageKey: string; - pagination: TablePaginationType; - setPagination: any; - }; - shouldUpdateSong?: boolean; - stickyHeader?: boolean; - transparentHeader?: boolean; -} - -export const VirtualTable = forwardRef( - ( - { - autoFitColumns, - autoHeight, - deselectOnClickOutside, - onColumnMoved, - onGridReady, - onGridSizeChanged, - onNewColumnsLoaded, - paginationProps, - shouldUpdateSong, - stickyHeader, - transparentHeader, - ...rest - }: VirtualTableProps, - ref: Ref, - ) => { - const tableRef = useRef(null); - - const mergedRef = useMergedRef(ref, tableRef); - - const deselectRef = useClickOutside(() => { - if (tableRef?.current?.api && deselectOnClickOutside) { - tableRef?.current?.api?.deselectAll(); - } - }); - - useTableChange(tableRef, shouldUpdateSong === true); - - const defaultColumnDefs: ColDef = useMemo(() => { - return { - lockPinned: true, - lockVisible: true, - resizable: true, - }; - }, []); - - // Auto fit columns on column change - useEffect(() => { - if (!tableRef?.current?.api) return; - if (autoFitColumns && tableRef?.current?.api) { - tableRef?.current?.api?.sizeColumnsToFit?.(); - } - }, [autoFitColumns]); - - // Reset row heights on row height change - useEffect(() => { - if (!tableRef?.current?.api) return; - tableRef?.current?.api?.resetRowHeights(); - tableRef?.current?.api?.redrawRows(); - }, [rest.rowHeight]); - - const handleColumnMoved = useCallback( - (e: ColumnMovedEvent) => { - if (!e?.api) return; - onColumnMoved?.(e); - if (autoFitColumns) e.api?.sizeColumnsToFit?.(); - }, - [autoFitColumns, onColumnMoved], - ); - - const handleNewColumnsLoaded = useCallback( - (e: NewColumnsLoadedEvent) => { - if (!e?.api) return; - onNewColumnsLoaded?.(e); - if (autoFitColumns) e.api?.sizeColumnsToFit?.(); - }, - [autoFitColumns, onNewColumnsLoaded], - ); - - const handleGridReady = useCallback( - (e: GridReadyEvent) => { - if (!e?.api) return; - onGridReady?.(e); - if (autoHeight) e.api.setDomLayout('autoHeight'); - }, - [autoHeight, onGridReady], - ); - - const handleGridSizeChanged = useCallback( - (e: GridSizeChangedEvent) => { - if (!e?.api) return; - onGridSizeChanged?.(e); - if (autoFitColumns) e.api?.sizeColumnsToFit?.(); - }, - [autoFitColumns, onGridSizeChanged], - ); - - const handleModelUpdated = useCallback( - (e: ModelUpdatedEvent) => { - if (!e?.api) return; - if (autoFitColumns) e.api?.sizeColumnsToFit?.(); - }, - [autoFitColumns], - ); - - const { tableContainerRef, tableHeaderRef } = useFixedTableHeader({ - enabled: stickyHeader || false, - }); - - const mergedWrapperRef = useMergedRef(deselectRef, tableContainerRef); - - return ( -
-
- - {paginationProps && ( - - - - )} -
- ); - }, -); diff --git a/src/renderer/components/virtual-table/table-config-dropdown.tsx b/src/renderer/components/virtual-table/table-config-dropdown.tsx deleted file mode 100644 index 5b1a84d05..000000000 --- a/src/renderer/components/virtual-table/table-config-dropdown.tsx +++ /dev/null @@ -1,467 +0,0 @@ -import type { ChangeEvent } from 'react'; - -import { useTranslation } from 'react-i18next'; - -import i18n from '/@/i18n/i18n'; -import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store'; -import { MultiSelect } from '/@/shared/components/multi-select/multi-select'; -import { Option } from '/@/shared/components/option/option'; -import { Slider } from '/@/shared/components/slider/slider'; -import { Switch } from '/@/shared/components/switch/switch'; -import { ItemListKey, TableColumn } from '/@/shared/types/types'; - -export const SONG_TABLE_COLUMNS = [ - { - label: i18n.t('table.config.label.rowIndex', { postProcess: 'titleCase' }), - value: TableColumn.ROW_INDEX, - }, - { - label: i18n.t('table.config.label.image', { postProcess: 'titleCase' }), - value: TableColumn.IMAGE, - }, - { - label: i18n.t('table.config.label.title', { postProcess: 'titleCase' }), - value: TableColumn.TITLE, - }, - { - label: i18n.t('table.config.label.titleCombined', { postProcess: 'titleCase' }), - value: TableColumn.TITLE_COMBINED, - }, - { - label: i18n.t('table.config.label.duration', { postProcess: 'titleCase' }), - value: TableColumn.DURATION, - }, - { - label: i18n.t('table.config.label.album', { postProcess: 'titleCase' }), - value: TableColumn.ALBUM, - }, - { - label: i18n.t('table.config.label.albumArtist', { postProcess: 'titleCase' }), - value: TableColumn.ALBUM_ARTIST, - }, - { - label: i18n.t('table.config.label.artist', { postProcess: 'titleCase' }), - value: TableColumn.ARTIST, - }, - { - label: i18n.t('table.config.label.genre', { postProcess: 'titleCase' }), - value: TableColumn.GENRE, - }, - { - label: i18n.t('table.config.label.genreBadge', { postProcess: 'titleCase' }), - value: TableColumn.GENRE_BADGE, - }, - { - label: i18n.t('table.config.label.year', { postProcess: 'titleCase' }), - value: TableColumn.YEAR, - }, - { - label: i18n.t('table.config.label.releaseDate', { postProcess: 'titleCase' }), - value: TableColumn.RELEASE_DATE, - }, - { - label: i18n.t('table.config.label.discNumber', { postProcess: 'titleCase' }), - value: TableColumn.DISC_NUMBER, - }, - { - label: i18n.t('table.config.label.trackNumber', { postProcess: 'titleCase' }), - value: TableColumn.TRACK_NUMBER, - }, - { - label: i18n.t('table.config.label.bitrate', { postProcess: 'titleCase' }), - value: TableColumn.BIT_RATE, - }, - { - label: i18n.t('table.config.label.codec', { postProcess: 'titleCase' }), - value: TableColumn.CODEC, - }, - { - label: i18n.t('table.config.label.lastPlayed', { postProcess: 'titleCase' }), - value: TableColumn.LAST_PLAYED, - }, - { - label: i18n.t('table.config.label.note', { postProcess: 'titleCase' }), - value: TableColumn.COMMENT, - }, - { - label: i18n.t('table.config.label.channels', { postProcess: 'titleCase' }), - value: TableColumn.CHANNELS, - }, - { - label: i18n.t('table.config.label.bpm', { postProcess: 'titleCase' }), - value: TableColumn.BPM, - }, - { - label: i18n.t('table.config.label.dateAdded', { postProcess: 'titleCase' }), - value: TableColumn.DATE_ADDED, - }, - { - label: i18n.t('table.config.label.path', { postProcess: 'titleCase' }), - value: TableColumn.PATH, - }, - { - label: i18n.t('table.config.label.playCount', { postProcess: 'titleCase' }), - value: TableColumn.PLAY_COUNT, - }, - { - label: i18n.t('table.config.label.size', { postProcess: 'titleCase' }), - value: TableColumn.SIZE, - }, - { - label: i18n.t('table.config.label.favorite', { postProcess: 'titleCase' }), - value: TableColumn.USER_FAVORITE, - }, - { - label: i18n.t('table.config.label.rating', { postProcess: 'titleCase' }), - value: TableColumn.USER_RATING, - }, - { - label: i18n.t('table.config.label.actions', { postProcess: 'titleCase' }), - value: TableColumn.ACTIONS, - }, - // { label: 'Skip', value: TableColumn.SKIP }, -]; - -export const ALBUM_TABLE_COLUMNS = [ - { - label: i18n.t('table.config.label.rowIndex', { postProcess: 'titleCase' }), - value: TableColumn.ROW_INDEX, - }, - { - label: i18n.t('table.config.label.image', { postProcess: 'titleCase' }), - value: TableColumn.IMAGE, - }, - { - label: i18n.t('table.config.label.title', { postProcess: 'titleCase' }), - value: TableColumn.TITLE, - }, - { - label: i18n.t('table.config.label.titleCombined', { postProcess: 'titleCase' }), - value: TableColumn.TITLE_COMBINED, - }, - { - label: i18n.t('table.config.label.duration', { postProcess: 'titleCase' }), - value: TableColumn.DURATION, - }, - { - label: i18n.t('table.config.label.albumArtist', { postProcess: 'titleCase' }), - value: TableColumn.ALBUM_ARTIST, - }, - { - label: i18n.t('table.config.label.artist', { postProcess: 'titleCase' }), - value: TableColumn.ARTIST, - }, - { - label: i18n.t('table.config.label.songCount', { postProcess: 'titleCase' }), - value: TableColumn.SONG_COUNT, - }, - { - label: i18n.t('table.config.label.genre', { postProcess: 'titleCase' }), - value: TableColumn.GENRE, - }, - { - label: i18n.t('table.config.label.genreBadge', { postProcess: 'titleCase' }), - value: TableColumn.GENRE_BADGE, - }, - { - label: i18n.t('table.config.label.year', { postProcess: 'titleCase' }), - value: TableColumn.YEAR, - }, - { - label: i18n.t('table.config.label.releaseDate', { postProcess: 'titleCase' }), - value: TableColumn.RELEASE_DATE, - }, - { - label: i18n.t('table.config.label.lastPlayed', { postProcess: 'titleCase' }), - value: TableColumn.LAST_PLAYED, - }, - { - label: i18n.t('table.config.label.dateAdded', { postProcess: 'titleCase' }), - value: TableColumn.DATE_ADDED, - }, - { - label: i18n.t('table.config.label.playCount', { postProcess: 'titleCase' }), - value: TableColumn.PLAY_COUNT, - }, - { - label: i18n.t('table.config.label.favorite', { postProcess: 'titleCase' }), - value: TableColumn.USER_FAVORITE, - }, - { - label: i18n.t('table.config.label.rating', { postProcess: 'titleCase' }), - value: TableColumn.USER_RATING, - }, - { - label: i18n.t('table.config.label.actions', { postProcess: 'titleCase' }), - value: TableColumn.ACTIONS, - }, -]; - -export const ALBUMARTIST_TABLE_COLUMNS = [ - { - label: i18n.t('table.config.label.rowIndex', { postProcess: 'titleCase' }), - value: TableColumn.ROW_INDEX, - }, - { - label: i18n.t('table.config.label.title', { postProcess: 'titleCase' }), - value: TableColumn.TITLE, - }, - { - label: i18n.t('table.config.label.titleCombined', { postProcess: 'titleCase' }), - value: TableColumn.TITLE_COMBINED, - }, - { - label: i18n.t('table.config.label.duration', { postProcess: 'titleCase' }), - value: TableColumn.DURATION, - }, - { - label: i18n.t('table.config.label.biography', { postProcess: 'titleCase' }), - value: TableColumn.BIOGRAPHY, - }, - { - label: i18n.t('table.config.label.genre', { postProcess: 'titleCase' }), - value: TableColumn.GENRE, - }, - { - label: i18n.t('table.config.label.lastPlayed', { postProcess: 'titleCase' }), - value: TableColumn.LAST_PLAYED, - }, - { - label: i18n.t('table.config.label.playCount', { postProcess: 'titleCase' }), - value: TableColumn.PLAY_COUNT, - }, - { - label: i18n.t('filter.albumCount', { postProcess: 'titleCase' }), - value: TableColumn.ALBUM_COUNT, - }, - { - label: i18n.t('table.config.label.songCount', { postProcess: 'titleCase' }), - value: TableColumn.SONG_COUNT, - }, - { - label: i18n.t('table.config.label.favorite', { postProcess: 'titleCase' }), - value: TableColumn.USER_FAVORITE, - }, - { - label: i18n.t('table.config.label.rating', { postProcess: 'titleCase' }), - value: TableColumn.USER_RATING, - }, - { - label: i18n.t('table.config.label.actions', { postProcess: 'titleCase' }), - value: TableColumn.ACTIONS, - }, -]; - -export const PLAYLIST_TABLE_COLUMNS = [ - { - label: i18n.t('table.config.label.rowIndex', { postProcess: 'titleCase' }), - value: TableColumn.ROW_INDEX, - }, - { - label: i18n.t('table.config.label.image', { postProcess: 'titleCase' }), - value: TableColumn.IMAGE, - }, - { - label: i18n.t('table.config.label.title', { postProcess: 'titleCase' }), - value: TableColumn.TITLE, - }, - { - label: i18n.t('table.config.label.titleCombined', { postProcess: 'titleCase' }), - value: TableColumn.TITLE_COMBINED, - }, - { - label: i18n.t('table.config.label.duration', { postProcess: 'titleCase' }), - value: TableColumn.DURATION, - }, - { - label: i18n.t('table.config.label.owner', { postProcess: 'titleCase' }), - value: TableColumn.OWNER, - }, - { - label: i18n.t('table.config.label.songCount', { postProcess: 'titleCase' }), - value: TableColumn.SONG_COUNT, - }, - { - label: i18n.t('table.config.label.actions', { postProcess: 'titleCase' }), - value: TableColumn.ACTIONS, - }, -]; - -export const ARTIST_TABLE_COLUMNS = [ - { - label: i18n.t('table.config.label.rowIndex', { postProcess: 'titleCase' }), - value: TableColumn.ROW_INDEX, - }, - { - label: i18n.t('table.config.label.title', { postProcess: 'titleCase' }), - value: TableColumn.TITLE, - }, - { - label: i18n.t('table.config.label.actions', { postProcess: 'titleCase' }), - value: TableColumn.ACTIONS, - }, -]; - -export const GENRE_TABLE_COLUMNS = [ - { - label: i18n.t('table.config.label.rowIndex', { postProcess: 'titleCase' }), - value: TableColumn.ROW_INDEX, - }, - { - label: i18n.t('table.config.label.title', { postProcess: 'titleCase' }), - value: TableColumn.TITLE, - }, - { - label: i18n.t('table.config.label.actions', { postProcess: 'titleCase' }), - value: TableColumn.ACTIONS, - }, -]; - -interface TableConfigDropdownProps { - // tableRef?: MutableRefObject | null>; - type: ItemListKey; -} - -export const TableConfigDropdown = ({ type }: TableConfigDropdownProps) => { - const { t } = useTranslation(); - const { setSettings } = useSettingsStoreActions(); - const tableConfig = useSettingsStore((state) => state.lists); - - const handleAddOrRemoveColumns = (values: string[]) => { - const existingColumns = tableConfig[type].columns; - - if (values.length === 0) { - setSettings({ - lists: { - ...useSettingsStore.getState().lists, - [type]: { - ...useSettingsStore.getState().lists[type], - columns: [], - }, - }, - }); - return; - } - - // If adding a column - if (values.length > existingColumns.length) { - const newColumn = { column: values[values.length - 1] }; - setSettings({ - lists: { - ...useSettingsStore.getState().lists, - [type]: { - ...useSettingsStore.getState().lists[type], - columns: [...existingColumns, newColumn], - }, - }, - }); - } - // If removing a column - else { - const removed = existingColumns.filter((column) => !values.includes(column.column)); - - const newColumns = existingColumns.filter((column) => !removed.includes(column)); - - setSettings({ - lists: { - ...useSettingsStore.getState().lists, - [type]: { - ...useSettingsStore.getState().lists[type], - columns: newColumns, - }, - }, - }); - } - }; - - const handleUpdateRowHeight = (value: number) => { - setSettings({ - lists: { - ...useSettingsStore.getState().lists, - [type]: { - ...useSettingsStore.getState().lists[type], - rowHeight: value, - }, - }, - }); - }; - - const handleUpdateAutoFit = (e: ChangeEvent) => { - setSettings({ - lists: { - ...useSettingsStore.getState().lists, - [type]: { - ...useSettingsStore.getState().lists[type], - autoFit: e.currentTarget.checked, - }, - }, - }); - }; - - const handleUpdateFollow = (e: ChangeEvent) => { - setSettings({ - lists: { - ...useSettingsStore.getState().lists, - [type]: { - ...useSettingsStore.getState().lists[type], - followCurrentSong: e.currentTarget.checked, - }, - }, - }); - }; - - return ( - <> - - {type !== 'albumDetail' && ( - - )} - - - - ); -}; diff --git a/src/renderer/components/virtual-table/table-pagination.tsx b/src/renderer/components/virtual-table/table-pagination.tsx deleted file mode 100644 index 14e62a852..000000000 --- a/src/renderer/components/virtual-table/table-pagination.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; - -import { useForm } from '@mantine/form'; -import { useDisclosure } from '@mantine/hooks'; -import { MutableRefObject } from 'react'; - -import { useContainerQuery } from '/@/renderer/hooks'; -import { ListKey } from '/@/renderer/store'; -import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; -import { Button } from '/@/shared/components/button/button'; -import { Flex } from '/@/shared/components/flex/flex'; -import { Group } from '/@/shared/components/group/group'; -import { NumberInput } from '/@/shared/components/number-input/number-input'; -import { Pagination } from '/@/shared/components/pagination/pagination'; -import { Popover } from '/@/shared/components/popover/popover'; -import { Text } from '/@/shared/components/text/text'; -import { ListPagination as TablePaginationType } from '/@/shared/types/types'; - -interface TablePaginationProps { - pageKey: ListKey; - pagination: TablePaginationType; - setIdPagination?: (id: string, pagination: Partial) => void; - setPagination?: (args: { data: Partial; key: ListKey }) => void; - tableRef: MutableRefObject; -} - -export const TablePagination = ({ - pageKey, - pagination, - setIdPagination, - setPagination, - tableRef, -}: TablePaginationProps) => { - const [isGoToPageOpen, handlers] = useDisclosure(false); - const containerQuery = useContainerQuery(); - - const goToForm = useForm({ - initialValues: { - pageNumber: undefined, - }, - }); - - const handlePagination = (index: number) => { - const newPage = index - 1; - tableRef.current?.api.paginationGoToPage(newPage); - setPagination?.({ data: { currentPage: newPage }, key: pageKey }); - setIdPagination?.(pageKey || '', { currentPage: newPage }); - }; - - const handleGoSubmit = goToForm.onSubmit((values) => { - handlers.close(); - if ( - !values.pageNumber || - values.pageNumber < 1 || - values.pageNumber > pagination.totalPages - ) { - return; - } - - const newPage = values.pageNumber - 1; - tableRef.current?.api.paginationGoToPage(newPage); - setPagination?.({ data: { currentPage: newPage }, key: pageKey }); - setIdPagination?.(pageKey || '', { currentPage: newPage }); - }); - - const currentPageStartIndex = pagination.currentPage * pagination.itemsPerPage + 1; - const currentPageMaxIndex = (pagination.currentPage + 1) * pagination.itemsPerPage; - const currentPageStopIndex = - currentPageMaxIndex > pagination.totalItems ? pagination.totalItems : currentPageMaxIndex; - - return ( - - - {containerQuery.isMd ? ( - <> - Showing {currentPageStartIndex} - {currentPageStopIndex} of{' '} - {pagination.totalItems} items - - ) : containerQuery.isSm ? ( - <> - {currentPageStartIndex} - {currentPageStopIndex} of{' '} - {pagination.totalItems} items - - ) : ( - <> - {currentPageStartIndex} - {currentPageStopIndex} of{' '} - {pagination.totalItems} - - )} - - - handlers.close()} - opened={isGoToPageOpen} - position="bottom-start" - trapFocus - > - - handlers.toggle()} - radius="sm" - size="sm" - style={{ height: '26px', padding: '0', width: '26px' }} - /> - - -
- - - - -
-
-
- -
-
- ); -}; diff --git a/src/renderer/components/virtual-table/utils.ts b/src/renderer/components/virtual-table/utils.ts deleted file mode 100644 index fd7b71d81..000000000 --- a/src/renderer/components/virtual-table/utils.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { GridApi, RowNode } from '@ag-grid-community/core'; - -export const getNodesByDiscNumber = (args: { - api: GridApi; - discNumber: number; - subtitle: null | string; -}) => { - const { api, discNumber, subtitle } = args; - - const nodes: RowNode[] = []; - api.forEachNode((node) => { - if (node.data.discNumber === discNumber && node.data.discSubtitle === subtitle) - nodes.push(node); - }); - - return nodes; -}; - -export const setNodeSelection = (args: { - deselectAll?: boolean; - isSelected: boolean; - nodes: RowNode[]; -}) => { - const { isSelected, nodes } = args; - - nodes.forEach((node) => { - node.setSelected(isSelected); - }); -}; - -export const toggleNodeSelection = (args: { nodes: RowNode[] }) => { - const { nodes } = args; - - nodes.forEach((node) => { - if (node.isSelected()) { - node.setSelected(false); - } else { - node.setSelected(true); - } - }); -}; diff --git a/src/renderer/components/virtual-table/virtual-table.module.css b/src/renderer/components/virtual-table/virtual-table.module.css deleted file mode 100644 index 260ce0486..000000000 --- a/src/renderer/components/virtual-table/virtual-table.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.table-wrapper { - position: relative; - display: flex; - flex-direction: column; - width: 100%; - height: 100%; -} - -.dummy-header { - position: absolute; -} diff --git a/src/renderer/features/artists/components/artist-list-header-filters.tsx b/src/renderer/features/artists/components/artist-list-header-filters.tsx index dfa5d8824..4f7ae9bfc 100644 --- a/src/renderer/features/artists/components/artist-list-header-filters.tsx +++ b/src/renderer/features/artists/components/artist-list-header-filters.tsx @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { ALBUMARTIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; +import { ALBUM_ARTIST_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; import { sharedQueries } from '/@/renderer/features/shared/api/shared-api'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { ListMusicFolderDropdown } from '/@/renderer/features/shared/components/list-music-folder-dropdown'; @@ -52,7 +52,7 @@ export const ArtistListHeaderFilters = () => { diff --git a/src/renderer/features/context-menu/actions/add-to-playlist-action.tsx b/src/renderer/features/context-menu/actions/add-to-playlist-action.tsx index 2d5f3a261..cfda88e43 100644 --- a/src/renderer/features/context-menu/actions/add-to-playlist-action.tsx +++ b/src/renderer/features/context-menu/actions/add-to-playlist-action.tsx @@ -259,6 +259,7 @@ export const AddToPlaylistAction = ({ items, itemType }: AddToPlaylistActionProp resourceType: itemType, }, modal: 'addToPlaylist', + size: 'lg', title: t('page.contextMenu.addToPlaylist', { postProcess: 'sentenceCase' }), }); }, [itemType, items, t]); diff --git a/src/renderer/features/context-menu/context-menu-items.tsx b/src/renderer/features/context-menu/context-menu-items.tsx deleted file mode 100644 index f93b5f0f5..000000000 --- a/src/renderer/features/context-menu/context-menu-items.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import React from 'react'; - -import { AppIconSelection } from '/@/shared/components/icon/icon'; - -export enum ContextMenuItemKey { - ADD_TO_FAVORITES = 'addToFavorites', - ADD_TO_PLAYLIST = 'addToPlaylist', - CREATE_PLAYLIST = 'createPlaylist', - DELETE_PLAYLIST = 'deletePlaylist', - DESELECT_ALL = 'deselectAll', - DOWNLOAD = 'download', - GO_TO_ALBUM = 'goToAlbum', - GO_TO_ALBUM_ARTIST = 'goToAlbumArtist', - MOVE_TO_BOTTOM_OF_QUEUE = 'moveToBottomOfQueue', - MOVE_TO_NEXT_OF_QUEUE = 'moveToNextOfQueue', - MOVE_TO_TOP_OF_QUEUE = 'moveToTopOfQueue', - PLAY = 'play', - PLAY_LAST = 'playLast', - PLAY_NEXT = 'playNext', - PLAY_SHUFFLED = 'playShuffled', - PLAY_SIMILAR_SONGS = 'playSimilarSongs', - REMOVE_FROM_FAVORITES = 'removeFromFavorites', - REMOVE_FROM_PLAYLIST = 'removeFromPlaylist', - REMOVE_FROM_QUEUE = 'removeFromQueue', - SET_RATING = 'setRating', - SET_RATING_1 = 'setRating1', - SET_RATING_2 = 'setRating2', - SET_RATING_3 = 'setRating3', - SET_RATING_4 = 'setRating4', - SET_RATING_5 = 'setRating5', - SHARE_ITEM = 'shareItem', - SHOW_DETAILS = 'showDetails', -} - -export type ContextMenuHandlers = Partial void>>; - -export interface ContextMenuItem { - disabled?: boolean; - hidden?: boolean; - icon?: React.ReactNode; - items?: ContextMenuItem[]; - key: string; - onClick?: () => void; -} - -export type ContextMenuItemDefinition = { - children?: ContextMenuItemDefinition[]; - disabled?: boolean; - key: ContextMenuItemKeys; -}; - -export type ContextMenuItemKeys = - | ContextMenuItemKey.ADD_TO_FAVORITES - | ContextMenuItemKey.ADD_TO_PLAYLIST - | ContextMenuItemKey.CREATE_PLAYLIST - | ContextMenuItemKey.DELETE_PLAYLIST - | ContextMenuItemKey.DESELECT_ALL - | ContextMenuItemKey.DOWNLOAD - | ContextMenuItemKey.GO_TO_ALBUM - | ContextMenuItemKey.GO_TO_ALBUM_ARTIST - | ContextMenuItemKey.MOVE_TO_BOTTOM_OF_QUEUE - | ContextMenuItemKey.MOVE_TO_NEXT_OF_QUEUE - | ContextMenuItemKey.MOVE_TO_TOP_OF_QUEUE - | ContextMenuItemKey.PLAY - | ContextMenuItemKey.PLAY_LAST - | ContextMenuItemKey.PLAY_NEXT - | ContextMenuItemKey.PLAY_SHUFFLED - | ContextMenuItemKey.PLAY_SIMILAR_SONGS - | ContextMenuItemKey.REMOVE_FROM_FAVORITES - | ContextMenuItemKey.REMOVE_FROM_PLAYLIST - | ContextMenuItemKey.REMOVE_FROM_QUEUE - | ContextMenuItemKey.SET_RATING - | ContextMenuItemKey.SET_RATING_1 - | ContextMenuItemKey.SET_RATING_2 - | ContextMenuItemKey.SET_RATING_3 - | ContextMenuItemKey.SET_RATING_4 - | ContextMenuItemKey.SET_RATING_5 - | ContextMenuItemKey.SHARE_ITEM - | ContextMenuItemKey.SHOW_DETAILS; - -export type ContextMenuItems = Array; - -const ICON_MAP: Partial> = { - [ContextMenuItemKey.ADD_TO_FAVORITES]: 'favorite', - [ContextMenuItemKey.ADD_TO_PLAYLIST]: 'playlistAdd', - [ContextMenuItemKey.DELETE_PLAYLIST]: 'playlistDelete', - [ContextMenuItemKey.DESELECT_ALL]: 'remove', - [ContextMenuItemKey.DOWNLOAD]: 'download', - [ContextMenuItemKey.GO_TO_ALBUM]: 'album', - [ContextMenuItemKey.GO_TO_ALBUM_ARTIST]: 'artist', - [ContextMenuItemKey.MOVE_TO_BOTTOM_OF_QUEUE]: 'arrowDownToLine', - [ContextMenuItemKey.MOVE_TO_NEXT_OF_QUEUE]: 'mediaPlayNext', - [ContextMenuItemKey.MOVE_TO_TOP_OF_QUEUE]: 'arrowUpToLine', - [ContextMenuItemKey.PLAY]: 'mediaPlay', - [ContextMenuItemKey.PLAY_LAST]: 'mediaPlayLast', - [ContextMenuItemKey.PLAY_NEXT]: 'mediaPlayNext', - [ContextMenuItemKey.PLAY_SHUFFLED]: 'mediaShuffle', - [ContextMenuItemKey.PLAY_SIMILAR_SONGS]: 'radio', - [ContextMenuItemKey.REMOVE_FROM_FAVORITES]: 'unfavorite', - [ContextMenuItemKey.REMOVE_FROM_PLAYLIST]: 'playlistDelete', - [ContextMenuItemKey.REMOVE_FROM_QUEUE]: 'delete', - [ContextMenuItemKey.SET_RATING]: 'star', - [ContextMenuItemKey.SHARE_ITEM]: 'share', - [ContextMenuItemKey.SHOW_DETAILS]: 'info', -}; - -// export const convertToContextMenuItems = ( -// definitions: ContextMenuItemDefinition[], -// handlers: ContextMenuHandlers, -// ): ContextMenuItemOptions[] => { -// const items: ContextMenuItemOptions[] = []; - -// for (const def of definitions) { -// if ('divider' in def && def.divider) { -// items.push({ key: 'divider' }); -// continue; -// } - -// if (!('key' in def)) { -// continue; -// } - -// const handler = handlers[def.key]; - -// if (!handler) { -// continue; -// } - -// const icon = ICON_MAP[def.key]; -// const menuItem: ContextMenuItemOptions = { -// disabled: def.disabled, -// icon: icon ? : undefined, -// key: def.key, -// onClick: handler, -// }; - -// if (def.children) { -// menuItem.items = undefined; -// } - -// items.push(menuItem); -// } - -// // Remove trailing divider -// const lastItem = items[items.length - 1]; -// if (items.length > 0 && lastItem && 'type' in lastItem && lastItem.type === 'divider') { -// items.pop(); -// } - -// return items; -// }; - -// export const QUEUE_CONTEXT_MENU_ITEMS = (): ContextMenuItemOptions[] => { -// return [ -// { key: ContextMenuItemKey.REMOVE_FROM_QUEUE }, -// // { key: ContextMenuItemKey.MOVE_TO_NEXT_OF_QUEUE }, -// // { key: ContextMenuItemKey.MOVE_TO_BOTTOM_OF_QUEUE }, -// // { key: 'divider_1' }, -// // { key: ContextMenuItemKey.MOVE_TO_TOP_OF_QUEUE }, -// // { key: 'divider_2' }, -// // { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// // { key: ContextMenuItemKey.ADD_TO_FAVORITES }, -// // { key: 'divider_3' }, -// // { key: ContextMenuItemKey.REMOVE_FROM_FAVORITES }, -// // { key: ContextMenuItemKey.SET_RATING }, -// // { key: ContextMenuItemKey.DESELECT_ALL }, -// // { key: 'divider_4' }, -// // { key: ContextMenuItemKey.DOWNLOAD }, -// // { key: 'divider_5' }, -// // { key: ContextMenuItemKey.SHARE_ITEM }, -// // { key: ContextMenuItemKey.GO_TO_ALBUM }, -// // { key: ContextMenuItemKey.GO_TO_ALBUM_ARTIST }, -// // { key: 'divider_6' }, -// // { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; -// }; - -// export const SONG_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { -// children: [ -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// ], -// key: ContextMenuItemKey.PLAY, -// }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SIMILAR_SONGS }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// { key: ContextMenuItemKey.ADD_TO_FAVORITES }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.REMOVE_FROM_FAVORITES }, -// { -// children: [ -// { key: ContextMenuItemKey.SET_RATING_1 }, -// { key: ContextMenuItemKey.SET_RATING_2 }, -// { key: ContextMenuItemKey.SET_RATING_3 }, -// { key: ContextMenuItemKey.SET_RATING_4 }, -// { key: ContextMenuItemKey.SET_RATING_5 }, -// ], -// key: ContextMenuItemKey.SET_RATING, -// }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.DOWNLOAD }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHARE_ITEM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM_ARTIST }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; - -// export const SONG_ALBUM_PAGE: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// ]; - -// export const PLAYLIST_SONG_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SIMILAR_SONGS }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.REMOVE_FROM_PLAYLIST }, -// { key: ContextMenuItemKey.ADD_TO_FAVORITES }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.REMOVE_FROM_FAVORITES }, -// { -// children: [ -// { key: ContextMenuItemKey.SET_RATING_1 }, -// { key: ContextMenuItemKey.SET_RATING_2 }, -// { key: ContextMenuItemKey.SET_RATING_3 }, -// { key: ContextMenuItemKey.SET_RATING_4 }, -// { key: ContextMenuItemKey.SET_RATING_5 }, -// ], -// key: ContextMenuItemKey.SET_RATING, -// }, -// { key: ContextMenuItemKey.DOWNLOAD }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHARE_ITEM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM_ARTIST }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; - -// export const SMART_PLAYLIST_SONG_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SIMILAR_SONGS }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// { key: ContextMenuItemKey.ADD_TO_FAVORITES }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.REMOVE_FROM_FAVORITES }, -// { -// children: [ -// { key: ContextMenuItemKey.SET_RATING_1 }, -// { key: ContextMenuItemKey.SET_RATING_2 }, -// { key: ContextMenuItemKey.SET_RATING_3 }, -// { key: ContextMenuItemKey.SET_RATING_4 }, -// { key: ContextMenuItemKey.SET_RATING_5 }, -// ], -// key: ContextMenuItemKey.SET_RATING, -// }, -// { key: ContextMenuItemKey.DOWNLOAD }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHARE_ITEM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM_ARTIST }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; - -// export const ALBUM_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// { key: ContextMenuItemKey.ADD_TO_FAVORITES }, -// { key: ContextMenuItemKey.REMOVE_FROM_FAVORITES }, -// { -// children: [ -// { key: ContextMenuItemKey.SET_RATING_1 }, -// { key: ContextMenuItemKey.SET_RATING_2 }, -// { key: ContextMenuItemKey.SET_RATING_3 }, -// { key: ContextMenuItemKey.SET_RATING_4 }, -// { key: ContextMenuItemKey.SET_RATING_5 }, -// ], -// key: ContextMenuItemKey.SET_RATING, -// }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHARE_ITEM }, -// { key: ContextMenuItemKey.GO_TO_ALBUM_ARTIST }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; - -// export const GENRE_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// ]; - -// export const ARTIST_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.ADD_TO_PLAYLIST }, -// { key: ContextMenuItemKey.ADD_TO_FAVORITES }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.REMOVE_FROM_FAVORITES }, -// { -// children: [ -// { key: ContextMenuItemKey.SET_RATING_1 }, -// { key: ContextMenuItemKey.SET_RATING_2 }, -// { key: ContextMenuItemKey.SET_RATING_3 }, -// { key: ContextMenuItemKey.SET_RATING_4 }, -// { key: ContextMenuItemKey.SET_RATING_5 }, -// ], -// key: ContextMenuItemKey.SET_RATING, -// }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHARE_ITEM }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; - -// export const PLAYLIST_CONTEXT_MENU_ITEMS: ContextMenuItemDefinition[] = [ -// { key: ContextMenuItemKey.PLAY }, -// { key: ContextMenuItemKey.PLAY_LAST }, -// { key: ContextMenuItemKey.PLAY_NEXT }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.PLAY_SHUFFLED }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHARE_ITEM }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.DELETE_PLAYLIST }, -// { key: ContextMenuItemKey.DIVIDER }, -// { key: ContextMenuItemKey.SHOW_DETAILS }, -// ]; diff --git a/src/renderer/features/context-menu/events.ts b/src/renderer/features/context-menu/events.ts deleted file mode 100644 index ee1db1dce..000000000 --- a/src/renderer/features/context-menu/events.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { GridOptions, RowNode } from '@ag-grid-community/core'; - -import { LibraryItem } from '/@/shared/types/domain-types'; -import { createUseExternalEvents } from '/@/shared/utils/create-use-external-events'; - -export type ContextMenuEvents = { - closeContextMenu: () => void; - openContextMenu: (args: OpenContextMenuProps) => void; -}; - -export const CONTEXT_MENU_ITEM_MAPPING: { [k in ContextMenuItemKeys]?: string } = { - [ContextMenuItemKey.MOVE_TO_BOTTOM_OF_QUEUE]: 'moveToBottom', - [ContextMenuItemKey.MOVE_TO_TOP_OF_QUEUE]: 'moveToTop', - [ContextMenuItemKey.PLAY_LAST]: 'addLast', - [ContextMenuItemKey.PLAY_NEXT]: 'addNext', -}; - -export type SetContextMenuItems = { - children?: boolean; - disabled?: boolean; - divider?: boolean; - id: ContextMenuItemKeys; - onClick?: () => void; -}[]; - -export const [useContextMenuEvents, createEvent] = - createUseExternalEvents('context-menu'); - -export const openContextMenu = createEvent('openContextMenu'); -export const closeContextMenu = createEvent('closeContextMenu'); diff --git a/src/renderer/features/player/components/full-screen-player.tsx b/src/renderer/features/player/components/full-screen-player.tsx index b350c2ea7..a2d9ae7ac 100644 --- a/src/renderer/features/player/components/full-screen-player.tsx +++ b/src/renderer/features/player/components/full-screen-player.tsx @@ -6,15 +6,14 @@ import { useLocation } from 'react-router'; import styles from './full-screen-player.module.css'; -import { TableConfigDropdown } from '/@/renderer/components/virtual-table'; import { FullScreenPlayerImage } from '/@/renderer/features/player/components/full-screen-player-image'; import { FullScreenPlayerQueue } from '/@/renderer/features/player/components/full-screen-player-queue'; import { useFastAverageColor } from '/@/renderer/hooks'; import { - usePlayerSong, useFullScreenPlayerStore, useFullScreenPlayerStoreActions, useLyricsSettings, + usePlayerSong, useSettingsStore, useSettingsStoreActions, useWindowSettings, @@ -348,7 +347,6 @@ const Controls = ({ isPageHovered }: ControlsProps) => { - diff --git a/src/renderer/features/playlists/components/playlist-detail-song-list-header-filters.tsx b/src/renderer/features/playlists/components/playlist-detail-song-list-header-filters.tsx index 6c0c00e67..f0467a246 100644 --- a/src/renderer/features/playlists/components/playlist-detail-song-list-header-filters.tsx +++ b/src/renderer/features/playlists/components/playlist-detail-song-list-header-filters.tsx @@ -1,32 +1,25 @@ -import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact'; - import { closeAllModals, openModal } from '@mantine/modals'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import debounce from 'lodash/debounce'; -import { ChangeEvent, MouseEvent, MutableRefObject, useCallback } from 'react'; +import { ChangeEvent, MouseEvent, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router'; import i18n from '/@/i18n/i18n'; import { queryKeys } from '/@/renderer/api/query-keys'; -import { SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; import { playlistsQueries } from '/@/renderer/features/playlists/api/playlists-api'; import { openUpdatePlaylistModal } from '/@/renderer/features/playlists/components/update-playlist-form'; import { useDeletePlaylist } from '/@/renderer/features/playlists/mutations/delete-playlist-mutation'; -import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { MoreButton } from '/@/renderer/features/shared/components/more-button'; import { OrderToggleButton } from '/@/renderer/features/shared/components/order-toggle-button'; import { SearchInput } from '/@/renderer/features/shared/components/search-input'; import { useContainerQuery } from '/@/renderer/hooks'; import { AppRoute } from '/@/renderer/router/routes'; import { - PersistedTableColumn, useCurrentServer, usePlaylistDetailStore, useSetPlaylistDetailFilters, - useSetPlaylistDetailTable, useSetPlaylistStore, - useSetPlaylistTablePagination, } from '/@/renderer/store'; import { Button } from '/@/shared/components/button/button'; import { Divider } from '/@/shared/components/divider/divider'; @@ -242,7 +235,7 @@ const FILTERS = { interface PlaylistDetailSongListHeaderFiltersProps { handlePlay: (playType: Play) => void; handleToggleShowQueryBuilder: () => void; - tableRef: MutableRefObject; + tableRef: any; } export const PlaylistDetailSongListHeaderFilters = ({ @@ -269,107 +262,37 @@ export const PlaylistDetailSongListHeaderFilters = ({ const cq = useContainerQuery(); - const setPagination = useSetPlaylistTablePagination(); - const setTable = useSetPlaylistDetailTable(); - const sortByLabel = (server?.type && FILTERS[server.type as keyof typeof FILTERS].find((f) => f.value === sortBy)?.name) || 'Unknown'; - const handleItemSize = (e: number) => { - setTable({ rowHeight: e }); - }; - - const debouncedHandleItemSize = debounce(handleItemSize, 20); - const handleFilterChange = useCallback(async () => { - tableRef.current?.api.redrawRows(); - tableRef.current?.api.ensureIndexVisible(0, 'top'); - - if (page.display === ListDisplayType.TABLE_PAGINATED) { - setPagination({ data: { currentPage: 0 } }); - } - }, [tableRef, page.display, setPagination]); + // tableRef.current?.api.redrawRows(); + // tableRef.current?.api.ensureIndexVisible(0, 'top'); + // if (page.display === ListDisplayType.TABLE) { + // setPagination({ data: { currentPage: 0 } }); + // } + }, []); const handleRefresh = () => { queryClient.invalidateQueries({ queryKey: queryKeys.playlists.songList(server?.id || '', playlistId), }); - handleFilterChange(); }; - const handleSetSortBy = useCallback( - (e: MouseEvent) => { - if (!e.currentTarget?.value || !server?.type) return; + const handleSetSortBy = useCallback((e: MouseEvent) => {}, []); - const newSortOrder = FILTERS[server.type as keyof typeof FILTERS].find( - (f) => f.value === e.currentTarget.value, - )?.defaultOrder; + const handleToggleSortOrder = useCallback(() => {}, [ + sortOrder, + handleFilterChange, + playlistId, + setFilter, + ]); - setFilter(playlistId, { - sortBy: e.currentTarget.value as SongListSort, - sortOrder: newSortOrder || SortOrder.ASC, - }); + const handleSearch = debounce((e: ChangeEvent) => {}, 500); - handleFilterChange(); - }, - [handleFilterChange, playlistId, server?.type, setFilter], - ); - - const handleToggleSortOrder = useCallback(() => { - const newSortOrder = sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC; - setFilter(playlistId, { sortOrder: newSortOrder }); - handleFilterChange(); - }, [sortOrder, handleFilterChange, playlistId, setFilter]); - - const handleSearch = debounce((e: ChangeEvent) => { - setFilter(playlistId, { searchTerm: e.target.value }); - handleFilterChange(); - }, 500); - - const handleSetViewType = useCallback( - (displayType: ListDisplayType) => { - setPage({ detail: { ...page, display: displayType } }); - }, - [page, setPage], - ); - - const handleTableColumns = (values: string[]) => { - const existingColumns = page.table.columns; - - if (values.length === 0) { - return setTable({ - columns: [], - }); - } - - // If adding a column - if (values.length > existingColumns.length) { - const newColumn = { - column: values[values.length - 1], - width: 100, - } as PersistedTableColumn; - - setTable({ columns: [...existingColumns, newColumn] }); - } else { - // If removing a column - const removed = existingColumns.filter((column) => !values.includes(column.column)); - const newColumns = existingColumns.filter((column) => !removed.includes(column)); - - setTable({ columns: newColumns }); - } - - return tableRef.current?.api.sizeColumnsToFit(); - }; - - const handleAutoFitColumns = (autoFitColumns: boolean) => { - setTable({ autoFit: autoFitColumns }); - - if (autoFitColumns) { - tableRef.current?.api.sizeColumnsToFit(); - } - }; + const handleSetViewType = useCallback((displayType: ListDisplayType) => {}, [page, setPage]); const deletePlaylistMutation = useDeletePlaylist({}); @@ -501,20 +424,7 @@ export const PlaylistDetailSongListHeaderFilters = ({ - - column.column)} - tableColumnsData={SONG_TABLE_COLUMNS} - /> - + ); }; diff --git a/src/renderer/features/playlists/components/playlist-list-header-filters.tsx b/src/renderer/features/playlists/components/playlist-list-header-filters.tsx index a3dcf1b53..6a3402c89 100644 --- a/src/renderer/features/playlists/components/playlist-list-header-filters.tsx +++ b/src/renderer/features/playlists/components/playlist-list-header-filters.tsx @@ -1,7 +1,7 @@ import { closeAllModals, openModal } from '@mantine/modals'; import { useTranslation } from 'react-i18next'; -import { PLAYLIST_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; +import { PLAYLIST_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; import { CreatePlaylistForm } from '/@/renderer/features/playlists/components/create-playlist-form'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; import { ListFilters } from '/@/renderer/features/shared/components/list-filters'; diff --git a/src/renderer/features/search/components/search-header.tsx b/src/renderer/features/search/components/search-header.tsx index cb6094639..6229247d5 100644 --- a/src/renderer/features/search/components/search-header.tsx +++ b/src/renderer/features/search/components/search-header.tsx @@ -3,9 +3,12 @@ import { ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { generatePath, Link, useParams, useSearchParams } from 'react-router'; -import { ALBUM_ARTIST_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns'; +import { + ALBUM_ARTIST_TABLE_COLUMNS, + ALBUM_TABLE_COLUMNS, + SONG_TABLE_COLUMNS, +} from '/@/renderer/components/item-list/item-table-list/default-columns'; import { PageHeader } from '/@/renderer/components/page-header/page-header'; -import { ALBUM_TABLE_COLUMNS, SONG_TABLE_COLUMNS } from '/@/renderer/components/virtual-table'; import { FilterBar } from '/@/renderer/features/shared/components/filter-bar'; import { LibraryHeaderBar } from '/@/renderer/features/shared/components/library-header-bar'; import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu'; diff --git a/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx b/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx index b69db021f..dfc38c693 100644 --- a/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx +++ b/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx @@ -111,6 +111,7 @@ const PlaylistRowButton = ({ name, onPlay, to, ...props }: PlaylistRowButtonProp openContextModal({ innerProps: modalProps, modal: 'addToPlaylist', + size: 'lg', title: t('form.addToPlaylist.title', { postProcess: 'titleCase' }), }); }, diff --git a/src/renderer/features/similar-songs/components/similar-songs-list.tsx b/src/renderer/features/similar-songs/components/similar-songs-list.tsx index cfc884e6d..c403d83a2 100644 --- a/src/renderer/features/similar-songs/components/similar-songs-list.tsx +++ b/src/renderer/features/similar-songs/components/similar-songs-list.tsx @@ -1,16 +1,13 @@ import { RowDoubleClickedEvent } from '@ag-grid-community/core'; import { AgGridReact } from '@ag-grid-community/react'; import { useQuery } from '@tanstack/react-query'; -import { useMemo, useRef } from 'react'; +import { useRef } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; -import { getColumnDefs, VirtualTable } from '/@/renderer/components/virtual-table'; import { ErrorFallback } from '/@/renderer/features/action-required/components/error-fallback'; -import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items'; import { songsQueries } from '/@/renderer/features/songs/api/songs-api'; -import { usePlayButtonBehavior, useTableSettings } from '/@/renderer/store'; import { Spinner } from '/@/shared/components/spinner/spinner'; -import { LibraryItem, Song } from '/@/shared/types/domain-types'; +import { Song } from '/@/shared/types/domain-types'; export type SimilarSongsListProps = { count?: number; @@ -56,26 +53,6 @@ export const SimilarSongsList = ({ count, fullScreen, song }: SimilarSongsListPr return songQuery.isLoading ? ( ) : ( - - {/* data.data.uniqueId} - onCellContextMenu={onCellContextMenu} - onCellDoubleClicked={handleRowDoubleClick} - ref={tableRef} - rowBuffer={50} - rowData={songQuery.data ?? []} - rowHeight={tableConfig.rowHeight || 40} - shouldUpdateSong - /> */} - + ); };