Front: Use history include to get best/last score for a song
This commit is contained in:
committed by
Clément Le Bihan
parent
b4f04f9b71
commit
7a6dc8b0c9
@@ -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 (
|
||||
<HStack space={screenSize === 'xl' ? 2 : 1} style={[styles.container, props.style]}>
|
||||
@@ -179,7 +175,7 @@ function MusicItemComponent(props: MusicItemType) {
|
||||
/>
|
||||
</Row>
|
||||
</Column>
|
||||
{[formattedLevel, formattedLastScore, formattedBestScore].map((value, index) => (
|
||||
{[formattedLastScore, formattedBestScore].map((value, index) => (
|
||||
<Text key={index} style={styles.stats}>
|
||||
{value}
|
||||
</Text>
|
||||
|
||||
@@ -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>
|
||||
{[
|
||||
{ text: translate('musicListTitleLevel'), icon: Chart2 },
|
||||
{ text: translate('musicListTitleLastScore'), icon: ArrowRotateLeft },
|
||||
{ text: translate('musicListTitleBestScore'), icon: Cup },
|
||||
].map((value) => (
|
||||
|
||||
@@ -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 (
|
||||
<View
|
||||
style={{
|
||||
|
||||
@@ -6,6 +6,7 @@ import ResponseHandler from './ResponseHandler';
|
||||
import API from '../API';
|
||||
import { AlbumValidator } from './Album';
|
||||
import { GenreValidator } from './Genre';
|
||||
import { SongHistoryItem, SongHistoryItemValidator } from './SongHistory';
|
||||
|
||||
export type SongInclude = 'artist' | 'album' | 'genre' | 'SongHistory' | 'likedByUsers';
|
||||
|
||||
@@ -20,20 +21,33 @@ export const SongValidator = yup
|
||||
difficulties: SongDetailsValidator.required(),
|
||||
details: SongDetailsValidator.required(),
|
||||
cover: yup.string().required(),
|
||||
SongHistory: yup
|
||||
.lazy(() => 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<typeof SongValidator>;
|
||||
|
||||
export const SongHandler: ResponseHandler<Song> = {
|
||||
export const SongHandler: ResponseHandler<yup.InferType<typeof SongValidator>> = {
|
||||
validator: SongValidator,
|
||||
};
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user