Front: Dark mode (#157)

This commit is contained in:
Arthur Jamet
2023-02-19 10:02:57 +00:00
committed by GitHub
parent 144d9bec5f
commit f5770cd104
22 changed files with 266 additions and 233 deletions
+8 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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 };
+22 -5
View File
@@ -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;
+4 -6
View File
@@ -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
+19 -23
View File
@@ -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>
);
}
+3 -5
View File
@@ -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;
+24
View File
@@ -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
+4 -3
View File
@@ -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;
+3 -5
View File
@@ -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>
+3 -5
View File
@@ -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>
+16
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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 -5
View File
@@ -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>
);
};
+10 -6
View File
@@ -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>
+6 -3
View File
@@ -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>
);
+9 -6
View File
@@ -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 -1
View File
@@ -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";
+5 -4
View File
@@ -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'}
+8 -7
View File
@@ -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/>}
+5
View File
@@ -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"