Migrate to Mantine v8 and Design Changes (#961)

* mantine v8 migration

* various design changes and improvements
This commit is contained in:
Jeff 2025-06-24 00:04:36 -07:00 committed by GitHub
parent bea55d48a8
commit c1330d92b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
473 changed files with 12469 additions and 11607 deletions

View file

@ -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>
);
};