mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-02 19:01: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,68 +1,27 @@
|
|||
import { Center, Flex, Group, Stack } from '@mantine/core';
|
||||
import { useSetState } from '@mantine/hooks';
|
||||
import { AnimatePresence, HTMLMotionProps, motion, Variants } from 'framer-motion';
|
||||
import clsx from 'clsx';
|
||||
import { AnimatePresence, HTMLMotionProps, motion, Variants } from 'motion/react';
|
||||
import { Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { RiAlbumFill } from 'react-icons/ri';
|
||||
import { generatePath } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Badge, Text, TextTitle } from '/@/renderer/components';
|
||||
import styles from './full-screen-player-image.module.css';
|
||||
|
||||
import { useFastAverageColor } from '/@/renderer/hooks';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useFullScreenPlayerStore, usePlayerData, usePlayerStore } from '/@/renderer/store';
|
||||
import { usePlayerData, usePlayerStore } from '/@/renderer/store';
|
||||
import { useSettingsStore } from '/@/renderer/store/settings.store';
|
||||
import { Badge } from '/@/shared/components/badge/badge';
|
||||
import { Center } from '/@/shared/components/center/center';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Image } from '/@/shared/components/image/image';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextTitle } from '/@/shared/components/text-title/text-title';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { PlayerData, QueueSong } from '/@/shared/types/domain-types';
|
||||
|
||||
const Image = styled(motion.img)<any>`
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
object-fit: ${({ $useAspectRatio }) => ($useAspectRatio ? 'contain' : 'cover')};
|
||||
object-position: 50% 100%;
|
||||
filter: drop-shadow(0 0 5px rgb(0 0 0 / 40%)) drop-shadow(0 0 5px rgb(0 0 0 / 40%));
|
||||
border-radius: 5px;
|
||||
`;
|
||||
|
||||
const ImageContainer = styled(motion.div)`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
height: 65%;
|
||||
aspect-ratio: 1/1;
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
interface TransparentMetadataContainer {
|
||||
opacity?: number;
|
||||
}
|
||||
|
||||
const MetadataContainer = styled(Stack)<TransparentMetadataContainer>`
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
|
||||
h1 {
|
||||
font-size: 3.5vh;
|
||||
}
|
||||
`;
|
||||
|
||||
const PlayerContainer = styled(Flex)`
|
||||
@media screen and (height <= 640px) {
|
||||
.full-screen-player-image-metadata {
|
||||
display: none;
|
||||
height: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
${ImageContainer} {
|
||||
height: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const imageVariants: Variants = {
|
||||
closed: {
|
||||
opacity: 0,
|
||||
|
|
@ -93,22 +52,22 @@ const scaleImageUrl = (imageSize: number, url?: null | string) => {
|
|||
.replace(/&height=\d+/, `&height=${imageSize}`);
|
||||
};
|
||||
|
||||
const ImageWithPlaceholder = ({
|
||||
useAspectRatio,
|
||||
...props
|
||||
}: HTMLMotionProps<'img'> & { placeholder?: string; useAspectRatio: boolean }) => {
|
||||
const MotionImage = motion.create(Image);
|
||||
|
||||
const ImageWithPlaceholder = ({ ...props }: HTMLMotionProps<'img'> & { placeholder?: string }) => {
|
||||
if (!props.src) {
|
||||
return (
|
||||
<Center
|
||||
sx={{
|
||||
background: 'var(--placeholder-bg)',
|
||||
borderRadius: 'var(--card-default-radius)',
|
||||
style={{
|
||||
background: 'var(--theme-colors-surface)',
|
||||
borderRadius: 'var(--theme-card-default-radius)',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<RiAlbumFill
|
||||
color="var(--placeholder-fg)"
|
||||
<Icon
|
||||
color="muted"
|
||||
icon="itemAlbum"
|
||||
size="25%"
|
||||
/>
|
||||
</Center>
|
||||
|
|
@ -116,8 +75,8 @@ const ImageWithPlaceholder = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<Image
|
||||
$useAspectRatio={useAspectRatio}
|
||||
<MotionImage
|
||||
className={styles.image}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
@ -130,7 +89,6 @@ export const FullScreenPlayerImage = () => {
|
|||
const albumArtRes = useSettingsStore((store) => store.general.albumArtRes);
|
||||
|
||||
const { queue } = usePlayerData();
|
||||
const { useImageAspectRatio } = useFullScreenPlayerStore();
|
||||
const currentSong = queue.current;
|
||||
const { background } = useFastAverageColor({
|
||||
algorithm: 'dominant',
|
||||
|
|
@ -195,14 +153,17 @@ export const FullScreenPlayerImage = () => {
|
|||
}, [imageState, mainImageDimensions.idealSize, queue, setImageState]);
|
||||
|
||||
return (
|
||||
<PlayerContainer
|
||||
<Flex
|
||||
align="center"
|
||||
className="full-screen-player-image-container"
|
||||
className={clsx(styles.playerContainer, 'full-screen-player-image-container')}
|
||||
direction="column"
|
||||
justify="flex-start"
|
||||
p="1rem"
|
||||
>
|
||||
<ImageContainer ref={mainImageRef}>
|
||||
<div
|
||||
className={styles.imageContainer}
|
||||
ref={mainImageRef}
|
||||
>
|
||||
<AnimatePresence
|
||||
initial={false}
|
||||
mode="sync"
|
||||
|
|
@ -216,9 +177,8 @@ export const FullScreenPlayerImage = () => {
|
|||
exit="closed"
|
||||
initial="closed"
|
||||
key={imageKey}
|
||||
placeholder="var(--placeholder-bg)"
|
||||
placeholder="var(--theme-colors-foreground-muted)"
|
||||
src={imageState.topImage || ''}
|
||||
useAspectRatio={useImageAspectRatio}
|
||||
variants={imageVariants}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -232,62 +192,55 @@ export const FullScreenPlayerImage = () => {
|
|||
exit="closed"
|
||||
initial="closed"
|
||||
key={imageKey}
|
||||
placeholder="var(--placeholder-bg)"
|
||||
placeholder="var(--theme-colors-foreground-muted)"
|
||||
src={imageState.bottomImage || ''}
|
||||
useAspectRatio={useImageAspectRatio}
|
||||
variants={imageVariants}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</ImageContainer>
|
||||
<MetadataContainer
|
||||
className="full-screen-player-image-metadata"
|
||||
</div>
|
||||
<Stack
|
||||
className={styles.metadataContainer}
|
||||
gap="xs"
|
||||
maw="100%"
|
||||
spacing="xs"
|
||||
>
|
||||
<TextTitle
|
||||
align="center"
|
||||
fw={900}
|
||||
order={1}
|
||||
overflow="hidden"
|
||||
style={{
|
||||
textShadow: 'var(--fullscreen-player-text-shadow)',
|
||||
}}
|
||||
w="100%"
|
||||
weight={900}
|
||||
>
|
||||
{currentSong?.name}
|
||||
</TextTitle>
|
||||
<TextTitle
|
||||
$link
|
||||
align="center"
|
||||
component={Link}
|
||||
fw={600}
|
||||
isLink
|
||||
order={3}
|
||||
overflow="hidden"
|
||||
style={{
|
||||
textShadow: 'var(--fullscreen-player-text-shadow)',
|
||||
textShadow: 'var(--theme-fullscreen-player-text-shadow)',
|
||||
}}
|
||||
to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, {
|
||||
albumId: currentSong?.albumId || '',
|
||||
})}
|
||||
w="100%"
|
||||
weight={600}
|
||||
>
|
||||
{currentSong?.album}{' '}
|
||||
</TextTitle>
|
||||
<TextTitle
|
||||
align="center"
|
||||
key="fs-artists"
|
||||
order={3}
|
||||
style={{
|
||||
textShadow: 'var(--fullscreen-player-text-shadow)',
|
||||
textShadow: 'var(--theme-fullscreen-player-text-shadow)',
|
||||
}}
|
||||
>
|
||||
{currentSong?.artists?.map((artist, index) => (
|
||||
<Fragment key={`fs-artist-${artist.id}`}>
|
||||
{index > 0 && (
|
||||
<Text
|
||||
$secondary
|
||||
sx={{
|
||||
isMuted
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '0 0.5rem',
|
||||
}}
|
||||
|
|
@ -296,16 +249,16 @@ export const FullScreenPlayerImage = () => {
|
|||
</Text>
|
||||
)}
|
||||
<Text
|
||||
$link
|
||||
$secondary
|
||||
component={Link}
|
||||
fw={600}
|
||||
isLink
|
||||
isMuted
|
||||
style={{
|
||||
textShadow: 'var(--fullscreen-player-text-shadow)',
|
||||
textShadow: 'var(--theme-fullscreen-player-text-shadow)',
|
||||
}}
|
||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
|
||||
albumArtistId: artist.id,
|
||||
})}
|
||||
weight={600}
|
||||
>
|
||||
{artist.name}
|
||||
</Text>
|
||||
|
|
@ -313,8 +266,8 @@ export const FullScreenPlayerImage = () => {
|
|||
))}
|
||||
</TextTitle>
|
||||
<Group
|
||||
justify="center"
|
||||
mt="sm"
|
||||
position="center"
|
||||
>
|
||||
{currentSong?.container && (
|
||||
<Badge size="lg">
|
||||
|
|
@ -325,7 +278,7 @@ export const FullScreenPlayerImage = () => {
|
|||
<Badge size="lg">{currentSong?.releaseYear}</Badge>
|
||||
)}
|
||||
</Group>
|
||||
</MetadataContainer>
|
||||
</PlayerContainer>
|
||||
</Stack>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue