From 7a6dc8b0c9660abef1e182b93ca1df80d783967f Mon Sep 17 00:00:00 2001 From: Arthur Jamet Date: Tue, 19 Dec 2023 11:13:25 +0100 Subject: [PATCH] Front: Use history include to get best/last score for a song --- front/components/UI/MusicItem.tsx | 14 +++++--------- front/components/UI/MusicList.tsx | 3 +-- front/components/V2/SongCardInfo.tsx | 27 ++++++++++++--------------- front/models/Song.ts | 18 ++++++++++++++++-- front/models/SongHistory.ts | 2 -- front/views/MusicView.tsx | 7 +++---- front/views/V2/DiscoveryView.tsx | 2 +- 7 files changed, 38 insertions(+), 35 deletions(-) diff --git a/front/components/UI/MusicItem.tsx b/front/components/UI/MusicItem.tsx index 8ff0287..804f50a 100644 --- a/front/components/UI/MusicItem.tsx +++ b/front/components/UI/MusicItem.tsx @@ -20,14 +20,11 @@ export interface MusicItemType { /** The URL for the song's cover image. */ image: string; - /** The level of the song difficulty . */ - level: number; - /** The last score achieved for this song. */ - lastScore: number; + lastScore: number | null | undefined; /** The highest score achieved for this song. */ - bestScore: number; + bestScore: number | null | undefined; /** Indicates whether the song is liked/favorited by the user. */ liked: boolean; @@ -141,9 +138,8 @@ function MusicItemComponent(props: MusicItemType) { ); // Memoizing formatted numbers to avoid unnecessary computations. - const formattedLevel = useMemo(() => formatNumber(props.level), [props.level]); - const formattedLastScore = useMemo(() => formatNumber(props.lastScore), [props.lastScore]); - const formattedBestScore = useMemo(() => formatNumber(props.bestScore), [props.bestScore]); + const formattedLastScore = useMemo(() => formatNumber(props.lastScore ?? 0), [props.lastScore]); + const formattedBestScore = useMemo(() => formatNumber(props.bestScore ?? 0), [props.bestScore]); return ( @@ -179,7 +175,7 @@ function MusicItemComponent(props: MusicItemType) { /> - {[formattedLevel, formattedLastScore, formattedBestScore].map((value, index) => ( + {[formattedLastScore, formattedBestScore].map((value, index) => ( {value} diff --git a/front/components/UI/MusicList.tsx b/front/components/UI/MusicList.tsx index 1c37dc2..fbd80c4 100644 --- a/front/components/UI/MusicList.tsx +++ b/front/components/UI/MusicList.tsx @@ -3,7 +3,7 @@ import { FlatList, HStack, View, useBreakpointValue, useTheme, Text, Row } from import { ActivityIndicator, StyleSheet } from 'react-native'; import MusicItem, { MusicItemType } from './MusicItem'; import ButtonBase from './ButtonBase'; -import { ArrowDown2, Chart2, ArrowRotateLeft, Cup, Icon } from 'iconsax-react-native'; +import { ArrowDown2, ArrowRotateLeft, Cup, Icon } from 'iconsax-react-native'; import { translate } from '../../i18n/i18n'; // Props type definition for MusicItemTitle. @@ -176,7 +176,6 @@ function MusicListComponent({ {translate('musicListTitleSong')} {[ - { text: translate('musicListTitleLevel'), icon: Chart2 }, { text: translate('musicListTitleLastScore'), icon: ArrowRotateLeft }, { text: translate('musicListTitleBestScore'), icon: Cup }, ].map((value) => ( diff --git a/front/components/V2/SongCardInfo.tsx b/front/components/V2/SongCardInfo.tsx index 26be401..d08a9cb 100644 --- a/front/components/V2/SongCardInfo.tsx +++ b/front/components/V2/SongCardInfo.tsx @@ -10,21 +10,6 @@ type SongCardInfoProps = { onPlay: () => void; }; -const Scores = [ - { - icon: 'warning', - score: 3, - }, - { - icon: 'star', - score: -225, - }, - { - icon: 'trophy', - score: 100, - }, -]; - const SongCardInfo = (props: SongCardInfoProps) => { const screenSize = useBreakpointValue({ base: 'small', md: 'big' }); const isPhone = screenSize === 'small'; @@ -37,6 +22,18 @@ const SongCardInfo = (props: SongCardInfoProps) => { width: isPhone ? 160 : 200, }; + +const Scores = [ + { + icon: 'time', + score: props.song.lastScore ?? '?', + }, + { + icon: 'trophy', + score: props.song.bestScore ?? '?', + }, +]; + return ( yup.array(SongHistoryItemValidator.default(undefined))) + .optional(), + bestScore: yup.number().optional().nullable(), + lastScore: yup.number().optional().nullable(), artist: yup.lazy(() => ArtistValidator.default(undefined)).optional(), album: yup.lazy(() => AlbumValidator.default(undefined)).optional(), genre: yup.lazy(() => GenreValidator.default(undefined)).optional(), }) .concat(ModelValidator) - .transform((song: Song) => ({ + .transform((song: Song & { SongHistory: SongHistoryItem[] }) => ({ ...song, cover: `${API.baseUrl}/song/${song.id}/illustration`, details: song.difficulties, + bestScore: + song.SongHistory?.map(({ info }) => info.score) + .sort() + .at(-1) ?? null, + lastScore: + song.SongHistory?.map(({ info, playDate }) => ({ info, playDate })) + .sort((a, b) => yup.date().cast(a.playDate)!.getTime() - yup.date().cast(b.playDate)!.getTime()) + .at(0)?.info.score ?? null, })); export type Song = yup.InferType; -export const SongHandler: ResponseHandler = { +export const SongHandler: ResponseHandler> = { validator: SongValidator, }; diff --git a/front/models/SongHistory.ts b/front/models/SongHistory.ts index 6d4510e..6e2cc9a 100644 --- a/front/models/SongHistory.ts +++ b/front/models/SongHistory.ts @@ -1,12 +1,10 @@ import * as yup from 'yup'; import ResponseHandler from './ResponseHandler'; import { ModelValidator } from './Model'; -import { SongValidator } from './Song'; export const SongHistoryItemValidator = yup .object({ songID: yup.number().required(), - song: SongValidator.optional().default(undefined), userID: yup.number().required(), info: yup .object({ diff --git a/front/views/MusicView.tsx b/front/views/MusicView.tsx index 617ecd4..d866b93 100644 --- a/front/views/MusicView.tsx +++ b/front/views/MusicView.tsx @@ -21,16 +21,15 @@ import { LoadingView } from '../components/Loading'; export const FavoritesMusic = () => { const navigation = useNavigation(); - const likedSongs = useQuery(API.getLikedSongs(['artist'])); + const likedSongs = useQuery(API.getLikedSongs(['artist', 'SongHistory'])); const musics = likedSongs.data?.map((x) => ({ artist: x.song.artist!.name, song: x.song.name, image: x.song.cover, - level: 42, - lastScore: 42, - bestScore: 42, + lastScore: x.song.lastScore, + bestScore: x.song.bestScore, liked: true, onLike: () => { console.log('onLike'); diff --git a/front/views/V2/DiscoveryView.tsx b/front/views/V2/DiscoveryView.tsx index 9c6a335..797716d 100644 --- a/front/views/V2/DiscoveryView.tsx +++ b/front/views/V2/DiscoveryView.tsx @@ -10,7 +10,7 @@ import GoldenRatio from '../../components/V2/GoldenRatio'; // eslint-disable-next-line @typescript-eslint/ban-types const HomeView = (props: RouteProps<{}>) => { - const suggestionsQuery = useQuery(API.getSongSuggestions(['artist'])); + const suggestionsQuery = useQuery(API.getSongSuggestions(['artist', 'SongHistory'])); const navigation = useNavigation(); const screenSize = useBreakpointValue({ base: 'small', md: 'big' }); const isPhone = screenSize === 'small';