Front: Score Modal

This commit is contained in:
Arthur Jamet
2023-11-30 14:24:48 +01:00
parent b417076ee6
commit 72f17c018e
6 changed files with 116 additions and 190 deletions

View File

@@ -15,7 +15,6 @@ import SettingsTab from './views/settings/SettingsView';
import { useQuery } from './Queries';
import API, { APIError } from './API';
import PlayView from './views/PlayView';
import ScoreView from './views/ScoreView';
import { LoadingView } from './components/Loading';
import ProfileView from './views/ProfileView';
import useColorScheme from './hooks/colorScheme';
@@ -77,11 +76,6 @@ const protectedRoutes = () =>
options: { title: translate('genreFilter') },
link: '/genre/:genreId',
},
Score: {
component: ScoreView,
options: { title: translate('score'), headerLeft: null },
link: undefined,
},
Search: {
component: SearchView,
options: { headerShown: false },

View File

@@ -0,0 +1,102 @@
import { Column, Row, Text, useTheme } from 'native-base';
import ButtonBase from './UI/ButtonBase';
import { Translate, TranslationKey, translate } from '../i18n/i18n';
import { Play, Star1 } from 'iconsax-react-native';
import { useNavigation } from '../Navigation';
type ScoreModalProps = {
songId: number;
overallScore: number;
precision: number;
score: {
missed: number;
good: number;
great: number;
perfect: number;
wrong: number;
max_score: number;
current_streak: number;
max_streak: number;
};
};
const ScoreModal = (props: ScoreModalProps) => {
// const props = {
// songId: 1,
// overallScore: 74,
// precision: 0,
// score: {
// missed: 9,
// good: 1,
// great: 2,
// perfect: 4,
// wrong: 0,
// max_score: 100,
// current_streak: 1,
// max_streak: 11,
// } as const
// } as const; //TODO DELETE ME
const navigation = useNavigation()
const theme = useTheme();
const score = (props.overallScore * 100) / props.score.max_score
const column1 = {
perfect: [props.score.perfect, 'primary'],
great: [props.score.great, 'secondary'],
good: [props.score.good, 'success']
} as const
const column2 = {
bestStreak: [props.score.max_streak, 'notification'],
missed: [props.score.missed, 'alert'],
wrong: [props.score.wrong, 'error']
} as const
return <Column space={4} style={{ alignItems: 'center' }}>
<Row space={2} style={{ justifyContent: 'center' }}>
{[1, 2, 3].map((index) => (
<Star1
color={theme.colors.primary[500]}
key={index}
variant={score >= (index * 100/ 4) ? 'Bold' : 'Outline' }
/>
))}
</Row>
<Text fontSize='3xl' >{score}%</Text>
<Row w="100%" style={{ justifyContent: 'space-between' }}>
<Translate translationKey='precision'/>
<Text>{props.precision}%</Text>
</Row>
<Row w="100%" space={2}>
{([column1, column2] as const).map((column, columnIndex) => (
<Column w="50%" space={2} key={columnIndex}>
{Object.keys(column).map((key) => {
const translationKey = key;
const [value, color] = column[translationKey as keyof typeof column] as [number, string];
return <Row key={translationKey} style={{ justifyContent: 'space-between' }}>
<Translate translationKey={translationKey as TranslationKey} fontWeight={'bold'} color={`${color}.500`} />
<Text>x{value}</Text>
</Row>
})}
</Column>
))}
</Row>
<Row style={{ justifyContent: 'space-between' }}>
<ButtonBase
style={{}}
icon={Play}
type="outlined"
title={translate('playAgain')}
onPress={() => navigation.navigate('Play', { songId: props.songId })}
/>
<ButtonBase
style={{}}
icon={Play}
type="filled"
title={translate('menuMusic')}
onPress={() => navigation.goBack()}
/>
</Row>
</Column>
};
export default ScoreModal;

View File

@@ -7,7 +7,7 @@ import React from 'react';
import GlassmorphismCC from './Glassmorphism';
type PopupCCProps = {
title: string;
title?: string;
description?: string;
children?: ReactNode;
isVisible: boolean;
@@ -34,7 +34,7 @@ const PopupCC = ({ title, description, children, isVisible, setIsVisible }: Popu
}}
space={4}
>
<Heading size="md" mb={2} alignItems={'flex-end'}>
{(setIsVisible || title) && <Heading size="md" mb={2} alignItems={'flex-end'}>
<Row style={{ flex: 1, width: '100%', alignItems: 'flex-end' }}>
<Text style={{ flex: 1, width: '100%' }}>{title}</Text>
{setIsVisible !== undefined && (
@@ -45,7 +45,7 @@ const PopupCC = ({ title, description, children, isVisible, setIsVisible }: Popu
/>
)}
</Row>
</Heading>
</Heading>}
{description !== undefined && <Text>{description}</Text>}
{children !== undefined && children}
</Column>

View File

@@ -3,7 +3,7 @@ import useColorScheme from '../../hooks/colorScheme';
import { useQuery } from '../../Queries';
import API from '../../API';
import { LinearGradient } from 'expo-linear-gradient';
import { Cup, Discover, Music, SearchNormal1, Setting2, User } from 'iconsax-react-native';
import { Discover, Music, SearchNormal1, Setting2, User } from 'iconsax-react-native';
import { LoadingView } from '../Loading';
import ScaffoldDesktopCC from './ScaffoldDesktopCC';
import ScaffoldMobileCC from './ScaffoldMobileCC';
@@ -13,7 +13,6 @@ const menu = [
{ type: 'main', title: 'menuProfile', icon: User, link: 'User' },
{ type: 'main', title: 'menuMusic', icon: Music, link: 'Music' },
{ type: 'main', title: 'menuSearch', icon: SearchNormal1, link: 'Search' },
{ type: 'main', title: 'menuLeaderBoard', icon: Cup, link: 'Score' },
{ type: 'sub', title: 'menuSettings', icon: Setting2, link: 'Settings' },
] as const;

View File

@@ -33,6 +33,7 @@ import PopupCC from '../components/UI/PopupCC';
import ButtonBase from '../components/UI/ButtonBase';
import { Clock, Cup } from 'iconsax-react-native';
import PlayViewControlBar from '../components/Play/PlayViewControlBar';
import ScoreModal from '../components/ScoreModal';
type PlayViewProps = {
songId: number;
@@ -88,6 +89,7 @@ const PlayView = ({ songId, route }: RouteProps<PlayViewProps>) => {
const [paused, setPause] = useState<boolean>(true);
const stopwatch = useStopwatch();
const [time, setTime] = useState(0);
const [endResult, setEndResult] = useState();
const songHistory = useQuery(API.getSongHistory(songId));
const [partitionRendered, setPartitionRendered] = useState(false); // Used to know when partitionview can render
const [score, setScore] = useState(0); // Between 0 and 100
@@ -186,11 +188,10 @@ const PlayView = ({ songId, route }: RouteProps<PlayViewProps>) => {
if (data.type == 'end') {
endMsgReceived = true;
webSocket.current?.close();
navigation.dispatch(
StackActions.replace('Score', { songId: song.data!.id, ...data })
);
setEndResult({ songId: song.data!.id, ...data });
return;
}
console.log(data);
const points = data.info.score;
const maxPoints = data.info.max_score || 1;
@@ -332,10 +333,15 @@ const PlayView = ({ songId, route }: RouteProps<PlayViewProps>) => {
zIndex: 100,
}}
>
<PopupCC
isVisible={endResult != undefined}
>{
(() => endResult ? <ScoreModal {...endResult}/> : <></>)()
}</PopupCC>
<PopupCC
title={translate('selectPlayMode')}
description={translate('selectPlayModeExplaination')}
isVisible={type === undefined}
isVisible={false}
setIsVisible={
navigation.canGoBack()
? (isVisible) => {

View File

@@ -1,175 +0,0 @@
import { Card, Column, Image, Row, Text, ScrollView, VStack } from 'native-base';
import Translate from '../components/Translate';
import { RouteProps, useNavigation } from '../Navigation';
import { CardBorderRadius } from '../components/Card';
import TextButton from '../components/TextButton';
import API from '../API';
import CardGridCustom from '../components/CardGridCustom';
import SongCard from '../components/SongCard';
import { useQueries, useQuery } from '../Queries';
import { LoadingView } from '../components/Loading';
import ScoreGraph from '../components/ScoreGraph';
type ScoreViewProps = {
songId: number;
overallScore: number;
precision: number;
score: {
missed: number;
good: number;
great: number;
perfect: number;
wrong: number;
max_score: number;
current_streak: number;
max_streak: number;
};
};
const ScoreView = (props: RouteProps<ScoreViewProps>) => {
const { songId, overallScore, precision, score } = props;
const navigation = useNavigation();
const songQuery = useQuery(API.getSong(songId));
const artistQuery = useQuery(() => API.getArtist(songQuery.data!.artistId!), {
enabled: songQuery.data !== undefined,
});
const recommendations = useQuery(API.getSongSuggestions);
const artistRecommendations = useQueries(
recommendations.data
?.filter(({ artistId }) => artistId !== null)
.map((song) => API.getArtist(song.artistId)) ?? []
);
if (
!recommendations.data ||
artistRecommendations.find(({ data }) => !data) ||
!songQuery.data ||
(songQuery.data.artistId && !artistQuery.data)
) {
return <LoadingView />;
}
if (songQuery.isError) {
navigation.navigate('Error');
return <></>;
}
return (
<ScrollView p={8} contentContainerStyle={{ alignItems: 'center' }}>
<VStack width={{ base: '100%', lg: '50%' }} space={3} textAlign="center">
<Text bold fontSize="lg">
{songQuery.data.name}
</Text>
<Text bold>{artistQuery.data?.name}</Text>
<Row style={{ justifyContent: 'center', display: 'flex' }}>
<Card shadow={3} style={{ flex: 1 }}>
<Image
style={{
zIndex: 0,
aspectRatio: 1,
margin: 5,
borderRadius: CardBorderRadius,
}}
source={{ uri: songQuery.data.cover }}
/>
</Card>
<Card shadow={3} style={{ flex: 1 }}>
<Column style={{ justifyContent: 'space-evenly', flexGrow: 1 }}>
{/*<Row style={{ alignItems: 'center' }}>
<Text bold fontSize='xl'>
</Text>
<Translate translationKey='goodNotes' format={(t) => ' ' + t}/>
</Row>
<Row style={{ alignItems: 'center' }}>
<Text bold fontSize='xl'>
80
</Text>
<Translate translationKey='goodNotesInARow' format={(t) => ' ' + t}/>
</Row>*/}
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="score" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{overallScore + 'pts'}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="perfect" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{score.perfect}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="great" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{score.great}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="good" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{score.good}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="wrong" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{score.wrong}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="missed" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{score.missed}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="bestStreak" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{score.max_streak}
</Text>
</Row>
<Row style={{ alignItems: 'center' }}>
<Translate translationKey="precision" format={(t) => t + ' : '} />
<Text bold fontSize="xl">
{precision + '%'}
</Text>
</Row>
</Column>
</Card>
</Row>
<ScoreGraph />
<CardGridCustom
style={{ justifyContent: 'space-evenly' }}
content={recommendations.data.map((i) => ({
cover: i.cover,
name: i.name,
artistName:
artistRecommendations.find(({ data }) => data?.id == i.artistId)?.data
?.name ?? '',
songId: i.id,
}))}
cardComponent={SongCard}
heading={
<Text fontSize="sm">
<Translate translationKey="songsToGetBetter" />
</Text>
}
/>
<Row space={3} style={{ width: '100%', justifyContent: 'center' }}>
<TextButton
colorScheme="gray"
translate={{ translationKey: 'backBtn' }}
onPress={() => navigation.navigate('Home', {})}
/>
<TextButton
onPress={() => navigation.navigate('Play', { songId })}
translate={{ translationKey: 'playAgain' }}
/>
</Row>
</VStack>
</ScrollView>
);
};
export default ScoreView;