diff --git a/front/Navigation.tsx b/front/Navigation.tsx
index 486ac99..6624786 100644
--- a/front/Navigation.tsx
+++ b/front/Navigation.tsx
@@ -11,7 +11,6 @@ import { RootState, useSelector } from './state/Store';
import { useDispatch } from 'react-redux';
import { Translate, translate } from './i18n/i18n';
import SongLobbyView from './views/SongLobbyView';
-import AuthenticationView from './views/AuthenticationView';
import StartPageView from './views/StartPageView';
import HomeView from './views/HomeView';
import SearchView from './views/SearchView';
@@ -31,6 +30,8 @@ import ErrorView from './views/ErrorView';
import GenreDetailsView from './views/GenreDetailsView';
import GoogleView from './views/GoogleView';
import VerifiedView from './views/VerifiedView';
+import SigninView from './views/SigninView';
+import SignupView from './views/SignupView';
// Util function to hide route props in URL
const removeMe = () => '';
@@ -97,15 +98,13 @@ const publicRoutes = () =>
link: '/',
},
Login: {
- component: (params: RouteProps<{}>) =>
- AuthenticationView({ isSignup: false, ...params }),
- options: { title: translate('signInBtn') },
+ component: SigninView,
+ options: { title: translate('signInBtn'), headerShown: false },
link: '/login',
},
Signup: {
- component: (params: RouteProps<{}>) =>
- AuthenticationView({ isSignup: true, ...params }),
- options: { title: translate('signUpBtn') },
+ component: SignupView,
+ options: { title: translate('signUpBtn'), headerShown: false },
link: '/signup',
},
Oops: {
diff --git a/front/assets/banner.jpg b/front/assets/banner.jpg
new file mode 100644
index 0000000..2767e1c
Binary files /dev/null and b/front/assets/banner.jpg differ
diff --git a/front/components/ScoreGraph.tsx b/front/components/ScoreGraph.tsx
index 01f3565..741522c 100644
--- a/front/components/ScoreGraph.tsx
+++ b/front/components/ScoreGraph.tsx
@@ -1,6 +1,5 @@
import { Box, useBreakpointValue, useTheme } from 'native-base';
import { LineChart } from 'react-native-chart-kit';
-import { CardBorderRadius } from './Card';
import SongHistory from '../models/SongHistory';
import { useState } from 'react';
@@ -36,8 +35,7 @@ const ScoreGraph = (props: ScoreGraphProps) => {
return (
setContainerWidth(event.nativeEvent.layout.width)}
>
{
data: scores.map(({ score }) => score),
},
],
- }}
+ }
+ }
width={containerWidth}
height={200} // Completely arbitrary
transparent={true}
+ withDots={false}
yAxisSuffix=" pts"
chartConfig={{
decimalPlaces: 0,
@@ -63,13 +63,13 @@ const ScoreGraph = (props: ScoreGraphProps) => {
},
}}
bezier
- style={{
- margin: 3,
- shadowColor: theme.colors.primary[400],
- shadowOpacity: 1,
- shadowRadius: 20,
- borderRadius: CardBorderRadius,
- }}
+ // style={{
+ // margin: 3,
+ // shadowColor: theme.colors.primary[400],
+ // shadowOpacity: 1,
+ // shadowRadius: 20,
+ // borderRadius: CardBorderRadius,
+ // }}
/>
);
diff --git a/front/components/UI/ButtonBase.tsx b/front/components/UI/ButtonBase.tsx
index d67757e..0834d71 100644
--- a/front/components/UI/ButtonBase.tsx
+++ b/front/components/UI/ButtonBase.tsx
@@ -2,15 +2,16 @@ import React, { useState } from 'react';
import { StyleSheet, ActivityIndicator, View, Image, StyleProp, ViewStyle } from 'react-native';
import InteractiveBase from './InteractiveBase';
import { Text, useTheme } from 'native-base';
+import { Icon } from 'iconsax-react-native';
interface ButtonProps {
title?: string;
style?: StyleProp;
onPress?: () => Promise;
isDisabled?: boolean;
- icon?: (size: string, color: string) => React.ReactNode;
+ icon?: Icon;
iconImage?: string;
- type: 'filled' | 'outlined' | 'menu';
+ type?: 'filled' | 'outlined' | 'menu';
}
const ButtonBase: React.FC = ({
@@ -88,6 +89,7 @@ const ButtonBase: React.FC = ({
});
const typeToStyleAnimator = { filled: styleButton, outlined: styleButton, menu: styleMenu };
+ const MyIcon: Icon = icon as Icon;
return (
= ({
/>
) : (
- {icon && icon('18', type === 'outlined' ? '#6075F9' : '#FFFFFF')}
+ {icon && }
{iconImage && }
{title && {title}}
@@ -132,7 +134,6 @@ const styles = StyleSheet.create({
icon: {
width: 18,
height: 18,
- // marginRight: 8,
},
text: {
color: '#fff',
diff --git a/front/components/UI/InteractiveBase.tsx b/front/components/UI/InteractiveBase.tsx
index 8022cb2..502f6c3 100644
--- a/front/components/UI/InteractiveBase.tsx
+++ b/front/components/UI/InteractiveBase.tsx
@@ -225,8 +225,18 @@ const InteractiveBase: React.FC = ({
elevation: elevationValue,
};
+ const disableStyle = {
+ backgroundColor: isOutlined ? 'rgba(0,0,0,0.3)' : styleAnimate.Disabled.backgroundColor,
+ borderColor: isOutlined ? styleAnimate.Disabled.backgroundColor : 'transparent',
+ borderWidth: 2,
+ scale: styleAnimate.Disabled.scale,
+ shadowOpacity: styleAnimate.Disabled.shadowOpacity,
+ shadowRadius: styleAnimate.Disabled.shadowRadius,
+ elevation: styleAnimate.Disabled.elevation,
+ }
+
return (
-
+
void};
+}
+
+const ScaffoldAuth: FunctionComponent = ({title, description, form, submitButton, link}) => {
+ const layout = useWindowDimensions();
+
+ return (
+
+
+
+
+ {title}
+ {description}
+
+
+ Linking.openURL(`${API.baseUrl}/auth/login/google`)}
+ />
+ or
+
+ {form}
+
+ {submitButton}
+
+ {link.description}
+
+ {link.text}
+
+
+
+
+
+ {
+ layout.width > 650 ?
+
+
+
+ : <>>
+ }
+
+
+ );
+};
+
+export default ScaffoldAuth;
diff --git a/front/components/UI/SeparatorBase.tsx b/front/components/UI/SeparatorBase.tsx
index 2ce23d4..2f94c79 100644
--- a/front/components/UI/SeparatorBase.tsx
+++ b/front/components/UI/SeparatorBase.tsx
@@ -11,7 +11,7 @@ const styles = StyleSheet.create({
width: '100%',
flexDirection: 'row',
alignItems: 'center',
- marginVertical: 20,
+ marginVertical: 2,
},
text: {
color: 'white',
diff --git a/front/components/UI/TextFieldBase.tsx b/front/components/UI/TextFieldBase.tsx
index 345c2b2..818f603 100644
--- a/front/components/UI/TextFieldBase.tsx
+++ b/front/components/UI/TextFieldBase.tsx
@@ -1,4 +1,4 @@
-import { Eye, EyeSlash } from 'iconsax-react-native';
+import { Eye, EyeSlash, Icon } from 'iconsax-react-native';
import React, { useState } from 'react';
import { View, TouchableOpacity, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import InteractiveBase from './InteractiveBase';
@@ -7,7 +7,7 @@ import { Input } from 'native-base';
export interface TextFieldBaseProps {
style?: StyleProp;
value?: string;
- icon?: (size: string, color: string) => React.ReactNode;
+ icon?: Icon;
iconColor?: string;
placeholder?: string;
autoComplete?:
@@ -66,6 +66,7 @@ const TextFieldBase: React.FC = ({
}) => {
const [isPasswordVisible, setPasswordVisible] = useState(!isSecret);
const [isFocused, setFocused] = useState(false);
+ const MyIcon: Icon = icon as Icon;
const styleAnimate = StyleSheet.create({
Default: {
@@ -102,7 +103,7 @@ const TextFieldBase: React.FC = ({
- {icon && icon('20', iconColor ? iconColor : isFocused ? '#5f74f7' : '#394694')}
+ {icon && }
= ({ error, style, ...textFiel
const styles = StyleSheet.create({
wrapper: {
- flex: 1,
width: '100%',
- // maxWidth: 400,
},
errorContainer: {
flexDirection: 'row',
diff --git a/front/views/AuthenticationView.test.tsx b/front/views/AuthenticationView.test.tsx
deleted file mode 100644
index 6814e23..0000000
--- a/front/views/AuthenticationView.test.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import { Provider } from 'react-redux';
-import TestRenderer from 'react-test-renderer';
-import store from '../state/Store';
-
-import AuthenticationView from '../views/AuthenticationView';
-
-describe('', () => {
- it('has 3 children', () => {
- const tree = TestRenderer.create(
-
-
-
- ).toJSON();
- expect(tree.children.length).toBe(3);
- });
-});
diff --git a/front/views/AuthenticationView.tsx b/front/views/AuthenticationView.tsx
deleted file mode 100644
index 36ff7b9..0000000
--- a/front/views/AuthenticationView.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import React from 'react';
-import { useDispatch } from '../state/Store';
-import { Translate, translate } from '../i18n/i18n';
-import API, { APIError } from '../API';
-import { setAccessToken } from '../state/UserSlice';
-import { Center, Button, Text } from 'native-base';
-import SigninForm from '../components/forms/signinform';
-import SignupForm from '../components/forms/signupform';
-import TextButton from '../components/TextButton';
-import { RouteProps, useNavigation } from '../Navigation';
-import * as Linking from 'expo-linking';
-
-const hanldeSignin = async (
- username: string,
- password: string,
- apiSetter: (accessToken: string) => void
-): Promise => {
- try {
- const apiAccess = await API.authenticate({ username, password });
- apiSetter(apiAccess);
- return translate('loggedIn');
- } catch (error) {
- if (error instanceof APIError) return translate(error.userMessage);
- if (error instanceof Error) return error.message;
- return translate('unknownError');
- }
-};
-
-const handleSignup = async (
- username: string,
- password: string,
- email: string,
- apiSetter: (accessToken: string) => void
-): Promise => {
- try {
- const apiAccess = await API.createAccount({ username, password, email });
- apiSetter(apiAccess);
- return translate('loggedIn');
- } catch (error) {
- if (error instanceof APIError) {
- if (error.status === 409) return translate('usernameTaken');
- return translate(error.userMessage);
- }
- if (error instanceof Error) return error.message;
- return translate('unknownError');
- }
-};
-
-type AuthenticationViewProps = {
- isSignup: boolean;
-};
-
-const AuthenticationView = ({ isSignup }: RouteProps) => {
- const dispatch = useDispatch();
- const navigation = useNavigation();
- const mode = isSignup ? 'signup' : 'signin';
-
- return (
-
-
-
-
- Linking.openURL(`${API.baseUrl}/auth/login/google`)}
- />
- {mode === 'signin' ? (
-
- hanldeSignin(username, password, (accessToken) =>
- dispatch(setAccessToken(accessToken))
- )
- }
- />
- ) : (
-
- handleSignup(username, password, email, (accessToken) =>
- dispatch(setAccessToken(accessToken))
- )
- }
- />
- )}
- {mode === 'signin' && (
-
- )}
- navigation.navigate(mode === 'signin' ? 'Signup' : 'Login', {})}
- />
-
- );
-};
-
-export default AuthenticationView;
diff --git a/front/views/ProfileView.tsx b/front/views/ProfileView.tsx
index 004c5c1..d66b7f0 100644
--- a/front/views/ProfileView.tsx
+++ b/front/views/ProfileView.tsx
@@ -1,14 +1,131 @@
import React from 'react';
-import { View } from 'react-native';
-import { Box, Heading, HStack } from 'native-base';
+import { useWindowDimensions, View } from 'react-native';
+import { Box, Column, Flex, Heading, HStack, Progress, Row, Text, VStack, Wrap } from 'native-base';
import { useNavigation } from '../Navigation';
import TextButton from '../components/TextButton';
import UserAvatar from '../components/UserAvatar';
import { LoadingView } from '../components/Loading';
import { useQuery } from '../Queries';
import API from '../API';
+import { LinearGradient } from 'expo-linear-gradient';
+import ButtonBase from '../components/UI/ButtonBase';
+import { translate } from '../i18n/i18n';
+import ScoreGraph from '../components/ScoreGraph';
+
+const fakeData = [
+ {score: 47, songID: 34, userID: 1, playDate: new Date("2023-08-20 8:27:21"), difficulties: 1},
+ {score: 1, songID: 603, userID: 1, playDate: new Date("2023-09-13 22:56:45"), difficulties: 1},
+ {score: 93, songID: 601, userID: 1, playDate: new Date("2023-08-11 5:30:13"), difficulties: 5},
+ {score: 55, songID: 456, userID: 1, playDate: new Date("2023-07-10 23:06:09"), difficulties: 4},
+ {score: 2, songID: 345, userID: 1, playDate: new Date("2023-07-23 18:33:24"), difficulties: 2},
+ {score: 47, songID: 625, userID: 1, playDate: new Date("2023-07-09 7:16:46"), difficulties: 1},
+ {score: 27, songID: 234, userID: 1, playDate: new Date("2023-07-06 15:56:53"), difficulties: 5},
+ {score: 85, songID: 866, userID: 1, playDate: new Date("2023-07-08 8:56:44"), difficulties: 2},
+ {score: 28, songID: 484, userID: 1, playDate: new Date("2023-09-12 6:05:32"), difficulties: 4},
+ {score: 5, songID: 443, userID: 1, playDate: new Date("2023-08-01 11:57:09"), difficulties: 3},
+ {score: 14, songID: 109, userID: 1, playDate: new Date("2023-07-03 22:54:07"), difficulties: 3},
+ {score: 57, songID: 892, userID: 1, playDate: new Date("2023-07-13 23:22:34"), difficulties: 5},
+ {score: 7, songID: 164, userID: 1, playDate: new Date("2023-07-02 0:15:13"), difficulties: 2},
+ {score: 42, songID: 761, userID: 1, playDate: new Date("2023-07-10 18:25:19"), difficulties: 3},
+ {score: 49, songID: 82, userID: 1, playDate: new Date("2023-09-12 12:51:15"), difficulties: 4},
+ {score: 83, songID: 488, userID: 1, playDate: new Date("2023-08-28 7:56:31"), difficulties: 5},
+ {score: 91, songID: 648, userID: 1, playDate: new Date("2023-07-21 10:16:33"), difficulties: 4},
+ {score: 67, songID: 210, userID: 1, playDate: new Date("2023-09-14 8:04:50"), difficulties: 1},
+ {score: 31, songID: 274, userID: 1, playDate: new Date("2023-07-10 11:24:28"), difficulties: 4},
+ {score: 29, songID: 930, userID: 1, playDate: new Date("2023-08-06 0:05:43"), difficulties: 5},
+ {score: 51, songID: 496, userID: 1, playDate: new Date("2023-08-14 9:43:14"), difficulties: 1},
+ {score: 56, songID: 370, userID: 1, playDate: new Date("2023-08-18 19:25:59"), difficulties: 2},
+ {score: 29, songID: 333, userID: 1, playDate: new Date("2023-07-11 4:26:44"), difficulties: 4},
+ {score: 95, songID: 921, userID: 1, playDate: new Date("2023-08-30 12:58:50"), difficulties: 1},
+ {score: 37, songID: 80, userID: 1, playDate: new Date("2023-07-16 7:17:57"), difficulties: 4},
+ {score: 90, songID: 134, userID: 1, playDate: new Date("2023-09-03 9:00:04"), difficulties: 1},
+ {score: 51, songID: 497, userID: 1, playDate: new Date("2023-07-31 19:34:43"), difficulties: 4},
+ {score: 95, songID: 368, userID: 1, playDate: new Date("2023-09-12 20:12:50"), difficulties: 4},
+ {score: 55, songID: 247, userID: 1, playDate: new Date("2023-09-16 2:45:13"), difficulties: 1},
+ {score: 26, songID: 725, userID: 1, playDate: new Date("2023-07-28 22:59:31"), difficulties: 2},
+ {score: 82, songID: 952, userID: 1, playDate: new Date("2023-08-01 6:31:47"), difficulties: 1},
+ {score: 88, songID: 85, userID: 1, playDate: new Date("2023-08-12 2:33:11"), difficulties: 5},
+ {score: 12, songID: 96, userID: 1, playDate: new Date("2023-09-03 14:00:33"), difficulties: 4},
+ {score: 100, songID: 807, userID: 1, playDate: new Date("2023-07-03 0:53:11"), difficulties: 3},
+ {score: 88, songID: 456, userID: 1, playDate: new Date("2023-08-06 9:17:15"), difficulties: 5},
+ {score: 10, songID: 889, userID: 1, playDate: new Date("2023-08-15 12:19:16"), difficulties: 3},
+ {score: 76, songID: 144, userID: 1, playDate: new Date("2023-09-10 2:56:49"), difficulties: 4},
+ {score: 60, songID: 808, userID: 1, playDate: new Date("2023-07-24 10:22:33"), difficulties: 1},
+ {score: 94, songID: 537, userID: 1, playDate: new Date("2023-08-03 23:22:29"), difficulties: 2},
+ {score: 100, songID: 465, userID: 1, playDate: new Date("2023-09-16 19:12:58"), difficulties: 2},
+ {score: 85, songID: 31, userID: 1, playDate: new Date("2023-08-17 5:29:49"), difficulties: 2},
+ {score: 98, songID: 345, userID: 1, playDate: new Date("2023-09-11 1:51:49"), difficulties: 1},
+ {score: 81, songID: 204, userID: 1, playDate: new Date("2023-08-21 2:46:56"), difficulties: 2},
+ {score: 21, songID: 40, userID: 1, playDate: new Date("2023-07-27 4:00:00"), difficulties: 2},
+ {score: 91, songID: 274, userID: 1, playDate: new Date("2023-07-14 16:09:49"), difficulties: 5},
+ {score: 99, songID: 416, userID: 1, playDate: new Date("2023-08-27 1:56:16"), difficulties: 5},
+ {score: 58, songID: 87, userID: 1, playDate: new Date("2023-09-08 19:30:20"), difficulties: 5},
+ {score: 90, songID: 744, userID: 1, playDate: new Date("2023-08-18 23:47:55"), difficulties: 2},
+ {score: 69, songID: 954, userID: 1, playDate: new Date("2023-08-07 1:55:52"), difficulties: 5},
+ {score: 75, songID: 467, userID: 1, playDate: new Date("2023-07-10 8:37:22"), difficulties: 4},
+ {score: 41, songID: 693, userID: 1, playDate: new Date("2023-09-11 5:15:16"), difficulties: 2},
+ {score: 56, songID: 140, userID: 1, playDate: new Date("2023-08-06 5:32:46"), difficulties: 2},
+ {score: 88, songID: 64, userID: 1, playDate: new Date("2023-07-31 20:24:30"), difficulties: 1},
+ {score: 99, songID: 284, userID: 1, playDate: new Date("2023-08-07 17:51:19"), difficulties: 5},
+ {score: 47, songID: 746, userID: 1, playDate: new Date("2023-07-18 17:45:56"), difficulties: 5},
+ {score: 80, songID: 791, userID: 1, playDate: new Date("2023-08-21 1:19:45"), difficulties: 1},
+ {score: 21, songID: 748, userID: 1, playDate: new Date("2023-07-04 9:09:27"), difficulties: 4},
+ {score: 75, songID: 541, userID: 1, playDate: new Date("2023-09-19 23:08:05"), difficulties: 2},
+ {score: 31, songID: 724, userID: 1, playDate: new Date("2023-07-09 2:01:29"), difficulties: 4},
+ {score: 24, songID: 654, userID: 1, playDate: new Date("2023-09-04 1:27:00"), difficulties: 1},
+ {score: 55, songID: 154, userID: 1, playDate: new Date("2023-07-10 17:48:17"), difficulties: 3},
+ {score: 4, songID: 645, userID: 1, playDate: new Date("2023-09-11 18:51:11"), difficulties: 2},
+ {score: 52, songID: 457, userID: 1, playDate: new Date("2023-07-30 19:12:52"), difficulties: 3},
+ {score: 68, songID: 236, userID: 1, playDate: new Date("2023-08-08 8:56:08"), difficulties: 3},
+ {score: 44, songID: 16, userID: 1, playDate: new Date("2023-07-22 10:39:34"), difficulties: 1},
+ {score: 59, songID: 863, userID: 1, playDate: new Date("2023-09-17 4:12:43"), difficulties: 1},
+ {score: 18, songID: 276, userID: 1, playDate: new Date("2023-07-08 15:47:54"), difficulties: 2},
+ {score: 64, songID: 557, userID: 1, playDate: new Date("2023-08-17 0:13:46"), difficulties: 1},
+ {score: 2, songID: 452, userID: 1, playDate: new Date("2023-07-26 5:13:31"), difficulties: 5},
+ {score: 99, songID: 546, userID: 1, playDate: new Date("2023-07-11 16:31:37"), difficulties: 1},
+ {score: 75, songID: 598, userID: 1, playDate: new Date("2023-08-12 22:56:24"), difficulties: 4},
+ {score: 4, songID: 258, userID: 1, playDate: new Date("2023-09-20 8:26:50"), difficulties: 2},
+ {score: 50, songID: 190, userID: 1, playDate: new Date("2023-09-20 20:07:06"), difficulties: 4},
+ {score: 9, songID: 914, userID: 1, playDate: new Date("2023-08-30 16:57:14"), difficulties: 5},
+ {score: 7, songID: 92, userID: 1, playDate: new Date("2023-07-18 20:33:44"), difficulties: 5},
+ {score: 94, songID: 98, userID: 1, playDate: new Date("2023-08-15 5:05:18"), difficulties: 5},
+ {score: 94, songID: 424, userID: 1, playDate: new Date("2023-07-22 9:59:12"), difficulties: 5},
+ {score: 14, songID: 635, userID: 1, playDate: new Date("2023-07-02 6:58:39"), difficulties: 4},
+ {score: 99, songID: 893, userID: 1, playDate: new Date("2023-08-05 16:09:33"), difficulties: 1},
+ {score: 94, songID: 67, userID: 1, playDate: new Date("2023-07-01 8:11:37"), difficulties: 2},
+ {score: 21, songID: 335, userID: 1, playDate: new Date("2023-08-03 2:07:44"), difficulties: 3},
+ {score: 47, songID: 294, userID: 1, playDate: new Date("2023-09-13 17:32:46"), difficulties: 4},
+ {score: 89, songID: 184, userID: 1, playDate: new Date("2023-07-04 5:20:13"), difficulties: 2},
+ {score: 28, songID: 345, userID: 1, playDate: new Date("2023-09-07 6:35:11"), difficulties: 3},
+ {score: 93, songID: 697, userID: 1, playDate: new Date("2023-07-29 0:07:10"), difficulties: 2},
+ {score: 58, songID: 666, userID: 1, playDate: new Date("2023-07-09 3:03:02"), difficulties: 2},
+ {score: 73, songID: 459, userID: 1, playDate: new Date("2023-08-05 7:33:54"), difficulties: 4},
+ {score: 50, songID: 695, userID: 1, playDate: new Date("2023-07-26 18:26:55"), difficulties: 4},
+ {score: 39, songID: 995, userID: 1, playDate: new Date("2023-08-24 17:34:09"), difficulties: 3},
+ {score: 25, songID: 122, userID: 1, playDate: new Date("2023-08-25 18:54:12"), difficulties: 1},
+ {score: 29, songID: 439, userID: 1, playDate: new Date("2023-09-15 0:44:48"), difficulties: 3},
+ {score: 79, songID: 234, userID: 1, playDate: new Date("2023-09-13 13:53:16"), difficulties: 2},
+ {score: 0, songID: 369, userID: 1, playDate: new Date("2023-08-30 22:54:34"), difficulties: 1},
+ {score: 25, songID: 223, userID: 1, playDate: new Date("2023-09-13 1:09:11"), difficulties: 3},
+ {score: 55, songID: 716, userID: 1, playDate: new Date("2023-07-12 19:43:23"), difficulties: 3},
+ {score: 100, songID: 62, userID: 1, playDate: new Date("2023-07-11 15:33:40"), difficulties: 5},
+ {score: 74, songID: 271, userID: 1, playDate: new Date("2023-08-25 23:14:51"), difficulties: 3},
+ {score: 22, songID: 265, userID: 1, playDate: new Date("2023-07-17 15:01:38"), difficulties: 1},
+ {score: 79, songID: 552, userID: 1, playDate: new Date("2023-07-28 20:13:14"), difficulties: 5},
+ {score: 50, songID: 603, userID: 1, playDate: new Date("2023-07-06 3:52:21"), difficulties: 5},
+
+];
+
+function xpToLevel(xp: number): number {
+ return Math.floor(xp / 1000);
+}
+
+function xpToProgressBarValue(xp: number): number {
+ return Math.floor(xp / 10);
+}
const ProfileView = () => {
+ const layout = useWindowDimensions();
const navigation = useNavigation();
const userQuery = useQuery(API.getUserInfo);
@@ -16,22 +133,59 @@ const ProfileView = () => {
return ;
}
+ // const user = userQuery.data;
+ const progessValue = xpToProgressBarValue(userQuery.data.data.xp);
+ const level = xpToLevel(userQuery.data.data.xp);
+
return (
-
-
-
-
- {userQuery.data.name}
- XP : {userQuery.data.data.xp}
-
-
-
- navigation.navigate('Settings')}
- translate={{ translationKey: 'settingsBtn' }}
- />
-
-
+
+ 650 ? 'row' : 'column', alignItems: 'center', paddingBottom: 20, justifyContent: 'space-between'}}>
+
+ 650 ? 20 : 0, paddingTop: layout.width > 650 ? 0 : 20, flex: 1, width: '100%'}}>
+
+ {userQuery.data.name}
+ navigation.navigate('Settings')}
+ />
+
+ Dernier entraînement il y a une semaine
+
+ 32 Completes
+ 42 En cours
+
+
+
+
+ {`${translate('level')} ${level}`}
+
+
+
+
);
};
diff --git a/front/views/SigninView.tsx b/front/views/SigninView.tsx
new file mode 100644
index 0000000..c82973a
--- /dev/null
+++ b/front/views/SigninView.tsx
@@ -0,0 +1,141 @@
+import React from 'react';
+import { useDispatch } from '../state/Store';
+import { translate } from '../i18n/i18n';
+import API, { APIError } from '../API';
+import { setAccessToken } from '../state/UserSlice';
+import { string } from 'yup';
+import { useToast } from 'native-base';
+import TextFormField from '../components/UI/TextFormField';
+import ButtonBase from '../components/UI/ButtonBase';
+import { Lock1, User } from 'iconsax-react-native';
+import ScaffoldAuth from '../components/UI/ScaffoldAuth';
+import { useNavigation } from '../Navigation';
+import LinkBase from '../components/UI/LinkBase';
+
+const hanldeSignin = async (
+ username: string,
+ password: string,
+ apiSetter: (accessToken: string) => void
+): Promise => {
+ try {
+ const apiAccess = await API.authenticate({ username, password });
+ apiSetter(apiAccess);
+ return translate('loggedIn');
+ } catch (error) {
+ if (error instanceof APIError) return translate(error.userMessage);
+ if (error instanceof Error) return error.message;
+ return translate('unknownError');
+ }
+};
+
+const SigninView = () => {
+ const dispatch = useDispatch();
+ const navigation = useNavigation();
+ const [formData, setFormData] = React.useState({
+ username: {
+ value: '',
+ error: null as string | null,
+ },
+ password: {
+ value: '',
+ error: null as string | null,
+ },
+ });
+ const validationSchemas = {
+ username: string()
+ .min(3, translate('usernameTooShort'))
+ .max(20, translate('usernameTooLong'))
+ .required('Username is required'),
+ password: string()
+ .min(4, translate('passwordTooShort'))
+ .max(100, translate('passwordTooLong'))
+ .required('Password is required'),
+ };
+ const toast = useToast();
+
+ const onSubmit= (username: string, password: string) => {
+ return hanldeSignin(username, password, (accessToken) =>
+ dispatch(setAccessToken(accessToken))
+ );
+ }
+
+ return (
+ {
+ let error: null | string = null;
+ validationSchemas.username
+ .validate(t)
+ .catch((e) => (error = e.message))
+ .finally(() => {
+ setFormData({ ...formData, username: { value: t, error } });
+ });
+ }}
+ isRequired
+ />,
+ {
+ let error: null | string = null;
+ validationSchemas.password
+ .validate(t)
+ .catch((e) => (error = e.message))
+ .finally(() => {
+ setFormData({ ...formData, password: { value: t, error } });
+ });
+ }}
+ isRequired
+ />,
+ console.log('Link clicked!')}>
+ {translate('forgottenPassword')}
+ ,
+ ]}
+ submitButton={
+ {
+ try {
+ const resp = await onSubmit(
+ formData.username.value,
+ formData.password.value
+ );
+ toast.show({ description: resp, colorScheme: 'secondary' });
+ } catch (e) {
+ toast.show({
+ description: e as string,
+ colorScheme: 'red',
+ avoidKeyboard: true,
+ });
+ }
+ }}
+ />
+ }
+ link={{
+ text: "Inscrivez-vous gratuitement",
+ description: "Vous n'avez pas de compte ? ",
+ onPress: () => navigation.navigate('Signup')
+ }}
+ />
+ );
+};
+
+export default SigninView;
\ No newline at end of file
diff --git a/front/views/SignupView.tsx b/front/views/SignupView.tsx
new file mode 100644
index 0000000..51f4231
--- /dev/null
+++ b/front/views/SignupView.tsx
@@ -0,0 +1,200 @@
+import React from 'react';
+import { useDispatch } from '../state/Store';
+import { translate } from '../i18n/i18n';
+import API, { APIError } from '../API';
+import { setAccessToken } from '../state/UserSlice';
+import { string } from 'yup';
+import { useToast } from 'native-base';
+import TextFormField from '../components/UI/TextFormField';
+import ButtonBase from '../components/UI/ButtonBase';
+import { Lock1, Sms, User } from 'iconsax-react-native';
+import ScaffoldAuth from '../components/UI/ScaffoldAuth';
+import { useNavigation } from '../Navigation';
+
+const handleSignup = async (
+ username: string,
+ password: string,
+ email: string,
+ apiSetter: (accessToken: string) => void
+): Promise => {
+ try {
+ const apiAccess = await API.createAccount({ username, password, email });
+ apiSetter(apiAccess);
+ return translate('loggedIn');
+ } catch (error) {
+ if (error instanceof APIError) return translate(error.userMessage);
+ if (error instanceof Error) return error.message;
+ return translate('unknownError');
+ }
+};
+
+const SignupView = () => {
+ const dispatch = useDispatch();
+ const navigation = useNavigation();
+ const [formData, setFormData] = React.useState({
+ username: {
+ value: '',
+ error: null as string | null,
+ },
+ password: {
+ value: '',
+ error: null as string | null,
+ },
+ repeatPassword: {
+ value: '',
+ error: null as string | null,
+ },
+ email: {
+ value: '',
+ error: null as string | null,
+ },
+ });
+ const validationSchemas = {
+ username: string()
+ .min(3, translate('usernameTooShort'))
+ .max(20, translate('usernameTooLong'))
+ .required('Username is required'),
+ email: string().email('Invalid email').required('Email is required'),
+ password: string()
+ .min(4, translate('passwordTooShort'))
+ .max(100, translate('passwordTooLong'))
+ // .matches(
+ // /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$-_%\^&\*])(?=.{8,})/,
+ // translate(
+ // "Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character"
+ // )
+ // )
+ .required('Password is required'),
+ };
+ const toast = useToast();
+
+ const onSubmit= (username: string, email: string, password: string) => {
+ return handleSignup(username, password, email, (accessToken) =>
+ dispatch(setAccessToken(accessToken))
+ )
+ }
+
+ return (
+ {
+ let error: null | string = null;
+ validationSchemas.username
+ .validate(t)
+ .catch((e) => (error = e.message))
+ .finally(() => {
+ setFormData({ ...formData, username: { value: t, error } });
+ });
+ }}
+ isRequired
+ />,
+ {
+ let error: null | string = null;
+ validationSchemas.email
+ .validate(t)
+ .catch((e) => (error = e.message))
+ .finally(() => {
+ setFormData({ ...formData, email: { value: t, error } });
+ });
+ }}
+ isRequired
+ />,
+ {
+ let error: null | string = null;
+ validationSchemas.password
+ .validate(t)
+ .catch((e) => (error = e.message))
+ .finally(() => {
+ setFormData({ ...formData, password: { value: t, error } });
+ });
+ }}
+ />,
+ {
+ let error: null | string = null;
+ validationSchemas.password
+ .validate(t)
+ .catch((e) => (error = e.message))
+ .finally(() => {
+ if (!error && t !== formData.password.value) {
+ error = translate('passwordsDontMatch');
+ }
+ setFormData({
+ ...formData,
+ repeatPassword: { value: t, error },
+ });
+ });
+ }}
+ />
+ ]}
+ submitButton={
+ {
+ try {
+ const resp = await onSubmit(
+ formData.username.value,
+ formData.password.value,
+ formData.email.value
+ );
+ toast.show({ description: resp, colorScheme: 'secondary' });
+ } catch (e) {
+ toast.show({
+ description: e as string,
+ colorScheme: 'red',
+ avoidKeyboard: true,
+ });
+ }
+ }}
+ />
+ }
+ link={{
+ text: "S'identifier",
+ description: "Vous avez déjà un compte ? ",
+ onPress: () => navigation.navigate('Login')
+ }}
+ />
+ );
+};
+
+export default SignupView;
\ No newline at end of file