Front: Dark mode (#157)
This commit is contained in:
+8
-7
@@ -1,5 +1,3 @@
|
||||
import { NativeBaseProvider } from "native-base";
|
||||
import Theme from './Theme';
|
||||
import React from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { Provider } from 'react-redux';
|
||||
@@ -9,6 +7,7 @@ import './i18n/i18n';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
import LanguageGate from "./i18n/LanguageGate";
|
||||
import ThemeProvider, { ColorSchemeProvider } from './Theme';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
@@ -21,11 +20,13 @@ export default function App() {
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<NativeBaseProvider theme={Theme}>
|
||||
<LanguageGate>
|
||||
<Router/>
|
||||
</LanguageGate>
|
||||
</NativeBaseProvider>
|
||||
<ThemeProvider>
|
||||
<ColorSchemeProvider>
|
||||
<LanguageGate>
|
||||
<Router/>
|
||||
</LanguageGate>
|
||||
</ColorSchemeProvider>
|
||||
</ThemeProvider>
|
||||
</QueryClientProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
|
||||
+18
-12
@@ -1,6 +1,6 @@
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
|
||||
import { RootState, useSelector } from './state/Store';
|
||||
import { translate } from './i18n/i18n';
|
||||
import SongLobbyView from './views/SongLobbyView';
|
||||
@@ -15,6 +15,7 @@ import ScoreView from './views/ScoreView';
|
||||
import { Center } from 'native-base';
|
||||
import LoadingComponent from './components/Loading';
|
||||
import ProfileView from './views/ProfileView';
|
||||
import useColorScheme from './hooks/colorScheme';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
@@ -38,20 +39,25 @@ export const Router = () => {
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
});
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
if (userProfile.isLoading && !userProfile.data) {
|
||||
return <Center style={{ flexGrow: 1 }}>
|
||||
<LoadingComponent/>
|
||||
</Center>
|
||||
}
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<NavigationContainer theme={colorScheme == 'light'
|
||||
? DefaultTheme
|
||||
: DarkTheme
|
||||
}>
|
||||
<Stack.Navigator>
|
||||
{ userProfile.isSuccess && accessToken
|
||||
? protectedRoutes
|
||||
: publicRoutes
|
||||
}
|
||||
{ userProfile.isLoading && !userProfile.data ?
|
||||
<Stack.Screen name="Loading" component={() =>
|
||||
<Center style={{ flexGrow: 1 }}>
|
||||
<LoadingComponent/>
|
||||
</Center>
|
||||
}/>
|
||||
: userProfile.isSuccess && accessToken
|
||||
? protectedRoutes
|
||||
: publicRoutes
|
||||
}
|
||||
</Stack.Navigator>
|
||||
</NavigationContainer>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
+88
-127
@@ -1,128 +1,89 @@
|
||||
import { extendTheme } from 'native-base';
|
||||
/**
|
||||
* Color theme to use thoughout the application
|
||||
* Using the Material Color guidelines
|
||||
*/
|
||||
const Theme = extendTheme({
|
||||
colors: {
|
||||
primary:
|
||||
{
|
||||
50: '#e6faea',
|
||||
100: '#c8e7d0',
|
||||
200: '#a7d6b5',
|
||||
300: '#86c498',
|
||||
400: '#65b47c',
|
||||
500: '#4b9a62',
|
||||
600: '#3a784b',
|
||||
700: '#275635',
|
||||
800: '#14341f',
|
||||
900: '#001405',
|
||||
},
|
||||
background:
|
||||
{
|
||||
50: '#f2f2f2',
|
||||
100: '#d9d9d9',
|
||||
200: '#bfbfbf',
|
||||
300: '#a6a6a6',
|
||||
400: '#8c8c8c',
|
||||
500: '#737373',
|
||||
600: '#595959',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#0d0d0d',
|
||||
},
|
||||
surface:
|
||||
{
|
||||
50: '#f2f2f2',
|
||||
100: '#d9d9d9',
|
||||
200: '#bfbfbf',
|
||||
300: '#a6a6a6',
|
||||
400: '#8c8c8c',
|
||||
500: '#737373',
|
||||
600: '#595959',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#0d0d0d',
|
||||
},
|
||||
secondary:
|
||||
{
|
||||
50: '#d8ffff',
|
||||
100: '#acffff',
|
||||
200: '#7dffff',
|
||||
300: '#4dffff',
|
||||
400: '#28ffff',
|
||||
500: '#18e5e6',
|
||||
600: '#00b2b3',
|
||||
700: '#007f80',
|
||||
800: '#004d4e',
|
||||
900: '#001b1d',
|
||||
},
|
||||
error:
|
||||
{
|
||||
50: '#ffe2e9',
|
||||
100: '#ffb1bf',
|
||||
200: '#ff7f97',
|
||||
300: '#ff4d6d',
|
||||
400: '#fe1d43',
|
||||
500: '#e5062b',
|
||||
600: '#b30020',
|
||||
700: '#810017',
|
||||
800: '#4f000c',
|
||||
900: '#200004',
|
||||
},
|
||||
text:
|
||||
{
|
||||
50: '#f2f2f2',
|
||||
100: '#d9d9d9',
|
||||
200: '#bfbfbf',
|
||||
300: '#a6a6a6',
|
||||
400: '#8c8c8c',
|
||||
500: '#737373',
|
||||
600: '#595959',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#0d0d0d',
|
||||
},
|
||||
onSurface:
|
||||
{
|
||||
50: '#f2f2f2',
|
||||
100: '#d9d9d9',
|
||||
200: '#bfbfbf',
|
||||
300: '#a6a6a6',
|
||||
400: '#8c8c8c',
|
||||
500: '#737373',
|
||||
600: '#595959',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#0d0d0d',
|
||||
},
|
||||
placeholder:
|
||||
{
|
||||
50: '#fbf0f2',
|
||||
100: '#dcd8d9',
|
||||
200: '#bfbfbf',
|
||||
300: '#a6a6a6',
|
||||
400: '#8c8c8c',
|
||||
500: '#737373',
|
||||
600: '#595959',
|
||||
700: '#404040',
|
||||
800: '#282626',
|
||||
900: '#150a0d',
|
||||
},
|
||||
notification:
|
||||
{
|
||||
50: '#ffe1e1',
|
||||
100: '#ffb1b1',
|
||||
200: '#ff7f7f',
|
||||
300: '#ff4c4c',
|
||||
400: '#ff1a1a',
|
||||
500: '#e60000',
|
||||
600: '#b40000',
|
||||
700: '#810000',
|
||||
800: '#500000',
|
||||
900: '#210000',
|
||||
}
|
||||
}
|
||||
});
|
||||
import { NativeBaseProvider, extendTheme, useColorMode, useTheme } from 'native-base';
|
||||
import useColorScheme from './hooks/colorScheme';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default Theme;
|
||||
const ThemeProvider = ({ children }: { children: JSX.Element }) => {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
return <NativeBaseProvider theme={extendTheme({
|
||||
config: {
|
||||
useSystemColorMode: false,
|
||||
initialColorMode: colorScheme
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#e6faea',
|
||||
100: '#c8e7d0',
|
||||
200: '#a7d6b5',
|
||||
300: '#86c498',
|
||||
400: '#65b47c',
|
||||
500: '#4b9a62',
|
||||
600: '#3a784b',
|
||||
700: '#275635',
|
||||
800: '#14341f',
|
||||
900: '#001405',
|
||||
},
|
||||
secondary: {
|
||||
50: '#d8ffff',
|
||||
100: '#acffff',
|
||||
200: '#7dffff',
|
||||
300: '#4dffff',
|
||||
400: '#28ffff',
|
||||
500: '#18e5e6',
|
||||
600: '#00b2b3',
|
||||
700: '#007f80',
|
||||
800: '#004d4e',
|
||||
900: '#001b1d',
|
||||
},
|
||||
error: {
|
||||
50: '#ffe2e9',
|
||||
100: '#ffb1bf',
|
||||
200: '#ff7f97',
|
||||
300: '#ff4d6d',
|
||||
400: '#fe1d43',
|
||||
500: '#e5062b',
|
||||
600: '#b30020',
|
||||
700: '#810017',
|
||||
800: '#4f000c',
|
||||
900: '#200004',
|
||||
},
|
||||
notification: {
|
||||
50: '#ffe1e1',
|
||||
100: '#ffb1b1',
|
||||
200: '#ff7f7f',
|
||||
300: '#ff4c4c',
|
||||
400: '#ff1a1a',
|
||||
500: '#e60000',
|
||||
600: '#b40000',
|
||||
700: '#810000',
|
||||
800: '#500000',
|
||||
900: '#210000',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Button: {
|
||||
variants: {
|
||||
solid: () => ({
|
||||
rounded: 'full',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})}>
|
||||
{ children }
|
||||
</NativeBaseProvider>;
|
||||
}
|
||||
|
||||
const ColorSchemeProvider = (props: { children: any }) => {
|
||||
|
||||
const colorScheme = useColorScheme();
|
||||
const colorMode = useColorMode();
|
||||
|
||||
useEffect(() => {
|
||||
colorMode.setColorMode(colorScheme);
|
||||
}, [colorScheme]);
|
||||
return props.children;
|
||||
}
|
||||
|
||||
export default ThemeProvider;
|
||||
export { ColorSchemeProvider };
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useTheme, Box } from 'native-base';
|
||||
import { useTheme, Box, Pressable } from 'native-base';
|
||||
import React from 'react';
|
||||
import { useColorScheme } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { SettingsState } from '../state/SettingsSlice';
|
||||
import { RootState } from '../state/Store';
|
||||
|
||||
export const CardBorderRadius = 10;
|
||||
|
||||
@@ -9,11 +13,24 @@ const cardBorder = (theme: ReturnType<typeof useTheme>) => ({
|
||||
borderWidth: 1
|
||||
})
|
||||
|
||||
const Card = (props: any) => {
|
||||
const Card = (props: Parameters<typeof Box>[0] & { onPress: () => void }) => {
|
||||
const theme = useTheme();
|
||||
return <Box {...props} style={{ ...props.style, ...cardBorder(theme) }}>
|
||||
{ props.children }
|
||||
</Box>
|
||||
const colorScheme: SettingsState['colorScheme'] = useSelector((state: RootState) => state.settings.settings.colorScheme);
|
||||
const systemColorMode = useColorScheme();
|
||||
|
||||
return <Pressable onPress={props.onPress}>
|
||||
{({ isHovered, isPressed }) => (
|
||||
<Box {...props} style={{ ...(props.style ?? {}), ...cardBorder(theme) }}
|
||||
bg={(colorScheme == 'system' ? systemColorMode : colorScheme) == 'dark'
|
||||
? (isHovered || isPressed) ? 'gray.800' : undefined
|
||||
: (isHovered || isPressed) ? 'coolGray.200' : undefined
|
||||
}
|
||||
>
|
||||
{ props.children }
|
||||
</Box>
|
||||
)}
|
||||
</Pressable>
|
||||
|
||||
}
|
||||
|
||||
export default Card;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigation } from "@react-navigation/core";
|
||||
import { HStack, VStack, Text, Progress, Pressable } from "native-base";
|
||||
import { HStack, VStack, Text, Progress } from "native-base";
|
||||
import { translate } from "../i18n/i18n";
|
||||
import Card from './Card';
|
||||
|
||||
@@ -14,9 +14,8 @@ type CompetenciesTableProps = {
|
||||
|
||||
const CompetenciesTable = (props: CompetenciesTableProps) => {
|
||||
const navigation = useNavigation();
|
||||
return <Pressable onPress={() => navigation.navigate('User')}>
|
||||
{({ isHovered, isFocused }) => (
|
||||
<Card padding={5} bg={(isHovered || isFocused) ? 'coolGray.200' : undefined }>
|
||||
return (
|
||||
<Card padding={5} onPress={() => navigation.navigate('User')} shadow={3}>
|
||||
<HStack space={5} flex={1}>
|
||||
<VStack space={5}>
|
||||
{ Object.keys(props).map((competencyName) => (
|
||||
@@ -30,8 +29,7 @@ const CompetenciesTable = (props: CompetenciesTableProps) => {
|
||||
</VStack>
|
||||
</HStack>
|
||||
</Card>
|
||||
)}
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
export default CompetenciesTable
|
||||
@@ -15,29 +15,25 @@ const ProgressBar = ({ xp }: { xp: number}) => {
|
||||
const flexDirection = useBreakpointValue({ base: 'column', xl: "row"});
|
||||
|
||||
return (
|
||||
<Pressable onPress={() => nav.navigate('User')}>
|
||||
{({ isHovered, isFocused }) => (
|
||||
<Card w="90%" maxW='500' style={{flexDirection}}
|
||||
bg={(isHovered || isFocused) ? 'coolGray.200' : undefined }>
|
||||
<Box w="20%" paddingRight={2} paddingLeft={2} paddingY={2}>
|
||||
<Image borderRadius={100} source={{
|
||||
uri: "https://wallpaperaccess.com/full/317501.jpg" // TODO : put the actual profile pic
|
||||
}} alt="Profile picture" size="sm"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box w='80%' paddingY={4}>
|
||||
<VStack alignItems={'center'}>
|
||||
<Text>{`${translate('level')} ${level}`}</Text>
|
||||
<Box w="100%">
|
||||
<Progress value={progessValue} mx="4" />
|
||||
</Box>
|
||||
<Text>{xp} / {nextLevelThreshold} {translate('levelProgress')}</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
</Card>
|
||||
)}
|
||||
</Pressable>
|
||||
<Card w="90%" maxW='500' style={{flexDirection}}
|
||||
onPress={() => nav.navigate('User')}
|
||||
>
|
||||
<Box w="20%" paddingRight={2} paddingLeft={2} paddingY={2}>
|
||||
<Image borderRadius={100} source={{
|
||||
uri: "https://wallpaperaccess.com/full/317501.jpg" // TODO : put the actual profile pic
|
||||
}} alt="Profile picture" size="sm"
|
||||
/>
|
||||
</Box>
|
||||
<Box w='80%' paddingY={4}>
|
||||
<VStack alignItems={'center'}>
|
||||
<Text>{`${translate('level')} ${level}`}</Text>
|
||||
<Box w="100%">
|
||||
<Progress value={progessValue} mx="4" />
|
||||
</Box>
|
||||
<Text>{xp} / {nextLevelThreshold} {translate('levelProgress')}</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,13 +12,12 @@ type SongCardProps = {
|
||||
const SongCard = (props: SongCardProps) => {
|
||||
const { albumCover, songTitle, artistName, songId } = props;
|
||||
const navigation = useNavigation();
|
||||
return <Pressable onPress={() => navigation.navigate('Song', { songId })}>
|
||||
{({ isHovered, isFocused }) => (
|
||||
return (
|
||||
<Card
|
||||
shadow={3}
|
||||
flexDirection='column'
|
||||
alignContent='space-around'
|
||||
bg={(isHovered || isFocused) ? 'coolGray.200' : 'background.50' }
|
||||
onPress={() => navigation.navigate('Song', { songId })}
|
||||
>
|
||||
<Image
|
||||
style={{ zIndex: 0, aspectRatio: 1, margin: 5, borderRadius: CardBorderRadius}}
|
||||
@@ -34,8 +33,7 @@ const SongCard = (props: SongCardProps) => {
|
||||
</Text>
|
||||
</VStack>
|
||||
</Card>
|
||||
)}
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
export default SongCard;
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Button, Text } from "native-base"
|
||||
import Translate from "./Translate";
|
||||
import { RequireExactlyOne } from 'type-fest';
|
||||
|
||||
type TextButtonProps = Parameters<typeof Button>[0] & RequireExactlyOne<{
|
||||
label: string;
|
||||
translate: Parameters<typeof Translate>[0];
|
||||
}>
|
||||
|
||||
const TextButton = (props: TextButtonProps) => {
|
||||
// accepts undefined variant, as it is the default variant
|
||||
const textColor = !props.variant || props.variant == 'solid'
|
||||
? 'light.50'
|
||||
: undefined;
|
||||
|
||||
return <Button {...props}>
|
||||
{ props.label !== undefined
|
||||
? <Text color={textColor}>{props.label}</Text>
|
||||
: <Translate color={textColor} {...props.translate} />
|
||||
}
|
||||
</Button>
|
||||
}
|
||||
|
||||
export default TextButton
|
||||
@@ -6,16 +6,17 @@ import { RootState, useSelector } from "../state/Store";
|
||||
type TranslateProps = {
|
||||
translationKey: keyof typeof en;
|
||||
format?: (translated: string) => string;
|
||||
}
|
||||
} & Parameters<typeof Text>[0];
|
||||
/**
|
||||
* Translation component
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
const Translate = ({ translationKey, format }: TranslateProps) => {
|
||||
const Translate = ({ translationKey, format, ...props }: TranslateProps) => {
|
||||
const selectedLanguage = useSelector((state: RootState) => state.language.value);
|
||||
const translated = translate(translationKey, selectedLanguage);
|
||||
return <Text>{format ? format(translated) : translated}</Text>;
|
||||
|
||||
return <Text {...props}>{format ? format(translated) : translated}</Text>;
|
||||
}
|
||||
|
||||
export default Translate;
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
Stack,
|
||||
WarningOutlineIcon,
|
||||
Box,
|
||||
Button,
|
||||
useToast,
|
||||
} from "native-base";
|
||||
import TextButton from "../TextButton";
|
||||
|
||||
interface SigninFormProps {
|
||||
onSubmit: (username: string, password: string) => Promise<string>;
|
||||
@@ -97,7 +97,7 @@ const LoginForm = ({ onSubmit }: SigninFormProps) => {
|
||||
>
|
||||
{formData.password.error}
|
||||
</FormControl.ErrorMessage>
|
||||
<Button
|
||||
<TextButton translate={{ translationKey: 'login' }}
|
||||
style={{ marginTop: 10 }}
|
||||
isLoading={submittingForm}
|
||||
isDisabled={
|
||||
@@ -120,9 +120,7 @@ const LoginForm = ({ onSubmit }: SigninFormProps) => {
|
||||
setSubmittingForm(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Translate translationKey='login'/>
|
||||
</Button>
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
Stack,
|
||||
WarningOutlineIcon,
|
||||
Box,
|
||||
Button,
|
||||
useToast,
|
||||
} from "native-base";
|
||||
import TextButton from "../TextButton";
|
||||
|
||||
interface SignupFormProps {
|
||||
onSubmit: (
|
||||
@@ -172,7 +172,7 @@ const LoginForm = ({ onSubmit }: SignupFormProps) => {
|
||||
>
|
||||
{formData.repeatPassword.error}
|
||||
</FormControl.ErrorMessage>
|
||||
<Button
|
||||
<TextButton translate={{ translationKey: 'signUp' }}
|
||||
style={{ marginTop: 10 }}
|
||||
isLoading={submittingForm}
|
||||
isDisabled={
|
||||
@@ -200,9 +200,7 @@ const LoginForm = ({ onSubmit }: SignupFormProps) => {
|
||||
setSubmittingForm(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Translate translationKey='signUp'/>
|
||||
</Button>
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Appearance } from "react-native";
|
||||
import { useSelector } from "react-redux";
|
||||
import { SettingsState } from "../state/SettingsSlice";
|
||||
import { RootState } from "../state/Store";
|
||||
|
||||
const useColorScheme = (): 'light' | 'dark' => {
|
||||
const colorScheme: SettingsState['colorScheme'] = useSelector((state: RootState) => state.settings.settings.colorScheme);
|
||||
const systemColorScheme = Appearance.getColorScheme();
|
||||
|
||||
if (colorScheme == 'system') {
|
||||
return systemColorScheme ?? 'light';
|
||||
}
|
||||
return colorScheme;
|
||||
}
|
||||
|
||||
export default useColorScheme;
|
||||
+3
-2
@@ -47,9 +47,9 @@
|
||||
"react-i18next": "^11.18.3",
|
||||
"react-native": "0.70.5",
|
||||
"react-native-paper": "^4.12.5",
|
||||
"react-native-reanimated": "~2.8.0",
|
||||
"react-native-safe-area-context": "4.4.1",
|
||||
"react-native-screens": "~3.18.0",
|
||||
"react-native-reanimated": "~2.8.0",
|
||||
"react-native-super-grid": "^4.6.1",
|
||||
"react-native-svg": "13.4.0",
|
||||
"react-native-testing-library": "^6.0.0",
|
||||
@@ -58,10 +58,12 @@
|
||||
"react-timer-hook": "^3.0.5",
|
||||
"redux-persist": "^6.0.0",
|
||||
"soundfont-player": "^0.12.0",
|
||||
"type-fest": "^3.6.0",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.9",
|
||||
"@expo/webpack-config": "^0.17.4",
|
||||
"@storybook/addon-actions": "^6.5.15",
|
||||
"@storybook/addon-essentials": "^6.5.15",
|
||||
"@storybook/addon-interactions": "^6.5.15",
|
||||
@@ -70,7 +72,6 @@
|
||||
"@storybook/manager-webpack4": "^6.5.15",
|
||||
"@storybook/react": "^6.5.15",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@expo/webpack-config": "^0.17.4",
|
||||
"@testing-library/react-native": "^11.0.0",
|
||||
"@types/node": "^18.11.8",
|
||||
"@types/react": "^18.0.18",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import userReducer from '../state/UserSlice';
|
||||
import settingsReduder from '../state/SettingsSlice';
|
||||
import settingsReduder from './SettingsSlice';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import languageReducer from './LanguageSlice';
|
||||
import { TypedUseSelectorHook, useDispatch as reduxDispatch, useSelector as reduxSelector } from 'react-redux'
|
||||
|
||||
@@ -6,6 +6,7 @@ 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";
|
||||
|
||||
const hanldeSignin = async (username: string, password: string, apiSetter: (accessToken: string) => void): Promise<string> => {
|
||||
try {
|
||||
@@ -39,11 +40,11 @@ const AuthenticationView = () => {
|
||||
: <SignupForm onSubmit={(username, password, email) => handleSignup(username, password, email, (accessToken) => dispatch(setAccessToken(accessToken)))} />
|
||||
}
|
||||
{ mode ==="signin" && <Button variant="outline" marginTop={5} colorScheme="error" >{translate("forgottenPassword")}</Button> }
|
||||
<Button variant='outline' marginTop={5} colorScheme='primary' onPress={() => setMode(mode === "signin" ? "signup" : "signin")}>
|
||||
<Text>
|
||||
<Translate translationKey={mode === "signin" ? "signUp" : "signIn"}/>
|
||||
</Text>
|
||||
</Button>
|
||||
<TextButton
|
||||
translate={{ translationKey: mode === "signin" ? "signUp" : "signIn" }}
|
||||
variant='outline' marginTop={5} colorScheme='primary'
|
||||
onPress={() => setMode(mode === "signin" ? "signup" : "signin")}
|
||||
/>
|
||||
</Center>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,15 +2,15 @@ import React from "react";
|
||||
import { useQueries, useQuery } from "react-query";
|
||||
import API from "../API";
|
||||
import LoadingComponent from "../components/Loading";
|
||||
import { Box, ScrollView, Flex, useBreakpointValue, Text, VStack, Button, useTheme, Heading, Progress } from 'native-base';
|
||||
import { Box, ScrollView, Flex, useBreakpointValue, Text, VStack, Button, Heading } from 'native-base';
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import SongCardGrid from '../components/SongCardGrid';
|
||||
import CompetenciesTable from '../components/CompetenciesTable'
|
||||
import ProgressBar from "../components/ProgressBar";
|
||||
import Translate from "../components/Translate";
|
||||
import TextButton from "../components/TextButton";
|
||||
|
||||
const HomeView = () => {
|
||||
const theme = useTheme();
|
||||
const navigation = useNavigation();
|
||||
const screenSize = useBreakpointValue({ base: 'small', md: "big"});
|
||||
const flexDirection = useBreakpointValue({ base: 'column', xl: "row"});
|
||||
@@ -82,7 +82,11 @@ const HomeView = () => {
|
||||
|
||||
<Box flex="2" padding={5}>
|
||||
<Box style={{ flexDirection: 'row', justifyContent:'center' }}>
|
||||
<Button backgroundColor={theme.colors.secondary[600]} rounded={"full"} size="sm" onPress={() => navigation.navigate('Search')} ><Translate translationKey='search'/></Button>
|
||||
<TextButton
|
||||
translate={{ translationKey: 'search' }}
|
||||
colorScheme='secondary' size="sm"
|
||||
onPress={() => navigation.navigate('Search')}
|
||||
/>
|
||||
</Box>
|
||||
<SongCardGrid
|
||||
maxItemPerRow={2}
|
||||
@@ -99,9 +103,9 @@ const HomeView = () => {
|
||||
</Box>
|
||||
</Box>
|
||||
<Box style={{ flexDirection: 'row', justifyContent:'center' }}>
|
||||
<Button backgroundColor={theme.colors.primary[600]} rounded={"full"} size="sm" onPress={() => navigation.navigate('Settings')} >
|
||||
<Translate translationKey='settingsBtn'/>
|
||||
</Button>
|
||||
<TextButton translate={{ translationKey: 'settingsBtn' }}
|
||||
size="sm" onPress={() => navigation.navigate('Settings')}
|
||||
/>
|
||||
</Box>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Dimensions, View } from 'react-native';
|
||||
import { Box, Image, Heading, HStack, Card, Button, Spacer, Text } from 'native-base';
|
||||
import Translate from '../components/Translate';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import TextButton from '../components/TextButton';
|
||||
|
||||
const UserMedals = () => {
|
||||
return (
|
||||
@@ -87,9 +88,11 @@ const ProfileView = () => {
|
||||
<UserMedals/>
|
||||
<PlayerStats/>
|
||||
<Box w="10%" paddingY={10} paddingLeft={5} paddingRight={50} zIndex={1}>
|
||||
<Button onPress={() => navigation.navigate('Settings')} style={{margin: 10}}>
|
||||
<Translate translationKey='settingsBtn'/>
|
||||
</Button>
|
||||
<TextButton
|
||||
onPress={() => navigation.navigate('Settings')}
|
||||
style={{margin: 10}}
|
||||
translate={{ translationKey: 'settingsBtn' }}
|
||||
/>
|
||||
</Box>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import Translate from "../components/Translate";
|
||||
import SongCardGrid from "../components/SongCardGrid";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { CardBorderRadius } from "../components/Card";
|
||||
import TextButton from "../components/TextButton";
|
||||
|
||||
const ScoreView = (/*{ songId }, { songId: number }*/) => {
|
||||
const theme = useTheme();
|
||||
@@ -57,12 +58,14 @@ const ScoreView = (/*{ songId }, { songId: number }*/) => {
|
||||
}))}
|
||||
/>
|
||||
<Row space={3} style={{ width: '100%', justifyContent: 'center' }}>
|
||||
<Button backgroundColor='gray.300' onPress={() => navigation.navigate('Home')}>
|
||||
<Translate translationKey='backBtn'/>
|
||||
</Button>
|
||||
<Button onPress={() => navigation.navigate('Song', { songId: 1 })}>
|
||||
<Translate translationKey='playAgain'/>
|
||||
</Button>
|
||||
<TextButton backgroundColor='gray.300'
|
||||
translate={{ translationKey: 'backBtn' }}
|
||||
onPress={() => navigation.navigate('Home')}
|
||||
/>
|
||||
<TextButton
|
||||
onPress={() => navigation.navigate('Song', { songId: 1 })}
|
||||
translate={{ translationKey: 'playAgain' }}
|
||||
/>
|
||||
</Row>
|
||||
</Column>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Box } from "native-base";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import SearchBarSuggestions from "../components/SearchBarSuggestions";
|
||||
|
||||
@@ -8,6 +8,7 @@ import { RootState, useSelector } from '../state/Store';
|
||||
import { useLanguage } from "../state/LanguageSlice";
|
||||
import { SettingsState, updateSettings } from '../state/SettingsSlice';
|
||||
import { AvailableLanguages, translate, Translate } from "../i18n/i18n";
|
||||
import TextButton from '../components/TextButton';
|
||||
|
||||
const SettingsStack = createNativeStackNavigator();
|
||||
|
||||
@@ -56,10 +57,10 @@ const PreferencesView = ({navigation}) => {
|
||||
<Heading style={{ textAlign: "center" }}>
|
||||
<Translate translationKey='prefBtn'/>
|
||||
</Heading>
|
||||
<Button onPress={() => navigation.navigate('Main')} style={{ margin: 10}}>
|
||||
<Translate translationKey='backBtn'/>
|
||||
</Button>
|
||||
|
||||
<TextButton
|
||||
onPress={() => navigation.navigate('Main')} style={{ margin: 10 }}
|
||||
translate={{ translationKey: 'backBtn' }}
|
||||
/>
|
||||
<View style={{margin: 20, maxHeight: 100, maxWidth: 500, width: '80%'}}>
|
||||
<Select selectedValue={settings.colorScheme}
|
||||
placeholder={'Theme'}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Translate, translate } from "../i18n/i18n";
|
||||
import formatDuration from "format-duration";
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import API from "../API";
|
||||
import TextButton from "../components/TextButton";
|
||||
|
||||
interface SongLobbyProps {
|
||||
// The unique identifier to find a song
|
||||
@@ -45,9 +46,10 @@ const SongLobbyView = () => {
|
||||
format={(level) => `${level} - ${ chaptersQuery.data!.reduce((a, b) => a + b.difficulty, 0) / chaptersQuery.data!.length }`}
|
||||
/>
|
||||
</Text>
|
||||
<Button width='auto' onPress={() => navigation.navigate('Play', { songId: songQuery.data?.id })} rightIcon={<Icon as={Ionicons} name="play-outline"/>}>
|
||||
<Translate translationKey='playBtn'/>
|
||||
</Button>
|
||||
<TextButton translate={{ translationKey: 'playBtn' }} width='auto'
|
||||
onPress={() => navigation.navigate('Play', { songId: songQuery.data?.id })}
|
||||
rightIcon={<Icon as={Ionicons} name="play-outline"/>}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -67,13 +69,12 @@ const SongLobbyView = () => {
|
||||
</Box>
|
||||
{/* <Text style={{ paddingBottom: 10 }}>{songQuery.data!.description}</Text> */}
|
||||
<Box flexDirection='row'>
|
||||
<Button
|
||||
<TextButton
|
||||
translate={{ translationKey: 'chapters' }}
|
||||
variant='ghost'
|
||||
onPress={() => setChaptersOpen(!chaptersOpen)}
|
||||
endIcon={<Icon as={Ionicons} name={chaptersOpen ? "chevron-up-outline" : "chevron-down-outline"}/>}
|
||||
>
|
||||
<Translate translationKey='chapters'/>
|
||||
</Button>
|
||||
/>
|
||||
</Box>
|
||||
<PresenceTransition visible={chaptersOpen} initial={{ opacity: 0 }}>
|
||||
{ chaptersQuery.isLoading && <LoadingComponent/>}
|
||||
|
||||
@@ -16981,6 +16981,11 @@ type-fest@^0.8.1:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-fest@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.6.0.tgz#827c36c0e7fcff0cb2d55d091a5c4cf586432b8a"
|
||||
integrity sha512-RqTRtKTzvPpNdDUp1dVkKQRunlPITk4mXeqFlAZoJsS+fLRn8AdPK0TcQDumGayhU7fjlBfiBjsq3pe3rIfXZQ==
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
||||
Reference in New Issue
Block a user