[FEAT] better covers, Icons and work on better display for the phones
This commit is contained in:
parent
031f5650e4
commit
6be185d435
@ -27,7 +27,6 @@
|
||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"framer-motion": "11.5.4",
|
||||
"@chakra-ui/anatomy": "2.2.2",
|
||||
"@chakra-ui/cli": "2.4.1",
|
||||
"@chakra-ui/react": "2.8.2",
|
||||
|
@ -1,28 +1,47 @@
|
||||
import { ReactElement, useEffect, useState } from 'react';
|
||||
|
||||
import { Box, BoxProps, Flex } from '@chakra-ui/react';
|
||||
import { As, Box, BoxProps, Flex, StyleProps } from '@chakra-ui/react';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
|
||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
export type CoversProps = BoxProps & {
|
||||
data?: string[];
|
||||
size?: string;
|
||||
iconEmpty?: ReactElement;
|
||||
size?: StyleProps["width"];
|
||||
iconEmpty?: As;
|
||||
slideshow?: boolean;
|
||||
};
|
||||
|
||||
export const Covers = ({
|
||||
data,
|
||||
iconEmpty,
|
||||
size = '100px',
|
||||
slideshow = false,
|
||||
...rest
|
||||
}: CoversProps) => {
|
||||
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
||||
const [previousImageIndex, setPreviousImageIndex] = useState(0);
|
||||
const [topOpacity, setTopOpacity] = useState(0.0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slideshow) {
|
||||
return;
|
||||
}
|
||||
const interval = setInterval(() => {
|
||||
setPreviousImageIndex(currentImageIndex);
|
||||
setTopOpacity(0.0);
|
||||
setTimeout(() => {
|
||||
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % (data?.length ?? 1));
|
||||
setTopOpacity(1.0);
|
||||
}, 1500);
|
||||
}, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}, [slideshow, data]);
|
||||
|
||||
if (!data || data.length < 1) {
|
||||
if (iconEmpty) {
|
||||
return iconEmpty;
|
||||
return <Icon icon={iconEmpty} sizeIcon={size} />;
|
||||
} else {
|
||||
return (
|
||||
<Box
|
||||
@ -38,24 +57,13 @@ export const Covers = ({
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.length == 1) {
|
||||
if (slideshow === false || data.length === 1) {
|
||||
const url = DataUrlAccess.getThumbnailUrl(data[0]);
|
||||
return <Image loading="lazy" src={url} boxSize={size} {...rest} />;
|
||||
return <Image loading="lazy" src={url} maxWidth={size} boxSize={size} {...rest} />;
|
||||
}
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setPreviousImageIndex(currentImageIndex);
|
||||
setTopOpacity(0.0);
|
||||
setTimeout(() => {
|
||||
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % data.length);
|
||||
setTopOpacity(1.0);
|
||||
}, 1500);
|
||||
}, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
const urlCurrent = DataUrlAccess.getThumbnailUrl(data[currentImageIndex]);
|
||||
const urlPrevious = DataUrlAccess.getThumbnailUrl(data[previousImageIndex]);
|
||||
return <Flex position="relative" {...rest} maxWidth={size} width={size} height={size} overflow="hidden" border="1px">
|
||||
return <Flex position="relative" {...rest} maxWidth={size} width={size} height={size} overflow="hidden">
|
||||
<Image
|
||||
src={urlPrevious}
|
||||
loading="lazy"
|
||||
@ -79,7 +87,6 @@ export const Covers = ({
|
||||
transition="opacity 0.5s ease-in-out"
|
||||
opacity={topOpacity}
|
||||
zIndex={2}
|
||||
border="1px"
|
||||
/>
|
||||
</Flex>
|
||||
};
|
||||
|
42
front2/src/components/Icon.tsx
Normal file
42
front2/src/components/Icon.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import {
|
||||
As,
|
||||
Box,
|
||||
BoxProps,
|
||||
Icon as ChakraIcon,
|
||||
IconProps as ChakraIconProps,
|
||||
Flex,
|
||||
FlexProps,
|
||||
forwardRef,
|
||||
LayoutProps,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
export type IconProps = FlexProps & {
|
||||
icon: As;
|
||||
color?: string;
|
||||
sizeIcon?: LayoutProps['width'];
|
||||
};
|
||||
|
||||
export const Icon = forwardRef<IconProps, 'span'>(
|
||||
({ icon: IconEl, color, sizeIcon = '1em', ...rest }, ref) => {
|
||||
return (
|
||||
<Flex flex="none"
|
||||
minWidth={sizeIcon}
|
||||
minHeight={sizeIcon}
|
||||
maxWidth={sizeIcon}
|
||||
maxHeight={sizeIcon}
|
||||
align="center"
|
||||
padding="1%"
|
||||
ref={ref}
|
||||
{...rest}>
|
||||
<Box
|
||||
marginX="auto"
|
||||
as={IconEl}
|
||||
width="100%"
|
||||
minWidth="100%"
|
||||
height="100%"
|
||||
color={color}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
);
|
@ -27,6 +27,7 @@ export const PageLayout = ({ children }: LayoutProps) => {
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
minWidth="300px"
|
||||
zIndex={-1}
|
||||
>
|
||||
<Image src={background} boxSize="90%" margin="auto" opacity="30%" />
|
||||
@ -42,6 +43,7 @@ export const PageLayout = ({ children }: LayoutProps) => {
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
minWidth="300px"
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
|
@ -4,6 +4,7 @@ import { LuDisc3 } from 'react-icons/lu';
|
||||
import { Album } from '@/back-api';
|
||||
import { Covers } from '@/components/Cover';
|
||||
import { useCountTracksWithAlbumId } from '@/service/Track';
|
||||
import { BASE_WRAP_ICON_SIZE } from '@/constants/genericSpacing';
|
||||
|
||||
export type DisplayAlbumProps = {
|
||||
dataAlbum?: Album;
|
||||
@ -21,18 +22,18 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
||||
<Flex direction="row" width="full" height="full">
|
||||
<Covers
|
||||
data={dataAlbum?.covers}
|
||||
size="100"
|
||||
height="full"
|
||||
size={BASE_WRAP_ICON_SIZE}
|
||||
flex={1}
|
||||
iconEmpty={<LuDisc3 size="100" height="full" />}
|
||||
iconEmpty={LuDisc3}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
width="150px"
|
||||
maxWidth="150px"
|
||||
//maxWidth="150px"
|
||||
height="full"
|
||||
paddingLeft="5px"
|
||||
overflowX="hidden"
|
||||
flex={1}
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
|
@ -23,7 +23,7 @@ export const DisplayGender = ({ dataGender }: DisplayGenderProps) => {
|
||||
data={dataGender?.covers}
|
||||
size="100"
|
||||
height="full"
|
||||
iconEmpty={<LuDisc3 size="100" height="full" />}
|
||||
iconEmpty={LuDisc3}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
|
@ -24,11 +24,7 @@ export const DisplayTrack = ({
|
||||
size="50"
|
||||
height="full"
|
||||
iconEmpty={
|
||||
trackActive?.id === track.id ? (
|
||||
<LuPlay size="50" height="full" />
|
||||
) : (
|
||||
<LuMusic2 size="50" height="full" />
|
||||
)
|
||||
trackActive?.id === track.id ? LuPlay : LuMusic2
|
||||
}
|
||||
onClick={onClick}
|
||||
/>
|
||||
|
@ -6,7 +6,6 @@ import { LuMusic2, LuPlay } from 'react-icons/lu';
|
||||
import { Track } from '@/back-api';
|
||||
import { Covers } from '@/components/Cover';
|
||||
import { ContextMenu, MenuElement } from '@/components/contextMenu/ContextMenu';
|
||||
import { DisplayTrackSkeleton } from '@/components/track/DisplayTrackSkeleton';
|
||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||
import { useSpecificAlbum } from '@/service/Album';
|
||||
import { useSpecificArtists } from '@/service/Artist';
|
||||
@ -31,14 +30,9 @@ export const DisplayTrackFull = ({
|
||||
<Covers
|
||||
data={track?.covers}
|
||||
size="50"
|
||||
//height="full"
|
||||
marginY="auto"
|
||||
iconEmpty={
|
||||
trackActive?.id === track.id ? (
|
||||
<LuPlay size="50" />
|
||||
) : (
|
||||
<LuMusic2 size="50" />
|
||||
)
|
||||
trackActive?.id === track.id ? LuPlay : LuMusic2
|
||||
}
|
||||
onClick={onClick}
|
||||
/>
|
||||
@ -76,7 +70,7 @@ export const DisplayTrackFull = ({
|
||||
marginY="auto"
|
||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||
>
|
||||
Album {dataAlbum.name}
|
||||
<Text as="span" fontWeight="normal">Album:</Text> {dataAlbum.name}
|
||||
</Text>
|
||||
)}
|
||||
{dataArtists && (
|
||||
@ -92,7 +86,7 @@ export const DisplayTrackFull = ({
|
||||
marginY="auto"
|
||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||
>
|
||||
Artist(s): {dataArtists.map((data) => data.name).join(', ')}
|
||||
<Text as="span" fontWeight="normal">Artist(s):</Text> {dataArtists.map((data) => data.name).join(', ')}
|
||||
</Text>
|
||||
)}
|
||||
{dataGender && (
|
||||
@ -108,7 +102,7 @@ export const DisplayTrackFull = ({
|
||||
marginY="auto"
|
||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||
>
|
||||
Gender: {dataGender.name}
|
||||
<Text as="span" fontWeight="normal">Gender:</Text> {dataGender.name}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
|
4
front2/src/constants/genericSpacing.ts
Normal file
4
front2/src/constants/genericSpacing.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const BASE_WRAP_SPACING = { base: "5px", md: "10px", lg: "20px" };
|
||||
export const BASE_WRAP_WIDTH = { base: "90%", md: "45%", lg: "270px" };
|
||||
export const BASE_WRAP_HEIGHT = { base: "75px", lg: "120px" };
|
||||
export const BASE_WRAP_ICON_SIZE = { base: "50px", lg: "100px" };
|
@ -16,6 +16,7 @@ import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||
import { useSpecificAlbum } from '@/service/Album';
|
||||
import { useTracksOfAnAlbum } from '@/service/Track';
|
||||
import { useThemeMode } from '@/utils/theme-tools';
|
||||
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
|
||||
|
||||
export const AlbumDetailPage = () => {
|
||||
const { albumId } = useParams();
|
||||
@ -75,7 +76,8 @@ export const AlbumDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataAlbum?.covers}
|
||||
iconEmpty={<LuDisc3 size="100" height="full" />}
|
||||
iconEmpty={LuDisc3}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
@ -92,7 +94,7 @@ export const AlbumDetailPage = () => {
|
||||
|
||||
<Flex
|
||||
direction="column"
|
||||
gap="20px"
|
||||
gap={BASE_WRAP_SPACING}
|
||||
marginX="auto"
|
||||
padding="20px"
|
||||
width="80%"
|
||||
@ -122,7 +124,7 @@ export const AlbumDetailPage = () => {
|
||||
navigate(`/album/${albumId}/edit-track/${data.id}`);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => {} },
|
||||
{ name: 'Add Playlist', onClick: () => { } },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -11,6 +11,7 @@ import { TopBar } from '@/components/TopBar/TopBar';
|
||||
import { DisplayAlbum } from '@/components/album/DisplayAlbum';
|
||||
import { useOrderedAlbums } from '@/service/Album';
|
||||
import { useThemeMode } from '@/utils/theme-tools';
|
||||
import { BASE_WRAP_SPACING, BASE_WRAP_WIDTH, BASE_WRAP_HEIGHT } from '@/constants/genericSpacing';
|
||||
|
||||
export const AlbumsPage = () => {
|
||||
const [filterTitle, setFilterTitle] = useState<string | undefined>(undefined);
|
||||
@ -36,11 +37,11 @@ export const AlbumsPage = () => {
|
||||
<SearchInput onChange={setFilterTitle} />
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<Wrap spacing="20px" marginX="auto" padding="20px" justify="center">
|
||||
<Wrap spacing={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||
{dataAlbums.map((data) => (
|
||||
<WrapItem
|
||||
width="270px"
|
||||
height="120px"
|
||||
width={BASE_WRAP_WIDTH}
|
||||
height={BASE_WRAP_HEIGHT}
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={mode('#FFFFFF88', '#00000088')}
|
||||
|
@ -68,7 +68,7 @@ export const ArtistAlbumDetailPage = () => {
|
||||
data={dataArtist?.covers}
|
||||
size="35px"
|
||||
borderRadius="full"
|
||||
iconEmpty={<MdPerson height="full" />}
|
||||
iconEmpty={MdPerson}
|
||||
/>
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
{dataArtist?.name}
|
||||
@ -95,7 +95,8 @@ export const ArtistAlbumDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataAlbum?.covers}
|
||||
iconEmpty={<LuDisc3 size="100" height="full" />}
|
||||
iconEmpty={LuDisc3}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
@ -144,7 +145,7 @@ export const ArtistAlbumDetailPage = () => {
|
||||
);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => {} },
|
||||
{ name: 'Add Playlist', onClick: () => { } },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -13,6 +13,7 @@ import { ArtistEditPopUp } from '@/components/popup/ArtistEditPopUp';
|
||||
import { useSpecificArtist } from '@/service/Artist';
|
||||
import { useAlbumIdsOfAnArtist } from '@/service/Track';
|
||||
import { useThemeMode } from '@/utils/theme-tools';
|
||||
import { BASE_WRAP_HEIGHT, BASE_WRAP_SPACING, BASE_WRAP_WIDTH } from '@/constants/genericSpacing';
|
||||
|
||||
export const ArtistDetailPage = () => {
|
||||
const { artistId } = useParams();
|
||||
@ -70,7 +71,8 @@ export const ArtistDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataArtist?.covers}
|
||||
iconEmpty={<LuUser size="100" height="full" />}
|
||||
iconEmpty={LuUser}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
@ -87,11 +89,11 @@ export const ArtistDetailPage = () => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Wrap spacing="20px" marginX="auto" padding="20px" justify="center">
|
||||
<Wrap spacing={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||
{albumIdsOfAnArtist?.map((data) => (
|
||||
<WrapItem
|
||||
width="270px"
|
||||
height="120px"
|
||||
width={BASE_WRAP_WIDTH}
|
||||
height={BASE_WRAP_HEIGHT}
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={mode('#FFFFFF88', '#00000088')}
|
||||
|
@ -12,6 +12,7 @@ import { SearchInput } from '@/components/SearchInput';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
import { useArtistService, useOrderedArtists } from '@/service/Artist';
|
||||
import { useThemeMode } from '@/utils/theme-tools';
|
||||
import { BASE_WRAP_HEIGHT, BASE_WRAP_ICON_SIZE, BASE_WRAP_SPACING, BASE_WRAP_WIDTH } from '@/constants/genericSpacing';
|
||||
|
||||
export const ArtistsPage = () => {
|
||||
const { mode } = useThemeMode();
|
||||
@ -27,11 +28,11 @@ export const ArtistsPage = () => {
|
||||
<SearchInput onChange={setFilterName} />
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<Wrap spacing="20px" marginX="auto" padding="20px" justify="center">
|
||||
<Wrap spacing={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||
{dataArtist?.map((data) => (
|
||||
<WrapItem
|
||||
width="270px"
|
||||
height="120px"
|
||||
width={BASE_WRAP_WIDTH}
|
||||
height={BASE_WRAP_HEIGHT}
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={mode('#FFFFFF88', '#00000088')}
|
||||
@ -47,9 +48,9 @@ export const ArtistsPage = () => {
|
||||
<Flex direction="row" width="full" height="full">
|
||||
<Covers
|
||||
data={data.covers}
|
||||
size="100"
|
||||
size={BASE_WRAP_ICON_SIZE}
|
||||
height="full"
|
||||
iconEmpty={<LuUser size="100" height="full" />}
|
||||
iconEmpty={LuUser}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
|
@ -76,7 +76,8 @@ export const GenderDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataGender?.covers}
|
||||
iconEmpty={<LuDisc3 size="100" height="full" />}
|
||||
iconEmpty={LuDisc3}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
@ -120,7 +121,7 @@ export const GenderDetailPage = () => {
|
||||
navigate(`/gender/${genderId}/edit-track/${data.id}`);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => {} },
|
||||
{ name: 'Add Playlist', onClick: () => { } },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
Loading…
x
Reference in New Issue
Block a user