mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 04:20:07 +02:00
optimize library headers (#1374)
This commit is contained in:
@@ -14,20 +14,14 @@ import { ItemControls } from '/@/renderer/components/item-list/types';
|
||||
import { AlbumInfiniteCarousel } from '/@/renderer/features/albums/components/album-infinite-carousel';
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { AlbumArtistGridCarousel } from '/@/renderer/features/artists/components/album-artist-grid-carousel';
|
||||
import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller';
|
||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||
import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu';
|
||||
import { DefaultPlayButton } from '/@/renderer/features/shared/components/play-button';
|
||||
import { searchLibraryItems } from '/@/renderer/features/shared/utils';
|
||||
import { useContainerQuery } from '/@/renderer/hooks';
|
||||
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { ArtistItem, useCurrentServer, usePlayerSong } from '/@/renderer/store';
|
||||
import {
|
||||
useGeneralSettings,
|
||||
usePlayButtonBehavior,
|
||||
useSettingsStore,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { useGeneralSettings, useSettingsStore } 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';
|
||||
@@ -52,58 +46,35 @@ import {
|
||||
import { ItemListKey, ListDisplayType, Play } from '/@/shared/types/types';
|
||||
|
||||
interface AlbumArtistActionButtonsProps {
|
||||
albumCount: null | number | undefined;
|
||||
artistDiscographyLink: string;
|
||||
artistSongsLink: string;
|
||||
onFavorite: () => void;
|
||||
onMoreOptions: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onPlay: () => void;
|
||||
userFavorite?: boolean;
|
||||
}
|
||||
|
||||
const AlbumArtistActionButtons = ({
|
||||
albumCount,
|
||||
artistDiscographyLink,
|
||||
artistSongsLink,
|
||||
onFavorite,
|
||||
onMoreOptions,
|
||||
onPlay,
|
||||
userFavorite,
|
||||
}: AlbumArtistActionButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Group gap="md">
|
||||
<DefaultPlayButton disabled={albumCount === 0} onClick={onPlay} />
|
||||
<Group gap="xs">
|
||||
<ActionIcon
|
||||
icon="favorite"
|
||||
iconProps={{
|
||||
fill: userFavorite ? 'primary' : undefined,
|
||||
}}
|
||||
onClick={onFavorite}
|
||||
size="lg"
|
||||
variant="transparent"
|
||||
/>
|
||||
<ActionIcon
|
||||
icon="ellipsisHorizontal"
|
||||
onClick={onMoreOptions}
|
||||
size="lg"
|
||||
variant="transparent"
|
||||
/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group gap="md">
|
||||
<Group gap="lg">
|
||||
<Button
|
||||
component={Link}
|
||||
p={0}
|
||||
size="compact-md"
|
||||
to={artistDiscographyLink}
|
||||
variant="subtle"
|
||||
variant="transparent"
|
||||
>
|
||||
{String(t('page.albumArtistDetail.viewDiscography')).toUpperCase()}
|
||||
</Button>
|
||||
<Button component={Link} size="compact-md" to={artistSongsLink} variant="subtle">
|
||||
<Button
|
||||
component={Link}
|
||||
p={0}
|
||||
size="compact-md"
|
||||
to={artistSongsLink}
|
||||
variant="transparent"
|
||||
>
|
||||
{String(t('page.albumArtistDetail.viewAllTracks')).toUpperCase()}
|
||||
</Button>
|
||||
</Group>
|
||||
@@ -175,8 +146,8 @@ const AlbumArtistMetadataBiography = ({
|
||||
artist: artistName,
|
||||
})}
|
||||
</TextTitle>
|
||||
<Spoiler maxHeight={50}>
|
||||
<div dangerouslySetInnerHTML={{ __html: sanitizedBiography }}></div>
|
||||
<Spoiler>
|
||||
<Text dangerouslySetInnerHTML={{ __html: sanitizedBiography }} />
|
||||
</Spoiler>
|
||||
</section>
|
||||
);
|
||||
@@ -440,7 +411,6 @@ export const AlbumArtistDetailContent = () => {
|
||||
};
|
||||
const routeId = (artistId || albumArtistId) as string;
|
||||
const { ref, ...cq } = useContainerQuery();
|
||||
const { addToQueueByFetch, setFavorite } = usePlayer();
|
||||
const server = useCurrentServer();
|
||||
|
||||
const [enabledItem, itemOrder] = useMemo(() => {
|
||||
@@ -506,21 +476,11 @@ export const AlbumArtistDetailContent = () => {
|
||||
sortBy: AlbumListSort.RELEASE_DATE,
|
||||
sortOrder: SortOrder.DESC,
|
||||
title: (
|
||||
<Group align="flex-end">
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.recentReleases', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</TextTitle>
|
||||
<Button
|
||||
component={Link}
|
||||
size="compact-md"
|
||||
to={artistDiscographyLink}
|
||||
variant="subtle"
|
||||
>
|
||||
{String(t('page.albumArtistDetail.viewDiscography')).toUpperCase()}
|
||||
</Button>
|
||||
</Group>
|
||||
<TextTitle fw={700} order={2}>
|
||||
{t('page.albumArtistDetail.recentReleases', {
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
</TextTitle>
|
||||
),
|
||||
uniqueId: 'recentReleases',
|
||||
},
|
||||
@@ -560,7 +520,6 @@ export const AlbumArtistDetailContent = () => {
|
||||
},
|
||||
];
|
||||
}, [
|
||||
artistDiscographyLink,
|
||||
detailQuery.data?.similarArtists,
|
||||
enabledItem.compilations,
|
||||
enabledItem.recentAlbums,
|
||||
@@ -573,37 +532,6 @@ export const AlbumArtistDetailContent = () => {
|
||||
t,
|
||||
]);
|
||||
|
||||
const playButtonBehavior = usePlayButtonBehavior();
|
||||
|
||||
const handlePlay = async (playType?: Play) => {
|
||||
if (!server?.id) return;
|
||||
addToQueueByFetch(
|
||||
server.id,
|
||||
[routeId],
|
||||
albumArtistId ? LibraryItem.ALBUM_ARTIST : LibraryItem.ARTIST,
|
||||
playType || playButtonBehavior,
|
||||
);
|
||||
};
|
||||
|
||||
const handleFavorite = () => {
|
||||
if (!detailQuery.data) return;
|
||||
setFavorite(
|
||||
detailQuery.data._serverId,
|
||||
[detailQuery.data.id],
|
||||
LibraryItem.ALBUM_ARTIST,
|
||||
!detailQuery.data.userFavorite,
|
||||
);
|
||||
};
|
||||
|
||||
const handleMoreOptions = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (!detailQuery.data) return;
|
||||
ContextMenuController.call({
|
||||
cmd: { items: [detailQuery.data], type: LibraryItem.ALBUM_ARTIST },
|
||||
event: e,
|
||||
});
|
||||
};
|
||||
|
||||
const albumCount = detailQuery.data?.albumCount;
|
||||
const biography =
|
||||
detailQuery.data?.biography && enabledItem.biography ? detailQuery.data.biography : null;
|
||||
const showGenres = detailQuery.data?.genres ? detailQuery.data.genres.length !== 0 : false;
|
||||
@@ -618,13 +546,8 @@ export const AlbumArtistDetailContent = () => {
|
||||
<div className={styles.contentContainer} ref={ref}>
|
||||
<div className={styles.detailContainer}>
|
||||
<AlbumArtistActionButtons
|
||||
albumCount={albumCount}
|
||||
artistDiscographyLink={artistDiscographyLink}
|
||||
artistSongsLink={artistSongsLink}
|
||||
onFavorite={handleFavorite}
|
||||
onMoreOptions={handleMoreOptions}
|
||||
onPlay={() => handlePlay(playButtonBehavior)}
|
||||
userFavorite={detailQuery.data?.userFavorite}
|
||||
/>
|
||||
<Grid gutter="xl">
|
||||
{showGenres && (
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.metadata-group {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
|
||||
@container (min-width: $mantine-breakpoint-sm) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,24 @@ import { forwardRef, Fragment, Ref } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
import styles from './album-artist-detail-header.module.css';
|
||||
|
||||
import { artistsQueries } from '/@/renderer/features/artists/api/artists-api';
|
||||
import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller';
|
||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||
import { LibraryHeader } from '/@/renderer/features/shared/components/library-header';
|
||||
import {
|
||||
LibraryHeader,
|
||||
LibraryHeaderMenu,
|
||||
} from '/@/renderer/features/shared/components/library-header';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
|
||||
import { formatDurationString } from '/@/renderer/utils';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Rating } from '/@/shared/components/rating/rating';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
|
||||
import { Play } from '/@/shared/types/types';
|
||||
|
||||
export const AlbumArtistDetailHeader = forwardRef((_props, ref: Ref<HTMLDivElement>) => {
|
||||
const { albumArtistId, artistId } = useParams() as {
|
||||
@@ -56,7 +63,28 @@ export const AlbumArtistDetailHeader = forwardRef((_props, ref: Ref<HTMLDivEleme
|
||||
},
|
||||
];
|
||||
|
||||
const { setRating } = usePlayer();
|
||||
const { addToQueueByFetch, setFavorite, setRating } = usePlayer();
|
||||
const playButtonBehavior = usePlayButtonBehavior();
|
||||
|
||||
const handlePlay = (type?: Play) => {
|
||||
if (!server?.id || !routeId) return;
|
||||
addToQueueByFetch(
|
||||
server.id,
|
||||
[routeId],
|
||||
LibraryItem.ALBUM_ARTIST,
|
||||
type || playButtonBehavior,
|
||||
);
|
||||
};
|
||||
|
||||
const handleFavorite = () => {
|
||||
if (!detailQuery?.data) return;
|
||||
setFavorite(
|
||||
detailQuery.data._serverId,
|
||||
[detailQuery.data.id],
|
||||
LibraryItem.ALBUM_ARTIST,
|
||||
!detailQuery.data.userFavorite,
|
||||
);
|
||||
};
|
||||
|
||||
const handleUpdateRating = (rating: number) => {
|
||||
if (!detailQuery?.data) return;
|
||||
@@ -78,6 +106,14 @@ export const AlbumArtistDetailHeader = forwardRef((_props, ref: Ref<HTMLDivEleme
|
||||
);
|
||||
};
|
||||
|
||||
const handleMoreOptions = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (!detailQuery?.data) return;
|
||||
ContextMenuController.call({
|
||||
cmd: { items: [detailQuery.data], type: LibraryItem.ALBUM_ARTIST },
|
||||
event: e,
|
||||
});
|
||||
};
|
||||
|
||||
const showRating = detailQuery?.data?._serverType === ServerType.NAVIDROME;
|
||||
|
||||
return (
|
||||
@@ -87,8 +123,8 @@ export const AlbumArtistDetailHeader = forwardRef((_props, ref: Ref<HTMLDivEleme
|
||||
ref={ref}
|
||||
title={detailQuery?.data?.name || ''}
|
||||
>
|
||||
<Stack>
|
||||
<Group>
|
||||
<Stack gap="md" w="100%">
|
||||
<Group className={styles.metadataGroup}>
|
||||
{metadataItems
|
||||
.filter((i) => i.enabled)
|
||||
.map((item, index) => (
|
||||
@@ -97,17 +133,16 @@ export const AlbumArtistDetailHeader = forwardRef((_props, ref: Ref<HTMLDivEleme
|
||||
<Text isMuted={item.secondary}>{item.value}</Text>
|
||||
</Fragment>
|
||||
))}
|
||||
{showRating && (
|
||||
<>
|
||||
<Text isNoSelect>•</Text>
|
||||
<Rating
|
||||
onChange={handleUpdateRating}
|
||||
readOnly={detailQuery?.isFetching}
|
||||
value={detailQuery?.data?.userRating || 0}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Group>
|
||||
<LibraryHeaderMenu
|
||||
favorite={detailQuery?.data?.userFavorite}
|
||||
onFavorite={handleFavorite}
|
||||
onMore={handleMoreOptions}
|
||||
onPlay={(type) => handlePlay(type)}
|
||||
onRating={showRating ? handleUpdateRating : undefined}
|
||||
onShuffle={() => handlePlay(Play.SHUFFLE)}
|
||||
rating={detailQuery?.data?.userRating || 0}
|
||||
/>
|
||||
</Stack>
|
||||
</LibraryHeader>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user