mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-03 03:11:40 +00:00
Migrate to Mantine v8 and Design Changes (#961)
* mantine v8 migration * various design changes and improvements
This commit is contained in:
parent
bea55d48a8
commit
c1330d92b2
473 changed files with 12469 additions and 11607 deletions
|
|
@ -1,16 +1,12 @@
|
|||
import { ColDef, RowDoubleClickedEvent } from '@ag-grid-community/core';
|
||||
import { Box, Grid, Group, Stack } from '@mantine/core';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaLastfmSquare } from 'react-icons/fa';
|
||||
import { RiHeartFill, RiHeartLine, RiMoreFill } from 'react-icons/ri';
|
||||
import { SiMusicbrainz } from 'react-icons/si';
|
||||
import { generatePath, useParams } from 'react-router';
|
||||
import { createSearchParams, Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Button, Spoiler, TextTitle } from '/@/renderer/components';
|
||||
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel';
|
||||
import styles from './album-artist-detail-content.module.css';
|
||||
|
||||
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel/grid-carousel';
|
||||
import { getColumnDefs, VirtualTable } from '/@/renderer/components/virtual-table';
|
||||
import { useAlbumList } from '/@/renderer/features/albums/queries/album-list-query';
|
||||
import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query';
|
||||
|
|
@ -32,6 +28,13 @@ import { AppRoute } from '/@/renderer/router/routes';
|
|||
import { ArtistItem, useCurrentServer } from '/@/renderer/store';
|
||||
import { useGeneralSettings, usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { sanitize } from '/@/renderer/utils/sanitize';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Grid } from '/@/shared/components/grid/grid';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Spoiler } from '/@/shared/components/spoiler/spoiler';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextTitle } from '/@/shared/components/text-title/text-title';
|
||||
import {
|
||||
Album,
|
||||
AlbumArtist,
|
||||
|
|
@ -43,23 +46,6 @@ import {
|
|||
} from '/@/shared/types/domain-types';
|
||||
import { CardRow, Play, TableColumn } from '/@/shared/types/types';
|
||||
|
||||
const ContentContainer = styled.div`
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
`;
|
||||
|
||||
const DetailContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
padding: 1rem 2rem 5rem;
|
||||
overflow: hidden;
|
||||
|
||||
.ag-theme-alpine-dark {
|
||||
--ag-header-background-color: rgb(0 0 0 / 0%) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
interface AlbumArtistDetailContentProps {
|
||||
background?: string;
|
||||
}
|
||||
|
|
@ -216,21 +202,20 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
title: (
|
||||
<Group align="flex-end">
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
weight={700}
|
||||
>
|
||||
{t('page.albumArtistDetail.recentReleases', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</TextTitle>
|
||||
<Button
|
||||
compact
|
||||
component={Link}
|
||||
size="compact-md"
|
||||
to={artistDiscographyLink}
|
||||
uppercase
|
||||
variant="subtle"
|
||||
>
|
||||
{t('page.albumArtistDetail.viewDiscography')}
|
||||
{String(t('page.albumArtistDetail.viewDiscography')).toUpperCase()}
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
|
|
@ -247,8 +232,8 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
order: itemOrder.compilations,
|
||||
title: (
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
weight={700}
|
||||
>
|
||||
{t('page.albumArtistDetail.appearsOn', { postProcess: 'sentenceCase' })}
|
||||
</TextTitle>
|
||||
|
|
@ -262,8 +247,8 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
order: itemOrder.similarArtists,
|
||||
title: (
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
weight={700}
|
||||
>
|
||||
{t('page.albumArtistDetail.relatedArtists', {
|
||||
postProcess: 'sentenceCase',
|
||||
|
|
@ -369,77 +354,77 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
detailQuery?.isLoading ||
|
||||
(server?.type === ServerType.NAVIDROME && enabledItem.topSongs && topSongsQuery?.isLoading);
|
||||
|
||||
if (isLoading) return <ContentContainer ref={cq.ref} />;
|
||||
if (isLoading)
|
||||
return (
|
||||
<div
|
||||
className={styles.contentContainer}
|
||||
ref={cq.ref}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ContentContainer ref={cq.ref}>
|
||||
<LibraryBackgroundOverlay $backgroundColor={background} />
|
||||
<DetailContainer>
|
||||
<Group spacing="md">
|
||||
<div
|
||||
className={styles.contentContainer}
|
||||
ref={cq.ref}
|
||||
>
|
||||
<LibraryBackgroundOverlay backgroundColor={background} />
|
||||
<div className={styles.detailContainer}>
|
||||
<Group gap="md">
|
||||
<PlayButton
|
||||
disabled={albumCount === 0}
|
||||
onClick={() => handlePlay(playButtonBehavior)}
|
||||
/>
|
||||
<Group spacing="xs">
|
||||
<Button
|
||||
compact
|
||||
<Group gap="xs">
|
||||
<ActionIcon
|
||||
icon="favorite"
|
||||
iconProps={{
|
||||
fill: detailQuery?.data?.userFavorite ? 'primary' : undefined,
|
||||
}}
|
||||
loading={
|
||||
createFavoriteMutation.isLoading || deleteFavoriteMutation.isLoading
|
||||
}
|
||||
onClick={handleFavorite}
|
||||
variant="subtle"
|
||||
>
|
||||
{detailQuery?.data?.userFavorite ? (
|
||||
<RiHeartFill
|
||||
color="red"
|
||||
size={20}
|
||||
/>
|
||||
) : (
|
||||
<RiHeartLine size={20} />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
size="lg"
|
||||
variant="transparent"
|
||||
/>
|
||||
<ActionIcon
|
||||
icon="ellipsisHorizontal"
|
||||
onClick={(e) => {
|
||||
if (!detailQuery?.data) return;
|
||||
handleGeneralContextMenu(e, [detailQuery.data!]);
|
||||
}}
|
||||
variant="subtle"
|
||||
>
|
||||
<RiMoreFill size={20} />
|
||||
</Button>
|
||||
size="lg"
|
||||
variant="transparent"
|
||||
/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group spacing="md">
|
||||
<Group gap="md">
|
||||
<Button
|
||||
compact
|
||||
component={Link}
|
||||
size="compact-md"
|
||||
to={artistDiscographyLink}
|
||||
uppercase
|
||||
variant="subtle"
|
||||
>
|
||||
{t('page.albumArtistDetail.viewDiscography')}
|
||||
{String(t('page.albumArtistDetail.viewDiscography')).toUpperCase()}
|
||||
</Button>
|
||||
<Button
|
||||
compact
|
||||
component={Link}
|
||||
size="compact-md"
|
||||
to={artistSongsLink}
|
||||
uppercase
|
||||
variant="subtle"
|
||||
>
|
||||
{t('page.albumArtistDetail.viewAllTracks')}
|
||||
{String(t('page.albumArtistDetail.viewAllTracks')).toUpperCase()}
|
||||
</Button>
|
||||
</Group>
|
||||
{showGenres ? (
|
||||
<Box component="section">
|
||||
<Group spacing="sm">
|
||||
<section>
|
||||
<Group gap="sm">
|
||||
{detailQuery?.data?.genres?.map((genre) => (
|
||||
<Button
|
||||
compact
|
||||
component={Link}
|
||||
key={`genre-${genre.id}`}
|
||||
radius="md"
|
||||
size="md"
|
||||
size="compact-md"
|
||||
to={generatePath(genrePath, {
|
||||
genreId: genre.id,
|
||||
})}
|
||||
|
|
@ -449,70 +434,65 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
</Button>
|
||||
))}
|
||||
</Group>
|
||||
</Box>
|
||||
</section>
|
||||
) : null}
|
||||
{externalLinks && (lastFM || musicBrainz) ? (
|
||||
<Box component="section">
|
||||
<Group spacing="sm">
|
||||
{lastFM && (
|
||||
<Button
|
||||
compact
|
||||
component="a"
|
||||
href={`https://www.last.fm/music/${encodeURIComponent(
|
||||
detailQuery?.data?.name || '',
|
||||
)}`}
|
||||
radius="md"
|
||||
rel="noopener noreferrer"
|
||||
size="md"
|
||||
target="_blank"
|
||||
tooltip={{
|
||||
label: t('action.openIn.lastfm'),
|
||||
}}
|
||||
variant="subtle"
|
||||
>
|
||||
<FaLastfmSquare size={25} />
|
||||
</Button>
|
||||
)}
|
||||
{musicBrainz && mbzId ? (
|
||||
<Button
|
||||
compact
|
||||
<section>
|
||||
<Group gap="sm">
|
||||
<ActionIcon
|
||||
component="a"
|
||||
href={`https://www.last.fm/music/${encodeURIComponent(
|
||||
detailQuery?.data?.name || '',
|
||||
)}`}
|
||||
icon="brandLastfm"
|
||||
iconProps={{
|
||||
fill: 'default',
|
||||
size: 'xl',
|
||||
}}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
tooltip={{
|
||||
label: t('action.openIn.lastfm'),
|
||||
}}
|
||||
variant="subtle"
|
||||
/>
|
||||
{mbzId ? (
|
||||
<ActionIcon
|
||||
component="a"
|
||||
href={`https://musicbrainz.org/artist/${mbzId}`}
|
||||
radius="md"
|
||||
icon="brandMusicBrainz"
|
||||
iconProps={{
|
||||
fill: 'default',
|
||||
size: 'xl',
|
||||
}}
|
||||
rel="noopener noreferrer"
|
||||
size="md"
|
||||
target="_blank"
|
||||
tooltip={{
|
||||
label: t('action.openIn.musicbrainz'),
|
||||
}}
|
||||
variant="subtle"
|
||||
>
|
||||
<SiMusicbrainz size={25} />
|
||||
</Button>
|
||||
/>
|
||||
) : null}
|
||||
</Group>
|
||||
</Box>
|
||||
</section>
|
||||
) : null}
|
||||
<Grid>
|
||||
<Grid gutter="xl">
|
||||
{biography ? (
|
||||
<Grid.Col
|
||||
order={itemOrder.biography}
|
||||
span={12}
|
||||
>
|
||||
<Box
|
||||
component="section"
|
||||
maw="1280px"
|
||||
>
|
||||
<section style={{ maxWidth: '1280px' }}>
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
weight={700}
|
||||
>
|
||||
{t('page.albumArtistDetail.about', {
|
||||
artist: detailQuery?.data?.name,
|
||||
})}
|
||||
</TextTitle>
|
||||
<Spoiler dangerouslySetInnerHTML={{ __html: biography }} />
|
||||
</Box>
|
||||
</section>
|
||||
</Grid.Col>
|
||||
) : null}
|
||||
{showTopSongs ? (
|
||||
|
|
@ -520,26 +500,26 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
order={itemOrder.topSongs}
|
||||
span={12}
|
||||
>
|
||||
<Box component="section">
|
||||
<section>
|
||||
<Group
|
||||
noWrap
|
||||
position="apart"
|
||||
justify="space-between"
|
||||
wrap="nowrap"
|
||||
>
|
||||
<Group
|
||||
align="flex-end"
|
||||
noWrap
|
||||
wrap="nowrap"
|
||||
>
|
||||
<TextTitle
|
||||
fw={700}
|
||||
order={2}
|
||||
weight={700}
|
||||
>
|
||||
{t('page.albumArtistDetail.topSongs', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</TextTitle>
|
||||
<Button
|
||||
compact
|
||||
component={Link}
|
||||
size="compact-md"
|
||||
to={generatePath(
|
||||
AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL_TOP_SONGS,
|
||||
{
|
||||
|
|
@ -577,7 +557,7 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
suppressLoadingOverlay
|
||||
suppressRowDrag
|
||||
/>
|
||||
</Box>
|
||||
</section>
|
||||
</Grid.Col>
|
||||
) : null}
|
||||
|
||||
|
|
@ -589,8 +569,8 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
order={carousel.order}
|
||||
span={12}
|
||||
>
|
||||
<Box component="section">
|
||||
<Stack spacing="xl">
|
||||
<section>
|
||||
<Stack gap="xl">
|
||||
<MemoizedSwiperGridCarousel
|
||||
cardRows={
|
||||
cardRows[carousel.itemType as keyof typeof cardRows]
|
||||
|
|
@ -614,11 +594,11 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
|
|||
uniqueId={carousel.uniqueId}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
</section>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
</DetailContainer>
|
||||
</ContentContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue