mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-02 10:53:33 +00:00
improve library header loading
This commit is contained in:
parent
b8ceb174b3
commit
b7fb7c7f94
9 changed files with 59 additions and 61 deletions
|
|
@ -18,8 +18,9 @@ import { AlbumDetailResponse, LibraryItem, ServerType } from '/@/shared/types/do
|
||||||
|
|
||||||
interface AlbumDetailHeaderProps {
|
interface AlbumDetailHeaderProps {
|
||||||
background: {
|
background: {
|
||||||
background: string;
|
background?: string;
|
||||||
blur: number;
|
blur: number;
|
||||||
|
loading: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/li
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
import { NativeScrollArea, Spinner } from '/@/renderer/components';
|
import { NativeScrollArea } from '/@/renderer/components';
|
||||||
import { AlbumDetailContent } from '/@/renderer/features/albums/components/album-detail-content';
|
import { AlbumDetailContent } from '/@/renderer/features/albums/components/album-detail-content';
|
||||||
import { AlbumDetailHeader } from '/@/renderer/features/albums/components/album-detail-header';
|
import { AlbumDetailHeader } from '/@/renderer/features/albums/components/album-detail-header';
|
||||||
import { useAlbumDetail } from '/@/renderer/features/albums/queries/album-detail-query';
|
import { useAlbumDetail } from '/@/renderer/features/albums/queries/album-detail-query';
|
||||||
|
|
@ -23,7 +23,7 @@ const AlbumDetailRoute = () => {
|
||||||
const { albumId } = useParams() as { albumId: string };
|
const { albumId } = useParams() as { albumId: string };
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
const detailQuery = useAlbumDetail({ query: { id: albumId }, serverId: server?.id });
|
const detailQuery = useAlbumDetail({ query: { id: albumId }, serverId: server?.id });
|
||||||
const { color: backgroundColor, colorId } = useFastAverageColor({
|
const { background: backgroundColor, colorId } = useFastAverageColor({
|
||||||
id: albumId,
|
id: albumId,
|
||||||
src: detailQuery.data?.imageUrl,
|
src: detailQuery.data?.imageUrl,
|
||||||
srcLoaded: !detailQuery.isLoading,
|
srcLoaded: !detailQuery.isLoading,
|
||||||
|
|
@ -41,10 +41,6 @@ const AlbumDetailRoute = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!backgroundColor || colorId !== albumId) {
|
|
||||||
return <Spinner container />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const backgroundUrl = detailQuery.data?.imageUrl || '';
|
const backgroundUrl = detailQuery.data?.imageUrl || '';
|
||||||
const background = (albumBackground && `url(${backgroundUrl})`) || backgroundColor;
|
const background = (albumBackground && `url(${backgroundUrl})`) || backgroundColor;
|
||||||
|
|
||||||
|
|
@ -70,6 +66,7 @@ const AlbumDetailRoute = () => {
|
||||||
background={{
|
background={{
|
||||||
background,
|
background,
|
||||||
blur: (albumBackground && albumBackgroundBlur) || 0,
|
blur: (albumBackground && albumBackgroundBlur) || 0,
|
||||||
|
loading: !backgroundColor || colorId !== albumId,
|
||||||
}}
|
}}
|
||||||
ref={headerRef}
|
ref={headerRef}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { styled } from 'styled-components';
|
||||||
|
|
||||||
import { api } from '/@/renderer/api';
|
import { api } from '/@/renderer/api';
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { Button, Spinner, Spoiler, Text } from '/@/renderer/components';
|
import { Button, Spoiler, Text } from '/@/renderer/components';
|
||||||
import { useHandleGeneralContextMenu } from '/@/renderer/features/context-menu';
|
import { useHandleGeneralContextMenu } from '/@/renderer/features/context-menu';
|
||||||
import { SONG_ALBUM_PAGE } from '/@/renderer/features/context-menu/context-menu-items';
|
import { SONG_ALBUM_PAGE } from '/@/renderer/features/context-menu/context-menu-items';
|
||||||
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
import { usePlayQueueAdd } from '/@/renderer/features/player';
|
||||||
|
|
@ -55,7 +55,7 @@ const DummyAlbumDetailRoute = () => {
|
||||||
queryKey,
|
queryKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { color: background, colorId } = useFastAverageColor({
|
const { background, colorId } = useFastAverageColor({
|
||||||
id: albumId,
|
id: albumId,
|
||||||
src: detailQuery.data?.imageUrl,
|
src: detailQuery.data?.imageUrl,
|
||||||
srcLoaded: !detailQuery.isLoading,
|
srcLoaded: !detailQuery.isLoading,
|
||||||
|
|
@ -114,10 +114,6 @@ const DummyAlbumDetailRoute = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!background || colorId !== albumId) {
|
|
||||||
return <Spinner container />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadataItems = [
|
const metadataItems = [
|
||||||
{
|
{
|
||||||
id: 'releaseYear',
|
id: 'releaseYear',
|
||||||
|
|
@ -138,6 +134,7 @@ const DummyAlbumDetailRoute = () => {
|
||||||
background={background}
|
background={background}
|
||||||
imageUrl={detailQuery?.data?.imageUrl}
|
imageUrl={detailQuery?.data?.imageUrl}
|
||||||
item={{ route: AppRoute.LIBRARY_SONGS, type: LibraryItem.SONG }}
|
item={{ route: AppRoute.LIBRARY_SONGS, type: LibraryItem.SONG }}
|
||||||
|
loading={!background || colorId !== albumId}
|
||||||
title={detailQuery?.data?.name || ''}
|
title={detailQuery?.data?.name || ''}
|
||||||
>
|
>
|
||||||
<Stack spacing="sm">
|
<Stack spacing="sm">
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,12 @@ import { formatDurationString } from '/@/renderer/utils';
|
||||||
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
|
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
interface AlbumArtistDetailHeaderProps {
|
interface AlbumArtistDetailHeaderProps {
|
||||||
background: string;
|
background?: string;
|
||||||
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlbumArtistDetailHeader = forwardRef(
|
export const AlbumArtistDetailHeader = forwardRef(
|
||||||
({ background }: AlbumArtistDetailHeaderProps, ref: Ref<HTMLDivElement>) => {
|
({ background, loading }: AlbumArtistDetailHeaderProps, ref: Ref<HTMLDivElement>) => {
|
||||||
const { albumArtistId, artistId } = useParams() as {
|
const { albumArtistId, artistId } = useParams() as {
|
||||||
albumArtistId?: string;
|
albumArtistId?: string;
|
||||||
artistId?: string;
|
artistId?: string;
|
||||||
|
|
@ -76,6 +77,7 @@ export const AlbumArtistDetailHeader = forwardRef(
|
||||||
background={background}
|
background={background}
|
||||||
imageUrl={detailQuery?.data?.imageUrl}
|
imageUrl={detailQuery?.data?.imageUrl}
|
||||||
item={{ route: AppRoute.LIBRARY_ALBUM_ARTISTS, type: LibraryItem.ALBUM_ARTIST }}
|
item={{ route: AppRoute.LIBRARY_ALBUM_ARTISTS, type: LibraryItem.ALBUM_ARTIST }}
|
||||||
|
loading={loading}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
title={detailQuery?.data?.name || ''}
|
title={detailQuery?.data?.name || ''}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
import { NativeScrollArea, Spinner } from '/@/renderer/components';
|
import { NativeScrollArea } from '/@/renderer/components';
|
||||||
import { AlbumArtistDetailContent } from '/@/renderer/features/artists/components/album-artist-detail-content';
|
import { AlbumArtistDetailContent } from '/@/renderer/features/artists/components/album-artist-detail-content';
|
||||||
import { AlbumArtistDetailHeader } from '/@/renderer/features/artists/components/album-artist-detail-header';
|
import { AlbumArtistDetailHeader } from '/@/renderer/features/artists/components/album-artist-detail-header';
|
||||||
import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query';
|
import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album-artist-detail-query';
|
||||||
|
|
@ -30,7 +30,7 @@ const AlbumArtistDetailRoute = () => {
|
||||||
query: { id: routeId },
|
query: { id: routeId },
|
||||||
serverId: server?.id,
|
serverId: server?.id,
|
||||||
});
|
});
|
||||||
const { color: background, colorId } = useFastAverageColor({
|
const { background, colorId } = useFastAverageColor({
|
||||||
id: routeId,
|
id: routeId,
|
||||||
src: detailQuery.data?.imageUrl,
|
src: detailQuery.data?.imageUrl,
|
||||||
srcLoaded: !detailQuery.isLoading,
|
srcLoaded: !detailQuery.isLoading,
|
||||||
|
|
@ -46,10 +46,6 @@ const AlbumArtistDetailRoute = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!background || colorId !== routeId) {
|
|
||||||
return <Spinner container />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedPage key={`album-artist-detail-${routeId}`}>
|
<AnimatedPage key={`album-artist-detail-${routeId}`}>
|
||||||
<NativeScrollArea
|
<NativeScrollArea
|
||||||
|
|
@ -70,6 +66,7 @@ const AlbumArtistDetailRoute = () => {
|
||||||
>
|
>
|
||||||
<AlbumArtistDetailHeader
|
<AlbumArtistDetailHeader
|
||||||
background={background}
|
background={background}
|
||||||
|
loading={!background || colorId !== routeId}
|
||||||
ref={headerRef}
|
ref={headerRef}
|
||||||
/>
|
/>
|
||||||
<AlbumArtistDetailContent background={background} />
|
<AlbumArtistDetailContent background={background} />
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ export const FullScreenPlayerImage = () => {
|
||||||
const { queue } = usePlayerData();
|
const { queue } = usePlayerData();
|
||||||
const { useImageAspectRatio } = useFullScreenPlayerStore();
|
const { useImageAspectRatio } = useFullScreenPlayerStore();
|
||||||
const currentSong = queue.current;
|
const currentSong = queue.current;
|
||||||
const { color: background } = useFastAverageColor({
|
const { background } = useFastAverageColor({
|
||||||
algorithm: 'dominant',
|
algorithm: 'dominant',
|
||||||
src: queue.current?.imageUrl,
|
src: queue.current?.imageUrl,
|
||||||
srcLoaded: true,
|
srcLoaded: true,
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ export const FullScreenPlayer = () => {
|
||||||
}, [location, setStore]);
|
}, [location, setStore]);
|
||||||
|
|
||||||
const currentSong = useCurrentSong();
|
const currentSong = useCurrentSong();
|
||||||
const { color: background } = useFastAverageColor({
|
const { background } = useFastAverageColor({
|
||||||
algorithm: 'dominant',
|
algorithm: 'dominant',
|
||||||
src: currentSong?.imageUrl,
|
src: currentSong?.imageUrl,
|
||||||
srcLoaded: true,
|
srcLoaded: true,
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,19 @@ import { useGeneralSettings } from '/@/renderer/store';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
interface LibraryHeaderProps {
|
interface LibraryHeaderProps {
|
||||||
background: string;
|
background?: string;
|
||||||
blur?: number;
|
blur?: number;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
imagePlaceholderUrl?: null | string;
|
imagePlaceholderUrl?: null | string;
|
||||||
imageUrl?: null | string;
|
imageUrl?: null | string;
|
||||||
item: { route: string; type: LibraryItem };
|
item: { route: string; type: LibraryItem };
|
||||||
|
loading?: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LibraryHeader = forwardRef(
|
export const LibraryHeader = forwardRef(
|
||||||
(
|
(
|
||||||
{ background, blur, children, imageUrl, item, title }: LibraryHeaderProps,
|
{ background, blur, children, imageUrl, item, loading, title }: LibraryHeaderProps,
|
||||||
ref: Ref<HTMLDivElement>,
|
ref: Ref<HTMLDivElement>,
|
||||||
) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -106,38 +107,41 @@ export const LibraryHeader = forwardRef(
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
{imageUrl && !isImageError ? (
|
{!loading &&
|
||||||
<img
|
(imageUrl && !isImageError ? (
|
||||||
alt="cover"
|
<img
|
||||||
className={styles.image}
|
alt="cover"
|
||||||
onError={onImageError}
|
className={styles.image}
|
||||||
// placeholder={imagePlaceholderUrl || 'var(--placeholder-bg)'}
|
onError={onImageError}
|
||||||
src={imageUrl}
|
// placeholder={imagePlaceholderUrl || 'var(--placeholder-bg)'}
|
||||||
style={{ height: '' }}
|
src={imageUrl}
|
||||||
/>
|
style={{ height: '' }}
|
||||||
) : (
|
/>
|
||||||
<ItemImagePlaceholder itemType={item.type} />
|
) : (
|
||||||
)}
|
<ItemImagePlaceholder itemType={item.type} />
|
||||||
</div>
|
))}
|
||||||
<div className={styles.metadataSection}>
|
|
||||||
<Group>
|
|
||||||
<h2>
|
|
||||||
<Text
|
|
||||||
$link
|
|
||||||
component={Link}
|
|
||||||
to={item.route}
|
|
||||||
tt="uppercase"
|
|
||||||
weight={600}
|
|
||||||
>
|
|
||||||
{itemTypeString()}
|
|
||||||
</Text>
|
|
||||||
</h2>
|
|
||||||
</Group>
|
|
||||||
<h1 className={styles.title}>
|
|
||||||
<AutoTextSize mode="box">{title}</AutoTextSize>
|
|
||||||
</h1>
|
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
|
{title && (
|
||||||
|
<div className={styles.metadataSection}>
|
||||||
|
<Group>
|
||||||
|
<h2>
|
||||||
|
<Text
|
||||||
|
$link
|
||||||
|
component={Link}
|
||||||
|
to={item.route}
|
||||||
|
tt="uppercase"
|
||||||
|
weight={600}
|
||||||
|
>
|
||||||
|
{itemTypeString()}
|
||||||
|
</Text>
|
||||||
|
</h2>
|
||||||
|
</Group>
|
||||||
|
<h1 className={styles.title}>
|
||||||
|
<AutoTextSize mode="box">{title}</AutoTextSize>
|
||||||
|
</h1>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export const useFastAverageColor = (args: {
|
||||||
const { algorithm, id, src, srcLoaded } = args;
|
const { algorithm, id, src, srcLoaded } = args;
|
||||||
const idRef = useRef<string | undefined>(id);
|
const idRef = useRef<string | undefined>(id);
|
||||||
|
|
||||||
const [color, setColor] = useState<string | undefined>(undefined);
|
const [background, setBackground] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fac = new FastAverageColor();
|
const fac = new FastAverageColor();
|
||||||
|
|
@ -27,16 +27,16 @@ export const useFastAverageColor = (args: {
|
||||||
})
|
})
|
||||||
.then((color) => {
|
.then((color) => {
|
||||||
idRef.current = id;
|
idRef.current = id;
|
||||||
return setColor(color.rgb);
|
return setBackground(color.rgb);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log('Error fetching average color', e);
|
console.log('Error fetching average color', e);
|
||||||
idRef.current = id;
|
idRef.current = id;
|
||||||
return setColor('rgba(0, 0, 0, 0)');
|
return setBackground('rgba(0, 0, 0, 0)');
|
||||||
});
|
});
|
||||||
} else if (srcLoaded) {
|
} else if (srcLoaded) {
|
||||||
idRef.current = id;
|
idRef.current = id;
|
||||||
return setColor('var(--placeholder-bg)');
|
return setBackground('var(--placeholder-bg)');
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -44,5 +44,5 @@ export const useFastAverageColor = (args: {
|
||||||
};
|
};
|
||||||
}, [algorithm, srcLoaded, src, id]);
|
}, [algorithm, srcLoaded, src, id]);
|
||||||
|
|
||||||
return { color, colorId: idRef.current };
|
return { background, colorId: idRef.current };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue