[FEAT] add album direct diaplay

This commit is contained in:
Edouard DUPIN 2024-08-26 22:19:27 +02:00
parent 30bc82edb3
commit 90fb1ed411
12 changed files with 354 additions and 128 deletions

View File

@ -35,11 +35,11 @@
"@dnd-kit/modifiers": "7.0.0",
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@emotion/react": "11.13.0",
"@emotion/react": "11.13.3",
"@emotion/styled": "11.13.0",
"allotment": "1.20.2",
"css-mediaquery": "0.1.2",
"dayjs": "1.11.12",
"dayjs": "1.11.13",
"history": "5.3.0",
"react": "18.3.1",
"react-color-palette": "7.2.2",
@ -48,13 +48,13 @@
"react-day-picker": "9.0.8",
"react-dom": "18.3.1",
"react-error-boundary": "4.0.13",
"react-focus-lock": "2.12.1",
"react-focus-lock": "2.13.2",
"react-icons": "5.3.0",
"react-popper": "2.3.0",
"react-router-dom": "6.26.1",
"react-select": "5.8.0",
"react-simple-keyboard": "3.7.144",
"react-sticky-el": "2.1.0",
"react-simple-keyboard": "3.7.147",
"react-sticky-el": "2.1.1",
"react-use": "17.5.1",
"react-use-draggable-scroll": "0.4.7",
"react-virtuoso": "4.10.1",
@ -65,7 +65,7 @@
},
"devDependencies": {
"@chakra-ui/styled-system": "2.9.2",
"@playwright/test": "1.46.0",
"@playwright/test": "1.46.1",
"@storybook/addon-actions": "8.2.9",
"@storybook/addon-essentials": "8.2.9",
"@storybook/addon-links": "8.2.9",
@ -73,19 +73,19 @@
"@storybook/react": "8.2.9",
"@storybook/react-vite": "8.2.9",
"@storybook/theming": "8.2.9",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/jest-dom": "6.5.0",
"@testing-library/react": "16.0.0",
"@testing-library/user-event": "14.5.2",
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@types/jest": "29.5.12",
"@types/node": "22.3.0",
"@types/react": "18.3.3",
"@types/node": "22.5.0",
"@types/react": "18.3.4",
"@types/react-dom": "18.3.0",
"@types/react-sticky-el": "1.0.7",
"@typescript-eslint/eslint-plugin": "8.1.0",
"@typescript-eslint/parser": "8.1.0",
"@typescript-eslint/eslint-plugin": "8.2.0",
"@typescript-eslint/parser": "8.2.0",
"@vitejs/plugin-react": "4.3.1",
"eslint": "9.9.0",
"eslint": "9.9.1",
"eslint-plugin-codeceptjs": "1.3.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-react": "7.35.0",
@ -93,16 +93,16 @@
"eslint-plugin-storybook": "0.8.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"knip": "5.27.2",
"knip": "5.27.4",
"lint-staged": "15.2.9",
"prettier": "3.3.3",
"puppeteer": "23.1.0",
"puppeteer": "23.1.1",
"react-is": "18.3.1",
"storybook": "8.2.9",
"ts-node": "10.9.2",
"typescript": "5.5.4",
"vite": "5.4.1",
"vite": "5.4.2",
"vitest": "2.0.5",
"npm-check-updates": "^17.0.6"
"npm-check-updates": "^17.1.0"
}
}

View File

@ -0,0 +1,42 @@
import { Flex, Text } from '@chakra-ui/react';
import { LuMusic2 } from 'react-icons/lu';
import { Track } from '@/back-api';
import { Covers } from '@/components/Cover';
export type DisplayTrackProps = {
track: Track;
};
export const DisplayTrack = ({ track }: DisplayTrackProps) => {
return (
<Flex direction="row" width="full" height="full">
<Covers
data={track?.covers}
size="50"
height="full"
iconEmpty={<LuMusic2 size="50" height="full" />}
/>
<Flex
direction="column"
width="full"
height="full"
paddingLeft="5px"
overflowX="hidden"
>
<Text
as="span"
align="left"
fontSize="20px"
fontWeight="bold"
userSelect="none"
marginRight="auto"
overflow="hidden"
noOfLines={[1, 2]}
marginY="auto"
>
[{track.track}] {track.name}
</Text>
</Flex>
</Flex>
);
};

View File

@ -0,0 +1,13 @@
import { Box } from '@chakra-ui/react';
export const EmptyEnd = () => {
return (
<Box
width="full"
height="25%"
minHeight="25%"
borderWidth="1px"
borderColor="red"
></Box>
);
};

View File

