feishin/src/renderer/features/albums/components/album-detail-content.tsx

318 lines
9.3 KiB
TypeScript
Raw Normal View History

2022-12-29 18:45:01 -08:00
import { MutableRefObject, useCallback, useMemo } from 'react';
import {
Button,
DropdownMenu,
getColumnDefs,
GridCarousel,
TextTitle,
useFixedTableHeader,
2022-12-29 18:45:01 -08:00
VirtualTable,
} from '/@/renderer/components';
import { ColDef, RowDoubleClickedEvent } from '@ag-grid-community/core';
2022-12-29 18:45:01 -08:00
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
import { Box, Group, Stack } from '@mantine/core';
2022-12-29 18:45:01 -08:00
import { useSetState } from '@mantine/hooks';
import { RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
2023-01-13 14:07:57 -08:00
import { generatePath, useParams } from 'react-router';
2022-12-29 18:45:01 -08:00
import { useAlbumDetail } from '/@/renderer/features/albums/queries/album-detail-query';
import { useSongListStore } from '/@/renderer/store';
2023-01-13 14:07:57 -08:00
import { Link } from 'react-router-dom';
2022-12-29 18:45:01 -08:00
import styled from 'styled-components';
import { AppRoute } from '/@/renderer/router/routes';
import { useContainerQuery } from '/@/renderer/hooks';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { useHandleTableContextMenu } from '/@/renderer/features/context-menu';
2023-01-05 21:59:07 -08:00
import { Play } from '/@/renderer/types';
2022-12-29 18:45:01 -08:00
import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import {
PlayButton,
PLAY_TYPES,
useCreateFavorite,
useDeleteFavorite,
} from '/@/renderer/features/shared';
2022-12-29 18:45:01 -08:00
import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query';
2023-01-06 13:50:40 -08:00
import { AlbumListSort, LibraryItem, QueueSong, SortOrder } from '/@/renderer/api/types';
2022-12-31 19:26:58 -08:00
import { usePlayQueueAdd } from '/@/renderer/features/player';
2022-12-29 18:45:01 -08:00
const ContentContainer = styled.div`
position: relative;
2022-12-29 18:45:01 -08:00
display: flex;
flex-direction: column;
max-width: 1920px;
padding: 1rem 2rem 5rem;
2022-12-29 18:45:01 -08:00
overflow: hidden;
.ag-theme-alpine-dark {
2023-01-06 23:34:31 -08:00
--ag-header-background-color: rgba(0, 0, 0, 0%) !important;
2022-12-29 18:45:01 -08:00
}
.ag-header {
margin-bottom: 0.5rem;
}
2022-12-29 18:45:01 -08:00
`;
interface AlbumDetailContentProps {
tableRef: MutableRefObject<AgGridReactType | null>;
}
export const AlbumDetailContent = ({ tableRef }: AlbumDetailContentProps) => {
const { albumId } = useParams() as { albumId: string };
const detailQuery = useAlbumDetail({ id: albumId });
const cq = useContainerQuery();
2022-12-31 19:26:58 -08:00
const handlePlayQueueAdd = usePlayQueueAdd();
2022-12-29 18:45:01 -08:00
const page = useSongListStore();
const columnDefs: ColDef[] = useMemo(
() =>
getColumnDefs(page.table.columns).filter((c) => c.colId !== 'album' && c.colId !== 'artist'),
[page.table.columns],
);
const [pagination, setPagination] = useSetState({
artist: 0,
});
const handleNextPage = useCallback(
(key: 'artist') => {
setPagination({
[key]: pagination[key as keyof typeof pagination] + 1,
});
},
[pagination, setPagination],
);
const handlePreviousPage = useCallback(
(key: 'artist') => {
setPagination({
[key]: pagination[key as keyof typeof pagination] - 1,
});
},
[pagination, setPagination],
);
const itemsPerPage = cq.isXl ? 9 : cq.isLg ? 7 : cq.isMd ? 5 : cq.isSm ? 4 : 3;
const artistQuery = useAlbumList(
{
jfParams: {
albumArtistIds: detailQuery?.data?.albumArtists[0]?.id,
},
limit: itemsPerPage,
ndParams: {
artist_id: detailQuery?.data?.albumArtists[0]?.id,
},
sortBy: AlbumListSort.YEAR,
sortOrder: SortOrder.DESC,
startIndex: pagination.artist * itemsPerPage,
},
{
cacheTime: 1000 * 60,
enabled: detailQuery?.data?.albumArtists[0]?.id !== undefined,
keepPreviousData: true,
staleTime: 1000 * 60,
},
);
const carousels = [
{
data: artistQuery?.data?.items,
loading: artistQuery?.isLoading || artistQuery.isFetching,
pagination: {
handleNextPage: () => handleNextPage('artist'),
handlePreviousPage: () => handlePreviousPage('artist'),
hasPreviousPage: pagination.artist > 0,
itemsPerPage,
},
title: (
<TextTitle
fw="bold"
order={3}
>
More from this artist
</TextTitle>
),
uniqueId: 'mostPlayed',
},
];
const playButtonBehavior = usePlayButtonBehavior();
const handlePlay = async (playType?: Play) => {
handlePlayQueueAdd?.({
byData: detailQuery?.data?.songs,
play: playType || playButtonBehavior,
});
};
const handleContextMenu = useHandleTableContextMenu(LibraryItem.SONG, SONG_CONTEXT_MENU_ITEMS);
2022-12-29 18:45:01 -08:00
2023-01-06 13:50:40 -08:00
const handleRowDoubleClick = (e: RowDoubleClickedEvent<QueueSong>) => {
if (!e.data) return;
handlePlayQueueAdd?.({
byData: [e.data],
play: playButtonBehavior,
});
};
const createFavoriteMutation = useCreateFavorite();
const deleteFavoriteMutation = useDeleteFavorite();
const handleFavorite = () => {
if (!detailQuery?.data) return;
if (detailQuery.data.userFavorite) {
deleteFavoriteMutation.mutate({
query: {
id: [detailQuery.data.id],
type: LibraryItem.ALBUM,
},
});
} else {
createFavoriteMutation.mutate({
query: {
id: [detailQuery.data.id],
type: LibraryItem.ALBUM,
},
});
}
};
2023-01-13 14:07:57 -08:00
const showGenres = detailQuery?.data?.genres ? detailQuery?.data?.genres.length !== 0 : false;
const { intersectRef, tableContainerRef } = useFixedTableHeader();
2022-12-29 18:45:01 -08:00
return (
<ContentContainer>
2023-01-13 14:07:57 -08:00
<Box component="section">
<Group
ref={showGenres ? null : intersectRef}
className="test"
pb="2rem"
pt="1rem"
spacing="lg"
>
<PlayButton onClick={() => handlePlay(playButtonBehavior)} />
<Group spacing="xs">
<Button
compact
loading={createFavoriteMutation.isLoading || deleteFavoriteMutation.isLoading}
variant="subtle"
onClick={handleFavorite}
>
{detailQuery?.data?.userFavorite ? (
<RiHeartFill
color="red"
size={20}
/>
) : (
<RiHeartLine size={20} />
)}
</Button>
<DropdownMenu position="bottom-start">
<DropdownMenu.Target>
<Button
compact
variant="subtle"
>
<RiMoreFill size={20} />
</Button>
</DropdownMenu.Target>
<DropdownMenu.Dropdown>
{PLAY_TYPES.filter((type) => type.play !== playButtonBehavior).map((type) => (
<DropdownMenu.Item
key={`playtype-${type.play}`}
onClick={() => handlePlay(type.play)}
>
{type.label}
</DropdownMenu.Item>
))}
<DropdownMenu.Divider />
<DropdownMenu.Item disabled>Add to playlist</DropdownMenu.Item>
</DropdownMenu.Dropdown>
</DropdownMenu>
</Group>
</Group>
</Box>
{showGenres && (
<Box
ref={showGenres ? intersectRef : null}
component="section"
py="1rem"
>
<Group>
{detailQuery?.data?.genres?.map((genre) => (
2022-12-29 18:45:01 -08:00
<Button
2023-01-13 14:07:57 -08:00
key={`genre-${genre.id}`}
2022-12-29 18:45:01 -08:00
compact
2023-01-13 14:07:57 -08:00
component={Link}
radius="md"
size="sm"
to={generatePath(`${AppRoute.LIBRARY_ALBUMS}?genre=${genre.id}`, { albumId })}
variant="default"
2022-12-29 18:45:01 -08:00
>
2023-01-13 14:07:57 -08:00
{genre.name}
2022-12-29 18:45:01 -08:00
</Button>
2023-01-13 14:07:57 -08:00
))}
</Group>
</Box>
)}
<Box ref={tableContainerRef}>
<VirtualTable
ref={tableRef}
autoFitColumns
autoHeight
2023-01-06 13:50:40 -08:00
deselectOnClickOutside
suppressCellFocus
2023-01-06 13:50:40 -08:00
suppressHorizontalScroll
suppressLoadingOverlay
suppressRowDrag
columnDefs={columnDefs}
enableCellChangeFlash={false}
getRowId={(data) => data.data.id}
rowData={detailQuery.data?.songs}
rowHeight={60}
rowSelection="multiple"
onCellContextMenu={handleContextMenu}
2023-01-06 13:50:40 -08:00
onRowDoubleClicked={handleRowDoubleClick}
/>
</Box>
2022-12-29 18:45:01 -08:00
<Stack
ref={cq.ref}
mt="5rem"
>
{carousels.map((carousel, index) => (
<GridCarousel
key={`carousel-${carousel.uniqueId}-${index}`}
cardRows={[
{
property: 'name',
route: {
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
slugs: [{ idProperty: 'id', slugProperty: 'albumId' }],
},
},
{
arrayProperty: 'name',
property: 'albumArtists',
route: {
2023-01-12 12:45:44 -08:00
route: AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL,
2022-12-29 18:45:01 -08:00
slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }],
},
},
]}
containerWidth={cq.width}
data={carousel.data}
2023-01-12 12:45:44 -08:00
itemType={LibraryItem.ALBUM}
2022-12-29 18:45:01 -08:00
loading={carousel.loading}
pagination={carousel.pagination}
uniqueId={carousel.uniqueId}
>
<GridCarousel.Title>{carousel.title}</GridCarousel.Title>
</GridCarousel>
))}
</Stack>
</ContentContainer>
);
};