@ -0,0 +1,62 @@
import { Flex, Text } from '@chakra-ui/react';
import { LuDisc3 } from 'react-icons/lu';
import { Album } from '@/back-api';
import { Covers } from '@/components/Cover';
import { useCountTracksWithAlbumId } from '@/service/Track';
export type DisplayAlbumProps = {
dataAlbum?: Album;
};
export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
const { countTracksOfAnAlbum } = useCountTracksWithAlbumId(dataAlbum?.id);
if (!dataAlbum) {
return (
<Flex direction="row" width="full" height="full">
Fail to retrieve Album Data.
</Flex>
);
}
return (
<Flex direction="row" width="full" height="full">
<Covers
data={dataAlbum?.covers}
size="100"
height="full"
iconEmpty={<LuDisc3 size="100" height="full" />}
/>
<Flex
direction="column"
width="150px"
maxWidth="150px"
height="full"
paddingLeft="5px"
overflowX="hidden"
>
<Text
as="span"
align="left"
fontSize="20px"
fontWeight="bold"
userSelect="none"
marginRight="auto"
overflow="hidden"
noOfLines={[1, 2]}
>
{dataAlbum?.name}
</Text>
<Text
as="span"
align="left"
fontSize="15px"
userSelect="none"
marginRight="auto"
overflow="hidden"
noOfLines={1}
>
{countTracksOfAnAlbum} track{countTracksOfAnAlbum >= 1 && 's'}
</Text>
</Flex>
</Flex>
);
};

View File

@ -0,0 +1,10 @@
import { DisplayAlbum } from '@/components/album/DisplayAlbum';
import { useSpecificAlbum } from '@/service/Album';
export type DisplayAlbumIdProps = {
id: number;
};
export const DisplayAlbumId = ({ id }: DisplayAlbumIdProps) => {
const { dataAlbum } = useSpecificAlbum(id);
return <DisplayAlbum dataAlbum={dataAlbum} />;
};

View File

@ -8,6 +8,7 @@ import {
import { AudioPlayer } from '@/components/AudioPlayer';
import { Error404 } from '@/errors';
import { ErrorBoundary } from '@/errors/ErrorBoundary';
import { AlbumRoutes } from '@/scene/album/AlbumRoutes';
import { ArtistRoutes } from '@/scene/artist/ArtistRoutes';
import { HomePage } from '@/scene/home/HomePage';
import { SSORoutes } from '@/scene/sso/SSORoutes';
@ -24,6 +25,7 @@ export const App = () => {
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="artist/*" element={<ArtistRoutes />} />
<Route path="album/*" element={<AlbumRoutes />} />
<Route path="sso/*" element={<SSORoutes />} />
<Route path="*" element={<Error404 />} />
</Routes>

View File

@ -0,0 +1,106 @@
import { Box, Flex, Text } from '@chakra-ui/react';
import { LuDisc3 } from 'react-icons/lu';
import { useParams } from 'react-router-dom';
import { Covers } from '@/components/Cover';
import { DisplayTrack } from '@/components/DisplayTrack';
import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { TopBar } from '@/components/TopBar/TopBar';
import { useActivePlaylistService } from '@/service/ActivePlaylist';
import { useSpecificAlbum } from '@/service/Album';
import { useTracksOfAnAlbum } from '@/service/Track';
import { useThemeMode } from '@/utils/theme-tools';
export const AlbumDetailPage = () => {
const { albumId } = useParams();
const albumIdInt = albumId ? parseInt(albumId, 10) : undefined;
const { mode } = useThemeMode();
const { playInList } = useActivePlaylistService();
const { dataAlbum } = useSpecificAlbum(albumIdInt);
const { tracksOnAnAlbum } = useTracksOfAnAlbum(albumIdInt);
const onSelectItem = (trackId: number) => {
//navigate(`/artist/${artistIdInt}/album/${albumId}`);
let currentPlay = 0;
const listTrackId: number[] = [];
for (let iii = 0; iii < tracksOnAnAlbum.length; iii++) {
listTrackId.push(tracksOnAnAlbum[iii].id);
if (tracksOnAnAlbum[iii].id === trackId) {
currentPlay = iii;
}
}
playInList(currentPlay, listTrackId);
};
console.log(`dataAlbum = ${JSON.stringify(dataAlbum, null, 2)}`);
if (!dataAlbum) {
return (
<>
<TopBar />
<PageLayoutInfoCenter>
Fail to load artist id: {albumId}
</PageLayoutInfoCenter>
</>
);
}
return (
<>
<TopBar />
<PageLayout>
<Flex
direction="row"
width="80%"
marginX="auto"
padding="10px"
gap="10px"
>
<Covers
data={dataAlbum?.covers}
iconEmpty={<LuDisc3 size="100" height="full" />}
/>
<Flex direction="column" width="80%" marginRight="auto">
<Text fontSize="24px" fontWeight="bold">
{dataAlbum?.name}
</Text>
{dataAlbum?.description && (
<Text>Description: {dataAlbum?.description}</Text>
)}
{dataAlbum?.publication && (
<Text>first name: {dataAlbum?.publication}</Text>
)}
</Flex>
</Flex>
<Flex
direction="column"
gap="20px"
marginX="auto"
padding="20px"
width="80%"
>
{tracksOnAnAlbum?.map((data) => (
<Box
minWidth="100%"
height="60px"
border="1px"
borderColor="brand.900"
backgroundColor={mode('#FFFFFF88', '#00000088')}
key={data.id}
padding="5px"
as="button"
_hover={{
boxShadow: 'outline-over',
bgColor: mode('#FFFFFFF7', '#000000F7'),
}}
onClick={() => onSelectItem(data.id)}
>
<DisplayTrack track={data} />
</Box>
))}
<EmptyEnd />
</Flex>
</PageLayout>
</>
);
};

View File

@ -0,0 +1,16 @@
import { Navigate, Route, Routes } from 'react-router-dom';
import { Error404 } from '@/errors';
import { AlbumDetailPage } from '@/scene/album/AlbumDetailPage';
import { AlbumsPage } from '@/scene/album/AlbumsPage';
export const AlbumRoutes = () => {
return (
<Routes>
<Route path="/" element={<Navigate to="all" replace />} />
<Route path="all" element={<AlbumsPage />} />
<Route path=":albumId" element={<AlbumDetailPage />} />
<Route path="*" element={<Error404 />} />
</Routes>
);
};

View File

@ -0,0 +1,57 @@
import { Text, Wrap, WrapItem } from '@chakra-ui/react';
import { useNavigate } from 'react-router-dom';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { TopBar } from '@/components/TopBar/TopBar';
import { DisplayAlbum } from '@/components/album/DisplayAlbum';
import { useAlbumService } from '@/service/Album';
import { useThemeMode } from '@/utils/theme-tools';
export const AlbumsPage = () => {
const { store } = useAlbumService();
const { mode } = useThemeMode();
const navigate = useNavigate();
const onSelectItem = (albumId: number) => {
navigate(`/album/${albumId}`);
};
if (store.isLoading) {
return (
<>
<TopBar />
<PageLayoutInfoCenter>No Album available</PageLayoutInfoCenter>
</>
);
}
return (
<>
<TopBar />
<PageLayout>
<Text>All Albums:</Text>
<Wrap spacing="20px" marginX="auto" padding="20px" justify="center">
{store.data?.map((data) => (
<WrapItem
width="270px"
height="120px"
border="1px"
borderColor="brand.900"
backgroundColor={mode('#FFFFFF88', '#00000088')}
key={data.id}
padding="5px"
as="button"
_hover={{
boxShadow: 'outline-over',
bgColor: mode('#FFFFFFF7', '#000000F7'),
}}
onClick={() => onSelectItem(data.id)}
>
<DisplayAlbum dataAlbum={data} />
</WrapItem>
))}
</Wrap>
</PageLayout>
</>
);
};

View File

@ -1,66 +1,19 @@
import { Box, Flex, Text, Wrap, WrapItem } from '@chakra-ui/react';
import { LuDisc3, LuFileAudio, LuMusic2, LuUser } from 'react-icons/lu';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, Flex, Text } from '@chakra-ui/react';
import { LuDisc3, LuUser } from 'react-icons/lu';
import { useParams } from 'react-router-dom';
import { Album, Artist, Track } from '@/back-api';
import { Covers } from '@/components/Cover';
import { DisplayTrack } from '@/components/DisplayTrack';
import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { TopBar } from '@/components/TopBar/TopBar';
import { useActivePlaylistService } from '@/service/ActivePlaylist';
import { useSpecificAlbum } from '@/service/Album';
import { useArtistService, useSpecificArtist } from '@/service/Artist';
import { useAlbumIdsOfAnArtist, useTracksOfAnAlbum } from '@/service/Track';
import { useSpecificArtist } from '@/service/Artist';
import { useTracksOfAnAlbum } from '@/service/Track';
import { useThemeMode } from '@/utils/theme-tools';
export type DisplayTrackProps = {
track: Track;
};
export const DisplayTrack = ({ track }: DisplayTrackProps) => {
return (
<Flex direction="row" width="full" height="full">
<Covers
data={track?.covers}
size="50"
height="full"
iconEmpty={<LuMusic2 size="50" height="full" />}
/>
<Flex
direction="column"
width="full"
height="full"
paddingLeft="5px"
overflowX="hidden"
>
<Text
as="span"
align="left"
fontSize="20px"
fontWeight="bold"
userSelect="none"
marginRight="auto"
overflow="hidden"
noOfLines={[1, 2]}
marginY="auto"
>
[{track.track}] {track.name}
</Text>
</Flex>
</Flex>
);
};
export const EmptyEnd = () => {
return (
<Box
width="full"
height="25%"
minHeight="25%"
borderWidth="1px"
borderColor="red"
></Box>
);
};
export const ArtistAlbumDetailPage = () => {
const { artistId, albumId } = useParams();
const artistIdInt = artistId ? parseInt(artistId, 10) : undefined;
@ -85,12 +38,14 @@ export const ArtistAlbumDetailPage = () => {
console.log(`dataAlbum = ${JSON.stringify(dataAlbum, null, 2)}`);
if (!dataAlbum) {
<>
<TopBar />
<PageLayoutInfoCenter>
Fail to load artist id: {artistId}
</PageLayoutInfoCenter>
</>;
return (
<>
<TopBar />
<PageLayoutInfoCenter>
Fail to load artist id: {artistId}
</PageLayoutInfoCenter>
</>
);
}
return (
<>

View File

@ -1,55 +1,16 @@
import { Flex, Text, Wrap, WrapItem } from '@chakra-ui/react';
import { LuDisc3, LuUser } from 'react-icons/lu';
import { LuUser } from 'react-icons/lu';
import { useNavigate, useParams } from 'react-router-dom';
import { Album, Artist } from '@/back-api';
import { Covers } from '@/components/Cover';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { TopBar } from '@/components/TopBar/TopBar';
import { useSpecificAlbum } from '@/service/Album';
import { useArtistService, useSpecificArtist } from '@/service/Artist';
import { DisplayAlbumId } from '@/components/album/DisplayAlbumId';
import { useSpecificArtist } from '@/service/Artist';
import { useAlbumIdsOfAnArtist } from '@/service/Track';
import { useThemeMode } from '@/utils/theme-tools';
export type DisplayAlbumProps = {
id: number;
};
export const DisplayAlbum = ({ id }: DisplayAlbumProps) => {
const { dataAlbum } = useSpecificAlbum(id);
return (
<Flex direction="row" width="full" height="full">
<Covers
data={dataAlbum?.covers}
size="100"
height="full"
iconEmpty={<LuDisc3 size="100" height="full" />}
/>
<Flex
direction="column"
width="150px"
maxWidth="150px"
height="full"
paddingLeft="5px"
overflowX="hidden"
>
<Text
as="span"
align="left"
fontSize="20px"
fontWeight="bold"
userSelect="none"
marginRight="auto"
overflow="hidden"
noOfLines={[1, 2]}
>
{dataAlbum?.name}
</Text>
</Flex>
</Flex>
);
};
export const ArtistDetailPage = () => {
const { artistId } = useParams();
const artistIdInt = artistId ? parseInt(artistId, 10) : undefined;
@ -62,12 +23,14 @@ export const ArtistDetailPage = () => {
const { albumIdsOfAnArtist } = useAlbumIdsOfAnArtist(artistIdInt);
if (!dataArtist) {
<>
<TopBar />
<PageLayoutInfoCenter>
Fail to load artist id: {artistId}
</PageLayoutInfoCenter>
</>;
return (
<>
<TopBar />
<PageLayoutInfoCenter>
Fail to load artist id: {artistId}
</PageLayoutInfoCenter>
</>
);
}
return (
<>
@ -116,7 +79,7 @@ export const ArtistDetailPage = () => {
}}
onClick={() => onSelectItem(data)}
>
<DisplayAlbum id={data} key={data} />
<DisplayAlbumId id={data} key={data} />
</WrapItem>
))}
</Wrap>

View File

@ -139,10 +139,10 @@ export const useTracksOfAnAlbum = (idAlbum?: number) => {
* @param AlbumId - Id of the album.
* @returns The number of element present in this season
*/
export const useCountTracksWithAlbumId = (albumId: number) => {
export const useCountTracksWithAlbumId = (albumId?: number) => {
const { tracksOnAnAlbum } = useTracksOfAnAlbum(albumId);
const countTracksOnAnArtist = tracksOnAnAlbum.length;
return { countTracksOnAnArtist };
const countTracksOfAnAlbum = tracksOnAnAlbum.length;
return { countTracksOfAnAlbum };
};
/**