Front: Pretty
This commit is contained in:
@@ -29,9 +29,7 @@ const ThemeProvider = ({ children }: { children: JSX.Element }) => {
|
||||
900: 'rgba(16,16,20,0.9)',
|
||||
};
|
||||
|
||||
const glassmorphism = colorScheme === 'light'
|
||||
? lightGlassmorphism
|
||||
: darkGlassmorphism
|
||||
const glassmorphism = colorScheme === 'light' ? lightGlassmorphism : darkGlassmorphism;
|
||||
|
||||
return (
|
||||
<NativeBaseProvider
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Select,
|
||||
useBreakpointValue,
|
||||
useTheme,
|
||||
Wrap,
|
||||
} from 'native-base';
|
||||
import { Box, Flex, Select, useBreakpointValue, useTheme, Wrap } from 'native-base';
|
||||
import { LineChart } from 'react-native-chart-kit';
|
||||
import { useState } from 'react';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
@@ -16,43 +9,46 @@ import { LoadingView } from './Loading';
|
||||
const formatScoreDate = (playDate: Date): string => {
|
||||
// const formattedDate = `${pad(playDate.getDay())}/${pad(playDate.getMonth())}`;
|
||||
// const formattedTime = `${pad(playDate.getHours())}:${pad(playDate.getMinutes())}`;
|
||||
|
||||
|
||||
// console.log(playDate.toDateString());
|
||||
// console.log(`${playDate.getDate()}/${playDate.getMonth() + 1}`);
|
||||
return `${playDate.getDate()}`;
|
||||
};
|
||||
|
||||
type GraphProps = {
|
||||
songId: number,
|
||||
since: Date
|
||||
songId: number;
|
||||
since: Date;
|
||||
};
|
||||
|
||||
const calculateDailyAverages = (scores: { playDate: Date, score: number }[]): { playDate: Date, score: number }[] => {
|
||||
const dailyScores: { [key: string]: number[] } = {};
|
||||
const calculateDailyAverages = (
|
||||
scores: { playDate: Date; score: number }[]
|
||||
): { playDate: Date; score: number }[] => {
|
||||
const dailyScores: { [key: string]: number[] } = {};
|
||||
|
||||
// Regroupez les scores par date
|
||||
scores.forEach((score) => {
|
||||
const date = score.playDate.toISOString().split('T')[0] as string; // Obtenez la date au format 'YYYY-MM-DD'
|
||||
if (!dailyScores[date]) {
|
||||
dailyScores[date] = [];
|
||||
}
|
||||
dailyScores[date]!.push(score.score);
|
||||
});
|
||||
// Regroupez les scores par date
|
||||
scores.forEach((score) => {
|
||||
const date = score.playDate.toISOString().split('T')[0] as string; // Obtenez la date au format 'YYYY-MM-DD'
|
||||
if (!dailyScores[date]) {
|
||||
dailyScores[date] = [];
|
||||
}
|
||||
dailyScores[date]!.push(score.score);
|
||||
});
|
||||
|
||||
// Calculez la moyenne des scores par jour et créez un tableau d'objets avec le format final
|
||||
const dailyAverages: { playDate: Date, score: number }[] = [];
|
||||
Object.keys(dailyScores).forEach((date) => {
|
||||
const oneDayScore = dailyScores[date];
|
||||
// Calculez la moyenne des scores par jour et créez un tableau d'objets avec le format final
|
||||
const dailyAverages: { playDate: Date; score: number }[] = [];
|
||||
Object.keys(dailyScores).forEach((date) => {
|
||||
const oneDayScore = dailyScores[date];
|
||||
if (oneDayScore) {
|
||||
const average = oneDayScore.reduce((total, score) => total + score, 0) / oneDayScore.length;
|
||||
const average =
|
||||
oneDayScore.reduce((total, score) => total + score, 0) / oneDayScore.length;
|
||||
dailyAverages.push({ playDate: new Date(date), score: average });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return dailyAverages;
|
||||
return dailyAverages;
|
||||
};
|
||||
|
||||
const Graph = ({songId, since}: GraphProps) => {
|
||||
const Graph = ({ songId, since }: GraphProps) => {
|
||||
const isSmall = useBreakpointValue({ base: true, md: false });
|
||||
const theme = useTheme();
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
@@ -63,8 +59,7 @@ const Graph = ({songId, since}: GraphProps) => {
|
||||
}
|
||||
|
||||
const dailyScore = calculateDailyAverages(scoresQuery.data.history);
|
||||
const scoresToSort = dailyScore
|
||||
.filter((item: { playDate: Date; }) => item.playDate >= since);
|
||||
const scoresToSort = dailyScore.filter((item: { playDate: Date }) => item.playDate >= since);
|
||||
|
||||
const scores = scoresToSort.sort((a, b) => {
|
||||
if (a.playDate < b.playDate) {
|
||||
@@ -81,10 +76,12 @@ const Graph = ({songId, since}: GraphProps) => {
|
||||
style={{ width: '100%', marginTop: 20 }}
|
||||
onLayout={(event) => setContainerWidth(event.nativeEvent.layout.width)}
|
||||
>
|
||||
{scores && scores.length > 0 &&
|
||||
{scores && scores.length > 0 && (
|
||||
<LineChart
|
||||
data={{
|
||||
labels: isSmall ? [] : scores.map(({ playDate }) => formatScoreDate(playDate)),
|
||||
labels: isSmall
|
||||
? []
|
||||
: scores.map(({ playDate }) => formatScoreDate(playDate)),
|
||||
datasets: [
|
||||
{
|
||||
data: scores.map(({ score }) => score),
|
||||
@@ -117,10 +114,10 @@ const Graph = ({songId, since}: GraphProps) => {
|
||||
}}
|
||||
bezier
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const ScoreGraph = () => {
|
||||
const layout = useWindowDimensions();
|
||||
@@ -158,25 +155,25 @@ const ScoreGraph = () => {
|
||||
setSelectedSinceDate(threeDaysAgo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex flex={1}>
|
||||
<Wrap style={{gap: 16, flexDirection: 'row', justifyContent: 'flex-end'}}>
|
||||
<Wrap style={{ gap: 16, flexDirection: 'row', justifyContent: 'flex-end' }}>
|
||||
<Box>
|
||||
<Select
|
||||
onValueChange={(selectedValue) => setSelectedSong(songs.data.find(song => selectedValue === song.name)?.id)}
|
||||
onValueChange={(selectedValue) =>
|
||||
setSelectedSong(
|
||||
songs.data.find((song) => selectedValue === song.name)?.id
|
||||
)
|
||||
}
|
||||
defaultValue={songs.data.at(0)?.name}
|
||||
bgColor={colors.coolGray[500]}
|
||||
variant="filled"
|
||||
width={layout.width > 650 ? '200' : '150'}
|
||||
>
|
||||
{songs.data.map((option) => (
|
||||
<Select.Item
|
||||
key={option.id}
|
||||
label={option.name}
|
||||
value={option.name}
|
||||
/>
|
||||
<Select.Item key={option.id} label={option.name} value={option.name} />
|
||||
))}
|
||||
</Select>
|
||||
</Box>
|
||||
@@ -198,12 +195,9 @@ const ScoreGraph = () => {
|
||||
</Select>
|
||||
</Box>
|
||||
</Wrap>
|
||||
{selectedSong !== undefined &&
|
||||
<Graph
|
||||
songId={selectedSong}
|
||||
since={selectedSinceDate}
|
||||
/>
|
||||
}
|
||||
{selectedSong !== undefined && (
|
||||
<Graph songId={selectedSong} since={selectedSinceDate} />
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -127,12 +127,25 @@ const ButtonBase: React.FC<ButtonProps> = ({
|
||||
{icon && (
|
||||
<MyIcon
|
||||
size={'18'}
|
||||
color={type === 'outlined' ? '#6075F9' : colorScheme === 'dark' || type === 'filled' ? '#FFFFFF' : colors.black[500] }
|
||||
color={
|
||||
type === 'outlined'
|
||||
? '#6075F9'
|
||||
: colorScheme === 'dark' || type === 'filled'
|
||||
? '#FFFFFF'
|
||||
: colors.black[500]
|
||||
}
|
||||
variant={iconVariant}
|
||||
/>
|
||||
)}
|
||||
{iconImage && <Image source={{ uri: iconImage }} style={styles.icon} />}
|
||||
{title && <Text style={[styles.text, type === 'filled' ? {color: '#fff'} : {}]} selectable={false}>{title}</Text>}
|
||||
{title && (
|
||||
<Text
|
||||
style={[styles.text, type === 'filled' ? { color: '#fff' } : {}]}
|
||||
selectable={false}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</InteractiveBase>
|
||||
|
||||
@@ -127,12 +127,22 @@ const ButtonBase: React.FC<ButtonProps> = ({
|
||||
{icon && (
|
||||
<MyIcon
|
||||
size={'18'}
|
||||
color={type === 'outlined' ? '#6075F9' : colorScheme === 'light' ? colors.black[500] : '#FFFFFF'}
|
||||
color={
|
||||
type === 'outlined'
|
||||
? '#6075F9'
|
||||
: colorScheme === 'light'
|
||||
? colors.black[500]
|
||||
: '#FFFFFF'
|
||||
}
|
||||
variant={iconVariant}
|
||||
/>
|
||||
)}
|
||||
{iconImage && <Image source={{ uri: iconImage }} style={styles.icon} />}
|
||||
{title && <Text style={styles.text} selectable={false}>{title}</Text>}
|
||||
{title && (
|
||||
<Text style={styles.text} selectable={false}>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</InteractiveBase>
|
||||
|
||||
@@ -54,17 +54,9 @@ const CheckboxBase: React.FC<CheckboxProps> = ({ title, color, style, check, set
|
||||
>
|
||||
<View style={styles.content}>
|
||||
{check ? (
|
||||
<TickSquare
|
||||
size="24"
|
||||
color={color ?? colors.primary[400]}
|
||||
variant="Bold"
|
||||
/>
|
||||
<TickSquare size="24" color={color ?? colors.primary[400]} variant="Bold" />
|
||||
) : (
|
||||
<AddSquare
|
||||
size="24"
|
||||
color={color ?? colors.primary[400]}
|
||||
variant="Outline"
|
||||
/>
|
||||
<AddSquare size="24" color={color ?? colors.primary[400]} variant="Outline" />
|
||||
)}
|
||||
<Text style={styles.text} selectable={false}>
|
||||
{title}
|
||||
|
||||
@@ -5,22 +5,22 @@ import { StyleProp, ViewStyle } from 'react-native';
|
||||
import useColorScheme from '../../hooks/colorScheme';
|
||||
|
||||
type GlassmorphismCCProps = {
|
||||
children?: ReactNode,
|
||||
children?: ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
const GlassmorphismCC = ({ children, style }: GlassmorphismCCProps) => {
|
||||
const colorScheme = useColorScheme();
|
||||
console.log(colorScheme);
|
||||
const colorScheme = useColorScheme();
|
||||
console.log(colorScheme);
|
||||
|
||||
return (
|
||||
<BlurView
|
||||
style={[{borderRadius: 12}, style]}
|
||||
intensity={60}
|
||||
tint={colorScheme === 'light' ? 'light' : 'dark'}
|
||||
>
|
||||
{children}
|
||||
</BlurView>
|
||||
return (
|
||||
<BlurView
|
||||
style={[{ borderRadius: 12 }, style]}
|
||||
intensity={60}
|
||||
tint={colorScheme === 'light' ? 'light' : 'dark'}
|
||||
>
|
||||
{children}
|
||||
</BlurView>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,86 +3,88 @@ import { TouchableOpacity, Animated, StyleSheet, Platform } from 'react-native';
|
||||
import { Column, Text, useTheme } from 'native-base';
|
||||
|
||||
interface LinkBaseProps {
|
||||
text: string;
|
||||
onPress: () => void;
|
||||
text: string;
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
const LinkBase: React.FC<LinkBaseProps> = ({ text, onPress }) => {
|
||||
const underlineHeight = useRef(new Animated.Value(4)).current;
|
||||
const opacity = useRef(new Animated.Value(1)).current;
|
||||
const color = useRef(new Animated.Value(1)).current;
|
||||
const underlineHeight = useRef(new Animated.Value(4)).current;
|
||||
const opacity = useRef(new Animated.Value(1)).current;
|
||||
const color = useRef(new Animated.Value(1)).current;
|
||||
const theme = useTheme();
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (Platform.OS === 'web') {
|
||||
Animated.timing(underlineHeight, {
|
||||
toValue: 20,
|
||||
duration: 250,
|
||||
useNativeDriver: false
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
const handleMouseEnter = () => {
|
||||
if (Platform.OS === 'web') {
|
||||
Animated.timing(underlineHeight, {
|
||||
toValue: 20,
|
||||
duration: 250,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
if (Platform.OS === 'web') {
|
||||
Animated.timing(underlineHeight, {
|
||||
toValue: 4,
|
||||
duration: 250,
|
||||
useNativeDriver: false
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
const handleMouseLeave = () => {
|
||||
if (Platform.OS === 'web') {
|
||||
Animated.timing(underlineHeight, {
|
||||
toValue: 4,
|
||||
duration: 250,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
const handlePressIn = () => {
|
||||
Animated.timing(opacity, {
|
||||
toValue: 0.8,
|
||||
duration: 250,
|
||||
useNativeDriver: false
|
||||
}).start();
|
||||
};
|
||||
const handlePressIn = () => {
|
||||
Animated.timing(opacity, {
|
||||
toValue: 0.8,
|
||||
duration: 250,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
};
|
||||
|
||||
const handlePressOut = () => {
|
||||
Animated.timing(opacity, {
|
||||
toValue: 1,
|
||||
duration: 250,
|
||||
useNativeDriver: false
|
||||
}).start();
|
||||
};
|
||||
const handlePressOut = () => {
|
||||
Animated.timing(opacity, {
|
||||
toValue: 1,
|
||||
duration: 250,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.container}
|
||||
onPress={onPress}
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Column>
|
||||
<Text>{text}</Text>
|
||||
<Animated.View style={[
|
||||
styles.underline,
|
||||
{
|
||||
backgroundColor: theme.colors.primary[400],
|
||||
height: underlineHeight,
|
||||
opacity: opacity
|
||||
}
|
||||
]}/>
|
||||
</Column>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.container}
|
||||
onPress={onPress}
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<Column>
|
||||
<Text>{text}</Text>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.underline,
|
||||
{
|
||||
backgroundColor: theme.colors.primary[400],
|
||||
height: underlineHeight,
|
||||
opacity: opacity,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Column>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
position: 'relative',
|
||||
},
|
||||
underline: {
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
bottom: 0,
|
||||
},
|
||||
container: {
|
||||
position: 'relative',
|
||||
},
|
||||
underline: {
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export default LinkBase;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { translate } from '../../i18n/i18n';
|
||||
import { unsetAccessToken } from '../../state/UserSlice';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { useState } from 'react';
|
||||
import Modal from "react-native-modal";
|
||||
import Modal from 'react-native-modal';
|
||||
import React from 'react';
|
||||
import SignUpForm from '../../components/forms/signupform';
|
||||
import API, { APIError } from '../../API';
|
||||
@@ -26,39 +26,48 @@ const handleSubmit = async (username: string, password: string, email: string) =
|
||||
type LogoutButtonCCProps = {
|
||||
collapse?: boolean;
|
||||
isGuest?: boolean;
|
||||
style: any;
|
||||
buttonType: ButtonType
|
||||
style: any;
|
||||
buttonType: ButtonType;
|
||||
};
|
||||
|
||||
const LogoutButtonCC = ({collapse = false, isGuest = false, buttonType = 'menu', style}: LogoutButtonCCProps) => {
|
||||
const LogoutButtonCC = ({
|
||||
collapse = false,
|
||||
isGuest = false,
|
||||
buttonType = 'menu',
|
||||
style,
|
||||
}: LogoutButtonCCProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ButtonBase
|
||||
style={style}
|
||||
icon={LogoutCurve}
|
||||
title={collapse ? translate('signOutBtn') : undefined}
|
||||
type={buttonType}
|
||||
onPress={async () => {isGuest ? setIsVisible(true) : dispatch(unsetAccessToken());}}
|
||||
/>
|
||||
<PopupCC
|
||||
title={translate('Attention')}
|
||||
description={translate('transformGuestToUserExplanations')}
|
||||
isVisible={isVisible}
|
||||
setIsVisible={setIsVisible}
|
||||
>
|
||||
<SignUpForm onSubmit={handleSubmit} />
|
||||
<ButtonBase
|
||||
style={!collapse ? { width: '100%' } : {}}
|
||||
type="outlined"
|
||||
icon={LogoutCurve}
|
||||
title={translate('signOutBtn')}
|
||||
onPress={async () => { dispatch(unsetAccessToken()) }}
|
||||
/>
|
||||
</PopupCC>
|
||||
</>
|
||||
<>
|
||||
<ButtonBase
|
||||
style={style}
|
||||
icon={LogoutCurve}
|
||||
title={collapse ? translate('signOutBtn') : undefined}
|
||||
type={buttonType}
|
||||
onPress={async () => {
|
||||
isGuest ? setIsVisible(true) : dispatch(unsetAccessToken());
|
||||
}}
|
||||
/>
|
||||
<PopupCC
|
||||
title={translate('Attention')}
|
||||
description={translate('transformGuestToUserExplanations')}
|
||||
isVisible={isVisible}
|
||||
setIsVisible={setIsVisible}
|
||||
>
|
||||
<SignUpForm onSubmit={handleSubmit} />
|
||||
<ButtonBase
|
||||
style={!collapse ? { width: '100%' } : {}}
|
||||
type="outlined"
|
||||
icon={LogoutCurve}
|
||||
title={translate('signOutBtn')}
|
||||
onPress={async () => {
|
||||
dispatch(unsetAccessToken());
|
||||
}}
|
||||
/>
|
||||
</PopupCC>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,61 +3,57 @@ import ButtonBase from './ButtonBase';
|
||||
import { CloseSquare } from 'iconsax-react-native';
|
||||
import { BlurView } from 'expo-blur';
|
||||
import { ReactNode } from 'react';
|
||||
import Modal from "react-native-modal";
|
||||
import Modal from 'react-native-modal';
|
||||
import React from 'react';
|
||||
import GlassmorphismCC from './Glassmorphism';
|
||||
|
||||
type PopupCCProps = {
|
||||
title: string,
|
||||
description?: string,
|
||||
children?: ReactNode,
|
||||
isVisible: boolean,
|
||||
setIsVisible?: (isVisible: boolean) => void,
|
||||
title: string;
|
||||
description?: string;
|
||||
children?: ReactNode;
|
||||
isVisible: boolean;
|
||||
setIsVisible?: (isVisible: boolean) => void;
|
||||
};
|
||||
|
||||
const PopupCC = ({ title, description, children, isVisible, setIsVisible }: PopupCCProps) => {
|
||||
return (
|
||||
<Modal
|
||||
backdropOpacity={0.75}
|
||||
isVisible={isVisible}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignContent: 'center',
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<GlassmorphismCC>
|
||||
<Column
|
||||
style={{
|
||||
maxWidth: '800px',
|
||||
maxHeight: 'fit-content',
|
||||
padding: '20px',
|
||||
}}
|
||||
space={4}
|
||||
>
|
||||
<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 &&
|
||||
<ButtonBase
|
||||
type='menu'
|
||||
style={{width: 'fit-content'}}
|
||||
icon={CloseSquare}
|
||||
onPress={async () => setIsVisible(false)}
|
||||
/>
|
||||
}
|
||||
</Row>
|
||||
</Heading>
|
||||
{description !== undefined &&
|
||||
<Text>{description}</Text>
|
||||
}
|
||||
{children !== undefined && children}
|
||||
</Column>
|
||||
</GlassmorphismCC>
|
||||
</Modal>
|
||||
<Modal
|
||||
backdropOpacity={0.75}
|
||||
isVisible={isVisible}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignContent: 'center',
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<GlassmorphismCC>
|
||||
<Column
|
||||
style={{
|
||||
maxWidth: '800px',
|
||||
maxHeight: 'fit-content',
|
||||
padding: '20px',
|
||||
}}
|
||||
space={4}
|
||||
>
|
||||
<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 && (
|
||||
<ButtonBase
|
||||
type="menu"
|
||||
style={{ width: 'fit-content' }}
|
||||
icon={CloseSquare}
|
||||
onPress={async () => setIsVisible(false)}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</Heading>
|
||||
{description !== undefined && <Text>{description}</Text>}
|
||||
{children !== undefined && children}
|
||||
</Column>
|
||||
</GlassmorphismCC>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -86,11 +86,11 @@ const ScaffoldCC = (props: ScaffoldCCProps) => {
|
||||
height: 32,
|
||||
}}
|
||||
/>
|
||||
{layout.width > 650 &&
|
||||
{layout.width > 650 && (
|
||||
<Text fontSize={'xl'} selectable={false}>
|
||||
Chromacase
|
||||
</Text>
|
||||
}
|
||||
)}
|
||||
</Row>
|
||||
<Spacer height="xl" />
|
||||
<View style={{ width: '100%' }}>
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { Flex, Stack, View, Text, Wrap, Image, Row, Column, ScrollView, useToast } from 'native-base';
|
||||
import {
|
||||
Flex,
|
||||
Stack,
|
||||
View,
|
||||
Text,
|
||||
Wrap,
|
||||
Image,
|
||||
Row,
|
||||
Column,
|
||||
ScrollView,
|
||||
useToast,
|
||||
} from 'native-base';
|
||||
import { FunctionComponent } from 'react';
|
||||
import { Linking, useWindowDimensions } from 'react-native';
|
||||
import ButtonBase from './ButtonBase';
|
||||
@@ -37,9 +48,10 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
const dispatch = useDispatch();
|
||||
const toast = useToast();
|
||||
const colorScheme = useColorScheme();
|
||||
const logo = colorScheme == 'light'
|
||||
? require('../../assets/icon_light.png')
|
||||
: require('../../assets/icon_dark.png');
|
||||
const logo =
|
||||
colorScheme == 'light'
|
||||
? require('../../assets/icon_light.png')
|
||||
: require('../../assets/icon_dark.png');
|
||||
const [banner] = useAssets(require('../../assets/banner.jpg'));
|
||||
|
||||
return (
|
||||
@@ -49,7 +61,7 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
style={{ flex: 1, backgroundColor: '#cdd4fd' }}
|
||||
>
|
||||
<Column style={{ flex: 1 }}>
|
||||
<Wrap space={4} direction='row' style={{padding: 16, paddingBottom: 0}}>
|
||||
<Wrap space={4} direction="row" style={{ padding: 16, paddingBottom: 0 }}>
|
||||
<Row space={2} flex={1}>
|
||||
<Image
|
||||
source={{ uri: logo }}
|
||||
@@ -59,14 +71,14 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
height: 32,
|
||||
}}
|
||||
/>
|
||||
{layout.width > 650 &&
|
||||
{layout.width > 650 && (
|
||||
<Text fontSize={'xl'} selectable={false}>
|
||||
Chromacase
|
||||
</Text>
|
||||
}
|
||||
)}
|
||||
</Row>
|
||||
<ButtonBase
|
||||
title='guest mode'
|
||||
title="guest mode"
|
||||
onPress={async () => {
|
||||
try {
|
||||
handleGuestLogin((accessToken: string) => {
|
||||
@@ -79,10 +91,17 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
}
|
||||
toast.show({ description: error as string });
|
||||
}
|
||||
}}
|
||||
}}
|
||||
/>
|
||||
</Wrap>
|
||||
<ScrollView contentContainerStyle={{ padding: 16, flexGrow: 1, justifyContent: 'center', alignSelf: 'center' }}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{
|
||||
padding: 16,
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
>
|
||||
<View style={{ width: '100%', maxWidth: 420 }}>
|
||||
<Stack
|
||||
space={8}
|
||||
@@ -125,16 +144,13 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
{submitButton}
|
||||
<Wrap style={{ flexDirection: 'row', justifyContent: 'center' }}>
|
||||
<Text>{link.label}</Text>
|
||||
<LinkBase
|
||||
text={link.text}
|
||||
onPress={link.onPress}
|
||||
/>
|
||||
<LinkBase text={link.text} onPress={link.onPress} />
|
||||
</Wrap>
|
||||
</Stack>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</Column>
|
||||
{layout.width > 650 &&
|
||||
{layout.width > 650 && (
|
||||
<View style={{ width: '50%', height: '100%', padding: 16 }}>
|
||||
<Image
|
||||
source={{ uri: banner?.at(0)?.uri }}
|
||||
@@ -142,8 +158,8 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
style={{ width: '100%', height: '100%', borderRadius: 8 }}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
{colorScheme === 'dark' &&
|
||||
)}
|
||||
{colorScheme === 'dark' && (
|
||||
<LinearGradient
|
||||
colors={['#101014', '#6075F9']}
|
||||
style={{
|
||||
@@ -153,7 +169,7 @@ const ScaffoldAuth: FunctionComponent<ScaffoldAuthProps> = ({
|
||||
zIndex: -2,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,31 +3,23 @@ import useColorScheme from '../../hooks/colorScheme';
|
||||
import { useQuery } from '../../Queries';
|
||||
import API from '../../API';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import {
|
||||
Cup,
|
||||
Discover,
|
||||
Icon,
|
||||
Music,
|
||||
SearchNormal1,
|
||||
Setting2,
|
||||
User,
|
||||
} from 'iconsax-react-native';
|
||||
import { Cup, Discover, Icon, Music, SearchNormal1, Setting2, User } from 'iconsax-react-native';
|
||||
import { LoadingView } from '../Loading';
|
||||
import ScaffoldDesktopCC from './ScaffoldDesktopCC';
|
||||
import ScaffoldMobileCC from './ScaffoldMobileCC';
|
||||
|
||||
const menu: {
|
||||
type: "main" | "sub";
|
||||
type: 'main' | 'sub';
|
||||
title: string;
|
||||
icon: Icon;
|
||||
link: string;
|
||||
}[] = [
|
||||
{ type: "main", title: 'menuDiscovery', icon: Discover, link: 'HomeNew' },
|
||||
{ type: "main", title: 'menuProfile', icon: User, link: 'User' },
|
||||
{ type: "main", title: 'menuMusic', icon: Music, link: 'Home' },
|
||||
{ type: "main", title: 'menuSearch', icon: SearchNormal1, link: 'Search' },
|
||||
{ type: "main", title: 'menuLeaderBoard', icon: Cup, link: 'Score' },
|
||||
{ type: "sub", title: 'menuSettings', icon: Setting2, link: 'Settings' },
|
||||
{ type: 'main', title: 'menuDiscovery', icon: Discover, link: 'HomeNew' },
|
||||
{ type: 'main', title: 'menuProfile', icon: User, link: 'User' },
|
||||
{ type: 'main', title: 'menuMusic', icon: Music, link: 'Home' },
|
||||
{ type: 'main', title: 'menuSearch', icon: SearchNormal1, link: 'Search' },
|
||||
{ type: 'main', title: 'menuLeaderBoard', icon: Cup, link: 'Score' },
|
||||
{ type: 'sub', title: 'menuSettings', icon: Setting2, link: 'Settings' },
|
||||
];
|
||||
|
||||
type ScaffoldCCProps = {
|
||||
@@ -36,7 +28,7 @@ type ScaffoldCCProps = {
|
||||
withPadding?: boolean;
|
||||
};
|
||||
|
||||
const ScaffoldCC = ({children, routeName, withPadding = true}: ScaffoldCCProps) => {
|
||||
const ScaffoldCC = ({ children, routeName, withPadding = true }: ScaffoldCCProps) => {
|
||||
const userQuery = useQuery(API.getUserInfo);
|
||||
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
|
||||
|
||||
@@ -44,13 +36,14 @@ const ScaffoldCC = ({children, routeName, withPadding = true}: ScaffoldCCProps)
|
||||
return <LoadingView />;
|
||||
}
|
||||
const colorScheme = useColorScheme();
|
||||
const logo = colorScheme == 'light'
|
||||
? require('../../assets/icon_light.png')
|
||||
: require('../../assets/icon_dark.png');
|
||||
const logo =
|
||||
colorScheme == 'light'
|
||||
? require('../../assets/icon_light.png')
|
||||
: require('../../assets/icon_dark.png');
|
||||
|
||||
return (
|
||||
<Flex style={{ flex: 1, backgroundColor: '#cdd4fd' }}>
|
||||
{screenSize === 'small' ?
|
||||
{screenSize === 'small' ? (
|
||||
<ScaffoldMobileCC
|
||||
user={userQuery.data}
|
||||
logo={logo}
|
||||
@@ -59,7 +52,8 @@ const ScaffoldCC = ({children, routeName, withPadding = true}: ScaffoldCCProps)
|
||||
>
|
||||
{children}
|
||||
</ScaffoldMobileCC>
|
||||
: <ScaffoldDesktopCC
|
||||
) : (
|
||||
<ScaffoldDesktopCC
|
||||
user={userQuery.data}
|
||||
logo={logo}
|
||||
routeName={routeName}
|
||||
@@ -68,8 +62,8 @@ const ScaffoldCC = ({children, routeName, withPadding = true}: ScaffoldCCProps)
|
||||
>
|
||||
{children}
|
||||
</ScaffoldDesktopCC>
|
||||
}
|
||||
{colorScheme === 'dark' &&
|
||||
)}
|
||||
{colorScheme === 'dark' && (
|
||||
<LinearGradient
|
||||
colors={['#101014', '#6075F9']}
|
||||
style={{
|
||||
@@ -79,7 +73,7 @@ const ScaffoldCC = ({children, routeName, withPadding = true}: ScaffoldCCProps)
|
||||
zIndex: -2,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,20 +17,20 @@ import { ColorSchemeProvider } from '../../Theme';
|
||||
import useColorScheme from '../../hooks/colorScheme';
|
||||
|
||||
type ScaffoldDesktopCCProps = {
|
||||
widthPadding: boolean,
|
||||
widthPadding: boolean;
|
||||
children?: React.ReactNode;
|
||||
user: User;
|
||||
logo: string;
|
||||
routeName: string;
|
||||
menu: {
|
||||
type: "main" | "sub";
|
||||
type: 'main' | 'sub';
|
||||
title: string;
|
||||
icon: Icon;
|
||||
link: string;
|
||||
}[]
|
||||
}[];
|
||||
};
|
||||
|
||||
const SongHistory = (props: {quantity: number}) => {
|
||||
const SongHistory = (props: { quantity: number }) => {
|
||||
const playHistoryQuery = useQuery(API.getUserPlayHistory);
|
||||
const songHistory = useQueries(
|
||||
playHistoryQuery.data?.map(({ songID }) => API.getSong(songID)) ?? []
|
||||
@@ -42,9 +42,10 @@ const SongHistory = (props: {quantity: number}) => {
|
||||
|
||||
return (
|
||||
<View>
|
||||
{songHistory.length === 0 ?
|
||||
{songHistory.length === 0 ? (
|
||||
<Text style={{ paddingHorizontal: 16 }}>{translate('menuNoSongsPlayedYet')}</Text>
|
||||
: songHistory
|
||||
) : (
|
||||
songHistory
|
||||
.map((h) => h.data)
|
||||
.filter((data): data is Song => data !== undefined)
|
||||
.filter(
|
||||
@@ -62,12 +63,11 @@ const SongHistory = (props: {quantity: number}) => {
|
||||
>
|
||||
<Text numberOfLines={1}>{histoItem.name}</Text>
|
||||
</View>
|
||||
)
|
||||
)
|
||||
}
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
|
||||
const navigation = useNavigation();
|
||||
@@ -103,39 +103,53 @@ const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
|
||||
alignItems: isSmallScreen ? 'center' : 'flex-start',
|
||||
}}
|
||||
/>
|
||||
{!isSmallScreen &&
|
||||
{!isSmallScreen && (
|
||||
<Text fontSize={'xl'} selectable={false}>
|
||||
Chromacase
|
||||
</Text>
|
||||
}
|
||||
)}
|
||||
</Row>
|
||||
<Spacer height="lg" />
|
||||
<View>
|
||||
{props.menu.map((value) => (
|
||||
value.type === "main" &&
|
||||
<View key={'key-menu-link-' + value.title}>
|
||||
<ButtonBase
|
||||
style={!isSmallScreen ? { width: '100%' } : {}}
|
||||
type="menu"
|
||||
icon={value.icon}
|
||||
title={!isSmallScreen ? translate(value.title as 'menuDiscovery' | 'menuProfile' | 'menuMusic' | 'menuSearch' | 'menuLeaderBoard' | 'menuSettings') : undefined}
|
||||
isDisabled={props.routeName === value.link}
|
||||
iconVariant={
|
||||
props.routeName === value.link ? 'Bold' : 'Outline'
|
||||
}
|
||||
onPress={async () =>
|
||||
navigation.navigate(value.link as never)
|
||||
}
|
||||
/>
|
||||
<Spacer height='xs'/>
|
||||
</View>
|
||||
))}
|
||||
{props.menu.map(
|
||||
(value) =>
|
||||
value.type === 'main' && (
|
||||
<View key={'key-menu-link-' + value.title}>
|
||||
<ButtonBase
|
||||
style={!isSmallScreen ? { width: '100%' } : {}}
|
||||
type="menu"
|
||||
icon={value.icon}
|
||||
title={
|
||||
!isSmallScreen
|
||||
? translate(
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
isDisabled={props.routeName === value.link}
|
||||
iconVariant={
|
||||
props.routeName === value.link ? 'Bold' : 'Outline'
|
||||
}
|
||||
onPress={async () =>
|
||||
navigation.navigate(value.link as never)
|
||||
}
|
||||
/>
|
||||
<Spacer height="xs" />
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
{!isSmallScreen &&
|
||||
{!isSmallScreen && (
|
||||
<View>
|
||||
<Divider my="2" _light={{bg: colors.black[500]}} _dark={{bg:'#FFF'}}/>
|
||||
<Spacer height='xs'/>
|
||||
<Divider my="2" _light={{ bg: colors.black[500] }} _dark={{ bg: '#FFF' }} />
|
||||
<Spacer height="xs" />
|
||||
<Text
|
||||
bold
|
||||
style={{
|
||||
@@ -146,38 +160,52 @@ const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
|
||||
>
|
||||
{translate('menuRecentlyPlayed')}
|
||||
</Text>
|
||||
<SongHistory quantity={3}/>
|
||||
<SongHistory quantity={3} />
|
||||
</View>
|
||||
}
|
||||
<Spacer height='xs'/>
|
||||
)}
|
||||
<Spacer height="xs" />
|
||||
<View style={!isSmallScreen ? { width: '100%' } : {}}>
|
||||
<Divider my="2" _light={{bg: colors.black[500]}} _dark={{bg: '#FFF'}}/>
|
||||
<Spacer height='xs'/>
|
||||
{props.menu.map((value) => (
|
||||
value.type === "sub" &&
|
||||
<ButtonBase
|
||||
key={'key-menu-link-' + value.title}
|
||||
style={!isSmallScreen ? { width: '100%' } : {}}
|
||||
type="menu"
|
||||
icon={value.icon}
|
||||
title={!isSmallScreen ? translate(value.title as 'menuDiscovery' | 'menuProfile' | 'menuMusic' | 'menuSearch' | 'menuLeaderBoard' | 'menuSettings') : undefined}
|
||||
isDisabled={props.routeName === value.link}
|
||||
iconVariant={
|
||||
props.routeName === value.link ? 'Bold' : 'Outline'
|
||||
}
|
||||
onPress={async () =>
|
||||
navigation.navigate(value.link as never)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<Spacer height='xs'/>
|
||||
<LogoutButtonCC collapse={!isSmallScreen} isGuest={props.user.isGuest} style={!isSmallScreen ? { width: '100%' } : {}} buttonType={'menu'}/>
|
||||
<Divider my="2" _light={{ bg: colors.black[500] }} _dark={{ bg: '#FFF' }} />
|
||||
<Spacer height="xs" />
|
||||
{props.menu.map(
|
||||
(value) =>
|
||||
value.type === 'sub' && (
|
||||
<ButtonBase
|
||||
key={'key-menu-link-' + value.title}
|
||||
style={!isSmallScreen ? { width: '100%' } : {}}
|
||||
type="menu"
|
||||
icon={value.icon}
|
||||
title={
|
||||
!isSmallScreen
|
||||
? translate(
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
isDisabled={props.routeName === value.link}
|
||||
iconVariant={
|
||||
props.routeName === value.link ? 'Bold' : 'Outline'
|
||||
}
|
||||
onPress={async () => navigation.navigate(value.link as never)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<Spacer height="xs" />
|
||||
<LogoutButtonCC
|
||||
collapse={!isSmallScreen}
|
||||
isGuest={props.user.isGuest}
|
||||
style={!isSmallScreen ? { width: '100%' } : {}}
|
||||
buttonType={'menu'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<ScrollView
|
||||
style={{ flex: 1, maxHeight: '100vh' }}
|
||||
contentContainerStyle={{ flex: 1 }}
|
||||
>
|
||||
<ScrollView style={{ flex: 1, maxHeight: '100vh' }} contentContainerStyle={{ flex: 1 }}>
|
||||
<GlassmorphismCC
|
||||
style={{
|
||||
backgroundColor: colors.coolGray[500],
|
||||
@@ -192,10 +220,9 @@ const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
|
||||
>
|
||||
{props.children}
|
||||
</GlassmorphismCC>
|
||||
<Spacer height='xs'/>
|
||||
<Spacer height="xs" />
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { View } from 'react-native';
|
||||
import { ScrollView, Flex, useMediaQuery, useTheme } from 'native-base';
|
||||
import { ScrollView, Flex, useMediaQuery, useTheme } from 'native-base';
|
||||
import ButtonBase from './ButtonBase';
|
||||
import { Icon } from 'iconsax-react-native';
|
||||
import { useNavigation } from '../../Navigation';
|
||||
@@ -13,11 +13,11 @@ type ScaffoldMobileCCProps = {
|
||||
logo: string;
|
||||
routeName: string;
|
||||
menu: {
|
||||
type: "main" | "sub";
|
||||
type: 'main' | 'sub';
|
||||
title: string;
|
||||
icon: Icon;
|
||||
link: string;
|
||||
}[]
|
||||
}[];
|
||||
};
|
||||
|
||||
const ScaffoldMobileCC = (props: ScaffoldMobileCCProps) => {
|
||||
@@ -35,16 +35,14 @@ const ScaffoldMobileCC = (props: ScaffoldMobileCCProps) => {
|
||||
maxHeight: '100vh',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
padding: 16
|
||||
padding: 16,
|
||||
}}
|
||||
contentContainerStyle={{ flex: 1 }}
|
||||
>
|
||||
<View style={{ flex: 1, minHeight: 'fit-content' }}>
|
||||
{props.children}
|
||||
</View>
|
||||
<Spacer/>
|
||||
<View style={{ flex: 1, minHeight: 'fit-content' }}>{props.children}</View>
|
||||
<Spacer />
|
||||
</ScrollView>
|
||||
<View style={{padding: 8, paddingTop: 0}}>
|
||||
<View style={{ padding: 8, paddingTop: 0 }}>
|
||||
<Flex
|
||||
style={{
|
||||
width: '100%',
|
||||
@@ -61,14 +59,22 @@ const ScaffoldMobileCC = (props: ScaffoldMobileCCProps) => {
|
||||
key={'key-menu-link-' + value.title}
|
||||
type="menu"
|
||||
icon={value.icon}
|
||||
title={props.routeName === value.link && !isSmallScreen ? translate(value.title as 'menuDiscovery' | 'menuProfile' | 'menuMusic' | 'menuSearch' | 'menuLeaderBoard' | 'menuSettings') : undefined}
|
||||
title={
|
||||
props.routeName === value.link && !isSmallScreen
|
||||
? translate(
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
isDisabled={props.routeName === value.link}
|
||||
iconVariant={
|
||||
props.routeName === value.link ? 'Bold' : 'Outline'
|
||||
}
|
||||
onPress={async () =>
|
||||
navigation.navigate(value.link as never)
|
||||
}
|
||||
iconVariant={props.routeName === value.link ? 'Bold' : 'Outline'}
|
||||
onPress={async () => navigation.navigate(value.link as never)}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
|
||||
@@ -27,14 +27,14 @@ const SeparatorBase: FunctionComponent<SeparatorBaseProps> = ({ children }) => {
|
||||
const colorScheme = useColorScheme();
|
||||
const { colors } = useTheme();
|
||||
const color = colorScheme === 'light' ? colors.black[500] : '#FFFFFF';
|
||||
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.line, {backgroundColor: color}]} />
|
||||
<View style={[styles.line, { backgroundColor: color }]} />
|
||||
<Text style={styles.text}>{children}</Text>
|
||||
<View style={[styles.line, {backgroundColor: color}]} />
|
||||
<View style={[styles.line, { backgroundColor: color }]} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default SeparatorBase;
|
||||
|
||||
@@ -104,7 +104,11 @@ const TextFieldBase: React.FC<TextFieldBaseProps> = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<InteractiveBase style={[style, { borderRadius: 12 }]} styleAnimate={styleAnimate} focusable={false}>
|
||||
<InteractiveBase
|
||||
style={[style, { borderRadius: 12 }]}
|
||||
styleAnimate={styleAnimate}
|
||||
focusable={false}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.iconContainerLeft}>
|
||||
{icon && (
|
||||
@@ -121,7 +125,9 @@ const TextFieldBase: React.FC<TextFieldBaseProps> = ({
|
||||
style={[styles.input, icon ? {} : { paddingLeft: 12 }]}
|
||||
autoComplete={autoComplete}
|
||||
placeholder={placeholder + (isRequired ? '*' : '')}
|
||||
placeholderTextColor={colorScheme === 'light' ? 'rgba(0,0,0,0.7)' : 'rgba(255,255,255,0.7)'}
|
||||
placeholderTextColor={
|
||||
colorScheme === 'light' ? 'rgba(0,0,0,0.7)' : 'rgba(255,255,255,0.7)'
|
||||
}
|
||||
secureTextEntry={isSecret ? !isPasswordVisible : false}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
|
||||
@@ -130,7 +130,7 @@ const SignUpForm = ({ onSubmit }: SignupFormProps) => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Spacer height='xs'/>
|
||||
<Spacer height="xs" />
|
||||
<ButtonBase
|
||||
style={{ width: '100%' }}
|
||||
title={translate('signUpBtn')}
|
||||
|
||||
@@ -50,14 +50,14 @@ export const en = {
|
||||
menuNoSongsPlayedYet: 'No songs played yet',
|
||||
|
||||
//signup
|
||||
signupPageTitle: "Create an account",
|
||||
signupPageParagraph: "Learn to play the piano in an enjoyable and free way.",
|
||||
signupLinkLabel: "Already have an account? ",
|
||||
signupLinkText: "Sign in.",
|
||||
signupPageTitle: 'Create an account',
|
||||
signupPageParagraph: 'Learn to play the piano in an enjoyable and free way.',
|
||||
signupLinkLabel: 'Already have an account? ',
|
||||
signupLinkText: 'Sign in.',
|
||||
|
||||
//signin
|
||||
signinPageTitle: "Welcome !",
|
||||
signinPageParagraph: "Continue with Google or enter your details.",
|
||||
signinPageTitle: 'Welcome !',
|
||||
signinPageParagraph: 'Continue with Google or enter your details.',
|
||||
signinLinkLabel: "You don't have an account? ",
|
||||
signinLinkText: 'Sign up for free.',
|
||||
|
||||
@@ -185,20 +185,25 @@ export const en = {
|
||||
settingsProfileTabAvatarSectionUpdateToast: 'Update successful',
|
||||
settingsProfileTabAvatarSectionUpdateToastError: 'Update failed',
|
||||
settingsProfileTabChangeEmailSectionTitle: 'Change email',
|
||||
settingsProfileTabChangeEmailSectionDescription: 'Enter your current email and set your new email',
|
||||
settingsProfileTabChangeEmailSectionDescription:
|
||||
'Enter your current email and set your new email',
|
||||
settingsProfileTabChangePasswordSectionTitle: 'Change password',
|
||||
settingsProfileTabChangePasswordSectionDescription: 'Enter your current password and set your new password',
|
||||
settingsProfileTabChangePasswordSectionDescription:
|
||||
'Enter your current password and set your new password',
|
||||
// Premium Tab
|
||||
settingsPremiumTabPremiumAccountSectionTitle: 'Premium account',
|
||||
settingsPremiumTabPremiumAccountSectionDescription: 'Premium customization and tools to take you to the next level',
|
||||
settingsPremiumTabPremiumAccountSectionDescription:
|
||||
'Premium customization and tools to take you to the next level',
|
||||
settingsPremiumTabPianoMagiqueSectionTitle: 'Magic Piano',
|
||||
settingsPremiumTabPianoMagiqueSectionDescription: 'Brings light to the piano during games',
|
||||
settingsPremiumTabPianoMagiqueSectionHelper: 'You must own the Chromacase light module to use this feature',
|
||||
settingsPremiumTabPianoMagiqueSectionHelper:
|
||||
'You must own the Chromacase light module to use this feature',
|
||||
settingsPremiumTabThemePianoSectionTitle: 'Piano Theme',
|
||||
settingsPremiumTabThemePianoSectionDescription: 'Set your piano theme',
|
||||
// Preferences Tab
|
||||
SettingsPreferencesTabThemeSectionTitle: 'Theme',
|
||||
SettingsPreferencesTabThemeSectionDescription: 'Set the theme (Dark or Light) of your application',
|
||||
SettingsPreferencesTabThemeSectionDescription:
|
||||
'Set the theme (Dark or Light) of your application',
|
||||
SettingsPreferencesTabLanguageSectionTitle: 'Language',
|
||||
SettingsPreferencesTabLanguageSectionDescription: 'Set the language of your application',
|
||||
SettingsPreferencesTabDifficultySectionTitle: 'Difficulty',
|
||||
@@ -206,23 +211,29 @@ export const en = {
|
||||
SettingsPreferencesTabColorblindModeSectionTitle: 'Colorblind Mode',
|
||||
SettingsPreferencesTabColorblindModeSectionDescription: 'Increases contrast',
|
||||
SettingsPreferencesTabMicVolumeSectionTitle: 'Mic Volume',
|
||||
SettingsPreferencesTabMicVolumeSectionDescription: 'Adjust the volume of your microphone according to your preference',
|
||||
SettingsPreferencesTabMicVolumeSectionDescription:
|
||||
'Adjust the volume of your microphone according to your preference',
|
||||
// Notifications Tab
|
||||
SettingsNotificationsTabPushNotificationsSectionTitle: 'Push Notifications',
|
||||
SettingsNotificationsTabPushNotificationsSectionDescription: 'This notification will appear on your device as a pop-up',
|
||||
SettingsNotificationsTabPushNotificationsSectionDescription:
|
||||
'This notification will appear on your device as a pop-up',
|
||||
SettingsNotificationsTabEmailNotificationsSectionTitle: 'Email',
|
||||
SettingsNotificationsTabEmailNotificationsSectionDescription: 'Receive emails to reach your goals',
|
||||
SettingsNotificationsTabEmailNotificationsSectionDescription:
|
||||
'Receive emails to reach your goals',
|
||||
SettingsNotificationsTabTrainingReminderSectionTitle: 'Training Reminder',
|
||||
SettingsNotificationsTabTrainingReminderSectionDescription: 'Regular training is the key',
|
||||
SettingsNotificationsTabReleaseAlertSectionTitle: 'New Release Alerts',
|
||||
SettingsNotificationsTabReleaseAlertSectionDescription: 'Stay informed of our updates',
|
||||
// Privacy Tab
|
||||
SettingsPrivacyTabDataCollectionSectionTitle: 'Data Collection',
|
||||
SettingsPrivacyTabDataCollectionSectionDescription: 'Do you accept the collection of your data for the improvement of Chromacase?',
|
||||
SettingsPrivacyTabDataCollectionSectionDescription:
|
||||
'Do you accept the collection of your data for the improvement of Chromacase?',
|
||||
SettingsPrivacyTabCustomAdsSectionTitle: 'Customized Ads',
|
||||
SettingsPrivacyTabCustomAdsSectionDescription: 'Display suggestions in the recommendations section',
|
||||
SettingsPrivacyTabCustomAdsSectionDescription:
|
||||
'Display suggestions in the recommendations section',
|
||||
SettingsPrivacyTabRecommendationsSectionTitle: 'Recommendations',
|
||||
SettingsPrivacyTabRecommendationsSectionDescription: 'Would you like to receive our advice and recommendations?',
|
||||
SettingsPrivacyTabRecommendationsSectionDescription:
|
||||
'Would you like to receive our advice and recommendations?',
|
||||
|
||||
SettingsCategoryProfile: 'Profile',
|
||||
SettingsCategoryPreferences: 'Preferences',
|
||||
@@ -322,18 +333,18 @@ export const fr: typeof en = {
|
||||
menuLeaderBoard: 'Classement',
|
||||
menuSettings: 'Paramètres',
|
||||
|
||||
menuRecentlyPlayed : 'Récemment jouée',
|
||||
menuNoSongsPlayedYet : 'Aucune chanson jouée pour l\'instant',
|
||||
menuRecentlyPlayed: 'Récemment jouée',
|
||||
menuNoSongsPlayedYet: "Aucune chanson jouée pour l'instant",
|
||||
|
||||
//signup
|
||||
signupPageTitle: "Créer un compte",
|
||||
signupPageParagraph: "Apprendre le piano gratuitement et de manière ludique",
|
||||
signupLinkLabel: "Vous avez déjà un compte ? ",
|
||||
signupPageTitle: 'Créer un compte',
|
||||
signupPageParagraph: 'Apprendre le piano gratuitement et de manière ludique',
|
||||
signupLinkLabel: 'Vous avez déjà un compte ? ',
|
||||
signupLinkText: "S'identifier",
|
||||
|
||||
//signin
|
||||
signinPageTitle: "Bienvenue !",
|
||||
signinPageParagraph: "Continuez avec Google ou entrez vos coordonnées.",
|
||||
signinPageTitle: 'Bienvenue !',
|
||||
signinPageParagraph: 'Continuez avec Google ou entrez vos coordonnées.',
|
||||
signinLinkLabel: "Vous n'avez pas de compte ? ",
|
||||
signinLinkText: 'Inscrivez-vous gratuitement',
|
||||
|
||||
@@ -438,8 +449,8 @@ export const fr: typeof en = {
|
||||
settingsTabPremium: 'Premium',
|
||||
settingsTabPreferences: 'Preferences',
|
||||
settingsTabNotifications: 'Notifications',
|
||||
settingsTabPrivacy: "Confidentialité",
|
||||
settingsTabPiano: "Piano",
|
||||
settingsTabPrivacy: 'Confidentialité',
|
||||
settingsTabPiano: 'Piano',
|
||||
// Profile Tab
|
||||
settingsProfileTabGoogleSectionTitle: 'Compte Google',
|
||||
settingsProfileTabGoogleSectionDescription: 'Liez votre compte Google à ChromaCase',
|
||||
@@ -450,51 +461,66 @@ export const fr: typeof en = {
|
||||
settingsProfileTabVerifiedSectionVerifiedText: 'vérifié',
|
||||
settingsProfileTabVerifiedSectionNotVerifiedText: 'non vérifié',
|
||||
settingsProfileTabVerifiedSectionVerificationToast: 'Courrier de vérification envoyé',
|
||||
settingsProfileTabVerifiedSectionVerificationToastError: 'Erreur d\'envoi du courrier de vérification',
|
||||
settingsProfileTabVerifiedSectionVerificationToastError:
|
||||
"Erreur d'envoi du courrier de vérification",
|
||||
settingsProfileTabAvatarSectionTitle: 'Avatar',
|
||||
settingsProfileTabAvatarSectionDescription: 'Changez votre photo de profil',
|
||||
settingsProfileTabAvatarSectionChangeItText: 'Modifier',
|
||||
settingsProfileTabAvatarSectionUpdateToast: 'Mise à jour réussie',
|
||||
settingsProfileTabAvatarSectionUpdateToastError: 'Échec de la mise à jour',
|
||||
settingsProfileTabChangeEmailSectionTitle: 'Changer l\'email',
|
||||
settingsProfileTabChangeEmailSectionDescription: 'Saisissez votre adresse électronique actuelle et définissez votre nouvelle adresse électronique',
|
||||
settingsProfileTabChangeEmailSectionTitle: "Changer l'email",
|
||||
settingsProfileTabChangeEmailSectionDescription:
|
||||
'Saisissez votre adresse électronique actuelle et définissez votre nouvelle adresse électronique',
|
||||
settingsProfileTabChangePasswordSectionTitle: 'Changer le mot de passe',
|
||||
settingsProfileTabChangePasswordSectionDescription: 'Saisissez votre mot de passe actuel et définissez votre nouveau mot de passe',
|
||||
settingsProfileTabChangePasswordSectionDescription:
|
||||
'Saisissez votre mot de passe actuel et définissez votre nouveau mot de passe',
|
||||
// Premium Tab
|
||||
settingsPremiumTabPremiumAccountSectionTitle: 'Compte premium',
|
||||
settingsPremiumTabPremiumAccountSectionDescription: 'Personnalisation premium et outils vous permettant de passer au niveau supérieur',
|
||||
settingsPremiumTabPremiumAccountSectionDescription:
|
||||
'Personnalisation premium et outils vous permettant de passer au niveau supérieur',
|
||||
settingsPremiumTabPianoMagiqueSectionTitle: 'Piano Magique',
|
||||
settingsPremiumTabPianoMagiqueSectionDescription: 'Fait apparaître de la lumière sur le piano pendant les parties',
|
||||
settingsPremiumTabPianoMagiqueSectionHelper: 'Vous devez posséder le module physique lumineux Chromacase pour pouvoir utiliser cette fonctionnalité',
|
||||
settingsPremiumTabPianoMagiqueSectionDescription:
|
||||
'Fait apparaître de la lumière sur le piano pendant les parties',
|
||||
settingsPremiumTabPianoMagiqueSectionHelper:
|
||||
'Vous devez posséder le module physique lumineux Chromacase pour pouvoir utiliser cette fonctionnalité',
|
||||
settingsPremiumTabThemePianoSectionTitle: 'Thème de piano',
|
||||
settingsPremiumTabThemePianoSectionDescription: 'Définissez le thème de votre piano',
|
||||
// Preferences Tab
|
||||
SettingsPreferencesTabThemeSectionTitle: 'Thème',
|
||||
SettingsPreferencesTabThemeSectionDescription: 'Définissez le thème (Sombre ou Clair) de votre application',
|
||||
SettingsPreferencesTabThemeSectionDescription:
|
||||
'Définissez le thème (Sombre ou Clair) de votre application',
|
||||
SettingsPreferencesTabLanguageSectionTitle: 'Langue',
|
||||
SettingsPreferencesTabLanguageSectionDescription: 'Définissez la langue de votre application',
|
||||
SettingsPreferencesTabDifficultySectionTitle: 'Difficulté',
|
||||
SettingsPreferencesTabDifficultySectionDescription: 'La précision du tempo est de plus en plus élevée',
|
||||
SettingsPreferencesTabDifficultySectionDescription:
|
||||
'La précision du tempo est de plus en plus élevée',
|
||||
SettingsPreferencesTabColorblindModeSectionTitle: 'Mode daltonien',
|
||||
SettingsPreferencesTabColorblindModeSectionDescription: 'Augmente le contraste',
|
||||
SettingsPreferencesTabMicVolumeSectionTitle: 'Volume du micro',
|
||||
SettingsPreferencesTabMicVolumeSectionDescription: 'Réglez le volume de votre micro selon vos préférences',
|
||||
SettingsPreferencesTabMicVolumeSectionDescription:
|
||||
'Réglez le volume de votre micro selon vos préférences',
|
||||
// Notifications Tab
|
||||
SettingsNotificationsTabPushNotificationsSectionTitle: 'Notifications push',
|
||||
SettingsNotificationsTabPushNotificationsSectionDescription: 'Cette notification apparaîtra sur votre appareil en pop-up',
|
||||
SettingsNotificationsTabPushNotificationsSectionDescription:
|
||||
'Cette notification apparaîtra sur votre appareil en pop-up',
|
||||
SettingsNotificationsTabEmailNotificationsSectionTitle: 'Email',
|
||||
SettingsNotificationsTabEmailNotificationsSectionDescription: 'Recevez des mails pour atteindre vos objectifs',
|
||||
SettingsNotificationsTabTrainingReminderSectionTitle: 'Rappel d\'entraînement',
|
||||
SettingsNotificationsTabTrainingReminderSectionDescription: 'Un apprentissage régulier est la clé',
|
||||
SettingsNotificationsTabEmailNotificationsSectionDescription:
|
||||
'Recevez des mails pour atteindre vos objectifs',
|
||||
SettingsNotificationsTabTrainingReminderSectionTitle: "Rappel d'entraînement",
|
||||
SettingsNotificationsTabTrainingReminderSectionDescription:
|
||||
'Un apprentissage régulier est la clé',
|
||||
SettingsNotificationsTabReleaseAlertSectionTitle: 'Alertes de nouvelles sorties',
|
||||
SettingsNotificationsTabReleaseAlertSectionDescription: 'Restez informé de nos mises à jour',
|
||||
// Privacy Tab
|
||||
SettingsPrivacyTabDataCollectionSectionTitle: 'Collecte de données',
|
||||
SettingsPrivacyTabDataCollectionSectionDescription: 'Acceptez-vous la récupération de vos données pour l\'amélioration de Chromacase ?',
|
||||
SettingsPrivacyTabDataCollectionSectionDescription:
|
||||
"Acceptez-vous la récupération de vos données pour l'amélioration de Chromacase ?",
|
||||
SettingsPrivacyTabCustomAdsSectionTitle: 'Publicités personnalisées',
|
||||
SettingsPrivacyTabCustomAdsSectionDescription: 'Afficher les suggestions dans la section des recommandations',
|
||||
SettingsPrivacyTabCustomAdsSectionDescription:
|
||||
'Afficher les suggestions dans la section des recommandations',
|
||||
SettingsPrivacyTabRecommendationsSectionTitle: 'Recommandations',
|
||||
SettingsPrivacyTabRecommendationsSectionDescription: 'Souhaitez-vous recevoir nos conseils et recommandations ?',
|
||||
SettingsPrivacyTabRecommendationsSectionDescription:
|
||||
'Souhaitez-vous recevoir nos conseils et recommandations ?',
|
||||
|
||||
SettingsCategoryProfile: 'Profil',
|
||||
SettingsCategoryPreferences: 'Préférences',
|
||||
@@ -506,7 +532,7 @@ export const fr: typeof en = {
|
||||
SettingsCategoryPiano: 'Piano',
|
||||
|
||||
transformGuestToUserExplanations:
|
||||
"Vous êtes actuellement connecté avec un compte invité. La déconnexion entraînera la perte de vos données. Pour sauvegarder votre progression, veuillez vous inscrire.",
|
||||
'Vous êtes actuellement connecté avec un compte invité. La déconnexion entraînera la perte de vos données. Pour sauvegarder votre progression, veuillez vous inscrire.',
|
||||
SettingsCategoryGuest: 'Invité',
|
||||
SettingsNotificationsEmailNotifications: 'Email',
|
||||
SettingsNotificationsPushNotifications: 'Notifications push',
|
||||
@@ -611,16 +637,16 @@ export const sp: typeof en = {
|
||||
menuNoSongsPlayedYet: 'No hay canciones reproducidas todavía',
|
||||
|
||||
//signup
|
||||
signupPageTitle: "Crear una cuenta",
|
||||
signupPageParagraph: "Aprende a tocar el piano de forma amena y gratuita.",
|
||||
signupLinkLabel: "¿Ya tienes una cuenta? ",
|
||||
signupLinkText: "Iniciar sesión.",
|
||||
signupPageTitle: 'Crear una cuenta',
|
||||
signupPageParagraph: 'Aprende a tocar el piano de forma amena y gratuita.',
|
||||
signupLinkLabel: '¿Ya tienes una cuenta? ',
|
||||
signupLinkText: 'Iniciar sesión.',
|
||||
|
||||
//signin
|
||||
signinPageTitle: "Bienvenido !",
|
||||
signinPageParagraph: "Continúa con Google o introduce tus datos.",
|
||||
signinLinkLabel: "¿No tienes una cuenta? ",
|
||||
signinLinkText: "Regístrate gratis.",
|
||||
signinPageTitle: 'Bienvenido !',
|
||||
signinPageParagraph: 'Continúa con Google o introduce tus datos.',
|
||||
signinLinkLabel: '¿No tienes una cuenta? ',
|
||||
signinLinkText: 'Regístrate gratis.',
|
||||
|
||||
//search
|
||||
allFilter: 'Todos',
|
||||
@@ -725,27 +751,34 @@ export const sp: typeof en = {
|
||||
settingsProfileTabVerifiedSectionVerifiedText: 'verificado',
|
||||
settingsProfileTabVerifiedSectionNotVerifiedText: 'no verificado',
|
||||
settingsProfileTabVerifiedSectionVerificationToast: 'Correo de verificación enviado',
|
||||
settingsProfileTabVerifiedSectionVerificationToastError: 'Error de envío del correo de verificación',
|
||||
settingsProfileTabVerifiedSectionVerificationToastError:
|
||||
'Error de envío del correo de verificación',
|
||||
settingsProfileTabAvatarSectionTitle: 'Avatar',
|
||||
settingsProfileTabAvatarSectionDescription: 'Cambia tu foto de perfil',
|
||||
settingsProfileTabAvatarSectionChangeItText: 'Cambiar',
|
||||
settingsProfileTabAvatarSectionUpdateToast: 'Actualización correcta',
|
||||
settingsProfileTabAvatarSectionUpdateToastError: 'Actualización fallida',
|
||||
settingsProfileTabChangeEmailSectionTitle: 'Cambiar correo electrónico',
|
||||
settingsProfileTabChangeEmailSectionDescription: 'Introduce tu correo electrónico actual y establece tu nuevo correo electrónico',
|
||||
settingsProfileTabChangeEmailSectionDescription:
|
||||
'Introduce tu correo electrónico actual y establece tu nuevo correo electrónico',
|
||||
settingsProfileTabChangePasswordSectionTitle: 'Cambiar contraseña',
|
||||
settingsProfileTabChangePasswordSectionDescription: 'Introduce tu contraseña actual y establece tu nueva contraseña',
|
||||
settingsProfileTabChangePasswordSectionDescription:
|
||||
'Introduce tu contraseña actual y establece tu nueva contraseña',
|
||||
// Premium Tab
|
||||
settingsPremiumTabPremiumAccountSectionTitle: 'Cuenta premium',
|
||||
settingsPremiumTabPremiumAccountSectionDescription: 'Personalización premium y herramientas para llevarte al siguiente nivel',
|
||||
settingsPremiumTabPremiumAccountSectionDescription:
|
||||
'Personalización premium y herramientas para llevarte al siguiente nivel',
|
||||
settingsPremiumTabPianoMagiqueSectionTitle: 'Piano Mágico',
|
||||
settingsPremiumTabPianoMagiqueSectionDescription: 'Hace aparecer luz en el piano durante los juegos',
|
||||
settingsPremiumTabPianoMagiqueSectionHelper: 'Debes poseer el módulo de luz Chromacase para usar esta función',
|
||||
settingsPremiumTabPianoMagiqueSectionDescription:
|
||||
'Hace aparecer luz en el piano durante los juegos',
|
||||
settingsPremiumTabPianoMagiqueSectionHelper:
|
||||
'Debes poseer el módulo de luz Chromacase para usar esta función',
|
||||
settingsPremiumTabThemePianoSectionTitle: 'Tema de piano',
|
||||
settingsPremiumTabThemePianoSectionDescription: 'Establece el tema de tu piano',
|
||||
// Preferences Tab
|
||||
SettingsPreferencesTabThemeSectionTitle: 'Tema',
|
||||
SettingsPreferencesTabThemeSectionDescription: 'Establece el tema (Oscuro o Claro) de tu aplicación',
|
||||
SettingsPreferencesTabThemeSectionDescription:
|
||||
'Establece el tema (Oscuro o Claro) de tu aplicación',
|
||||
SettingsPreferencesTabLanguageSectionTitle: 'Idioma',
|
||||
SettingsPreferencesTabLanguageSectionDescription: 'Establece el idioma de tu aplicación',
|
||||
SettingsPreferencesTabDifficultySectionTitle: 'Dificultad',
|
||||
@@ -753,23 +786,31 @@ export const sp: typeof en = {
|
||||
SettingsPreferencesTabColorblindModeSectionTitle: 'Modo para daltónicos',
|
||||
SettingsPreferencesTabColorblindModeSectionDescription: 'Aumenta el contraste',
|
||||
SettingsPreferencesTabMicVolumeSectionTitle: 'Volumen del micrófono',
|
||||
SettingsPreferencesTabMicVolumeSectionDescription: 'Ajusta el volumen de tu micrófono según tus preferencias',
|
||||
SettingsPreferencesTabMicVolumeSectionDescription:
|
||||
'Ajusta el volumen de tu micrófono según tus preferencias',
|
||||
// Notifications Tab
|
||||
SettingsNotificationsTabPushNotificationsSectionTitle: 'Notificaciones push',
|
||||
SettingsNotificationsTabPushNotificationsSectionDescription: 'Esta notificación aparecerá en tu dispositivo como un pop-up',
|
||||
SettingsNotificationsTabPushNotificationsSectionDescription:
|
||||
'Esta notificación aparecerá en tu dispositivo como un pop-up',
|
||||
SettingsNotificationsTabEmailNotificationsSectionTitle: 'Correo electrónico',
|
||||
SettingsNotificationsTabEmailNotificationsSectionDescription: 'Recibe correos electrónicos para alcanzar tus objetivos',
|
||||
SettingsNotificationsTabEmailNotificationsSectionDescription:
|
||||
'Recibe correos electrónicos para alcanzar tus objetivos',
|
||||
SettingsNotificationsTabTrainingReminderSectionTitle: 'Recordatorio de entrenamiento',
|
||||
SettingsNotificationsTabTrainingReminderSectionDescription: 'El entrenamiento regular es la clave',
|
||||
SettingsNotificationsTabTrainingReminderSectionDescription:
|
||||
'El entrenamiento regular es la clave',
|
||||
SettingsNotificationsTabReleaseAlertSectionTitle: 'Alertas de nuevos lanzamientos',
|
||||
SettingsNotificationsTabReleaseAlertSectionDescription: 'Mantente informado de nuestras actualizaciones',
|
||||
SettingsNotificationsTabReleaseAlertSectionDescription:
|
||||
'Mantente informado de nuestras actualizaciones',
|
||||
// Privacy Tab
|
||||
SettingsPrivacyTabDataCollectionSectionTitle: 'Recolección de datos',
|
||||
SettingsPrivacyTabDataCollectionSectionDescription: '¿Aceptas la recopilación de tus datos para la mejora de Chromacase?',
|
||||
SettingsPrivacyTabDataCollectionSectionDescription:
|
||||
'¿Aceptas la recopilación de tus datos para la mejora de Chromacase?',
|
||||
SettingsPrivacyTabCustomAdsSectionTitle: 'Anuncios personalizados',
|
||||
SettingsPrivacyTabCustomAdsSectionDescription: 'Muestra sugerencias en la sección de recomendaciones',
|
||||
SettingsPrivacyTabCustomAdsSectionDescription:
|
||||
'Muestra sugerencias en la sección de recomendaciones',
|
||||
SettingsPrivacyTabRecommendationsSectionTitle: 'Recomendaciones',
|
||||
SettingsPrivacyTabRecommendationsSectionDescription: '¿Deseas recibir nuestros consejos y recomendaciones?',
|
||||
SettingsPrivacyTabRecommendationsSectionDescription:
|
||||
'¿Deseas recibir nuestros consejos y recomendaciones?',
|
||||
|
||||
SettingsCategoryProfile: 'Perfil',
|
||||
SettingsCategoryPreferences: 'Preferencias',
|
||||
|
||||
@@ -76,7 +76,13 @@ const ProfileView = (props: RouteProps<{}>) => {
|
||||
<Text style={{ paddingBottom: 10, fontWeight: 'bold' }}>
|
||||
Account created on {userQuery.data.data.createdAt.toLocaleDateString()}
|
||||
</Text>
|
||||
<Wrap style={{ flexDirection: 'row', alignItems: 'center', paddingBottom: 10 }}>
|
||||
<Wrap
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingBottom: 10,
|
||||
}}
|
||||
>
|
||||
<Text style={{ paddingRight: 20 }}>
|
||||
Your client ID is {userQuery.data.id}
|
||||
</Text>
|
||||
@@ -93,7 +99,7 @@ const ProfileView = (props: RouteProps<{}>) => {
|
||||
flex={1}
|
||||
/>
|
||||
</Row>
|
||||
<ScoreGraph/>
|
||||
<ScoreGraph />
|
||||
</Flex>
|
||||
</ScaffoldCC>
|
||||
);
|
||||
|
||||
@@ -110,4 +110,4 @@ const SearchView = (props: RouteProps<SearchViewProps>) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchView;
|
||||
export default SearchView;
|
||||
|
||||
@@ -61,11 +61,11 @@ const SigninView = () => {
|
||||
|
||||
return (
|
||||
<ScaffoldAuth
|
||||
title={translate("signinPageTitle")}
|
||||
description={translate("signinPageParagraph")}
|
||||
title={translate('signinPageTitle')}
|
||||
description={translate('signinPageParagraph')}
|
||||
form={[
|
||||
<TextFormField
|
||||
style={{width: '100%', flex: 1}}
|
||||
style={{ width: '100%', flex: 1 }}
|
||||
key={'signin-form-1'}
|
||||
error={formData.username.error}
|
||||
icon={User}
|
||||
|
||||
@@ -76,8 +76,8 @@ const SignupView = () => {
|
||||
|
||||
return (
|
||||
<ScaffoldAuth
|
||||
title={translate("signupPageTitle")}
|
||||
description={translate("signupPageParagraph")}
|
||||
title={translate('signupPageTitle')}
|
||||
description={translate('signupPageParagraph')}
|
||||
form={[
|
||||
<TextFormField
|
||||
key={'signup-form-1'}
|
||||
|
||||
@@ -17,13 +17,15 @@ const NotificationsSettings = () => {
|
||||
}
|
||||
return (
|
||||
<ElementList
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
elements={[
|
||||
{
|
||||
type: 'toggle',
|
||||
icon: <MonitorMobbile size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
title: translate('SettingsNotificationsTabPushNotificationsSectionTitle'),
|
||||
description: translate('SettingsNotificationsTabPushNotificationsSectionDescription'),
|
||||
description: translate(
|
||||
'SettingsNotificationsTabPushNotificationsSectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: settings.data.notifications.pushNotif,
|
||||
onToggle: () => {
|
||||
@@ -39,7 +41,9 @@ const NotificationsSettings = () => {
|
||||
type: 'toggle',
|
||||
icon: <Send2 size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
title: translate('SettingsNotificationsTabEmailNotificationsSectionTitle'),
|
||||
description: translate('SettingsNotificationsTabEmailNotificationsSectionDescription'),
|
||||
description: translate(
|
||||
'SettingsNotificationsTabEmailNotificationsSectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: settings.data.notifications.emailNotif,
|
||||
onToggle: () => {
|
||||
@@ -55,7 +59,9 @@ const NotificationsSettings = () => {
|
||||
type: 'toggle',
|
||||
icon: <Calendar1 size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
title: translate('SettingsNotificationsTabTrainingReminderSectionTitle'),
|
||||
description: translate('SettingsNotificationsTabTrainingReminderSectionDescription'),
|
||||
description: translate(
|
||||
'SettingsNotificationsTabTrainingReminderSectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: settings.data.notifications.trainNotif,
|
||||
onToggle: () => {
|
||||
@@ -71,7 +77,9 @@ const NotificationsSettings = () => {
|
||||
type: 'toggle',
|
||||
icon: <Warning2 size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
title: translate('SettingsNotificationsTabReleaseAlertSectionTitle'),
|
||||
description: translate('SettingsNotificationsTabReleaseAlertSectionDescription'),
|
||||
description: translate(
|
||||
'SettingsNotificationsTabReleaseAlertSectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: settings.data.notifications.newSongNotif,
|
||||
onToggle: () => {
|
||||
|
||||
@@ -17,7 +17,7 @@ const PreferencesSettings = () => {
|
||||
const colorScheme = useColorScheme();
|
||||
const color = colorScheme === 'light' ? '#000' : '#fff';
|
||||
return (
|
||||
<Column space={4} style={{width: '100%'}}>
|
||||
<Column space={4} style={{ width: '100%' }}>
|
||||
<ElementList
|
||||
elements={[
|
||||
{
|
||||
@@ -64,7 +64,9 @@ const PreferencesSettings = () => {
|
||||
icon: <Rank size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
type: 'dropdown',
|
||||
title: translate('SettingsPreferencesTabDifficultySectionTitle'),
|
||||
description: translate('SettingsPreferencesTabDifficultySectionDescription'),
|
||||
description: translate(
|
||||
'SettingsPreferencesTabDifficultySectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: settings.difficulty,
|
||||
defaultValue: 'medium',
|
||||
@@ -90,7 +92,9 @@ const PreferencesSettings = () => {
|
||||
icon: <Colorfilter size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
type: 'toggle',
|
||||
title: translate('SettingsPreferencesTabColorblindModeSectionTitle'),
|
||||
description: translate('SettingsPreferencesTabColorblindModeSectionDescription'),
|
||||
description: translate(
|
||||
'SettingsPreferencesTabColorblindModeSectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: settings.colorBlind,
|
||||
onToggle: () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ const PrivacySettings = () => {
|
||||
}
|
||||
return (
|
||||
<ElementList
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
elements={[
|
||||
{
|
||||
type: 'toggle',
|
||||
@@ -32,9 +32,7 @@ const PrivacySettings = () => {
|
||||
data: {
|
||||
value: settings.dataCollection,
|
||||
onToggle: () =>
|
||||
dispatch(
|
||||
updateSettings({ dataCollection: !settings.dataCollection })
|
||||
),
|
||||
dispatch(updateSettings({ dataCollection: !settings.dataCollection })),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ const PremiumSettings = () => {
|
||||
const color = colorScheme === 'light' ? '#000' : '#fff';
|
||||
return (
|
||||
<ElementList
|
||||
style={{width: '100%'}}
|
||||
style={{ width: '100%' }}
|
||||
elements={[
|
||||
{
|
||||
icon: <Star1 size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
|
||||
@@ -36,7 +36,7 @@ const ProfileSettings = () => {
|
||||
const colorScheme = useColorScheme();
|
||||
const color = colorScheme === 'light' ? '#000' : '#fff';
|
||||
return (
|
||||
<Column space={4} style={{width: '100%'}}>
|
||||
<Column space={4} style={{ width: '100%' }}>
|
||||
<ElementList
|
||||
elements={[
|
||||
{
|
||||
@@ -45,7 +45,11 @@ const ProfileSettings = () => {
|
||||
title: translate('settingsProfileTabGoogleSectionTitle'),
|
||||
description: translate('settingsProfileTabGoogleSectionDescription'),
|
||||
data: {
|
||||
text: translate(user.googleID ? 'settingsProfileTabGoogleSectionLinkedText' : 'settingsProfileTabGoogleSectionNotLinkedText')
|
||||
text: translate(
|
||||
user.googleID
|
||||
? 'settingsProfileTabGoogleSectionLinkedText'
|
||||
: 'settingsProfileTabGoogleSectionNotLinkedText'
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -54,20 +58,28 @@ const ProfileSettings = () => {
|
||||
title: translate('settingsProfileTabVerifiedSectionTitle'),
|
||||
description: translate('settingsProfileTabVerifiedSectionDescription'),
|
||||
data: {
|
||||
text: translate(user.emailVerified ? 'settingsProfileTabVerifiedSectionVerifiedText' : 'settingsProfileTabVerifiedSectionNotVerifiedText'),
|
||||
text: translate(
|
||||
user.emailVerified
|
||||
? 'settingsProfileTabVerifiedSectionVerifiedText'
|
||||
: 'settingsProfileTabVerifiedSectionNotVerifiedText'
|
||||
),
|
||||
onPress: user.emailVerified
|
||||
? undefined
|
||||
: () =>
|
||||
API.fetch({ route: '/auth/reverify', method: 'PUT' })
|
||||
.then(() =>
|
||||
Toast.show({
|
||||
description: translate('settingsProfileTabVerifiedSectionVerificationToast')
|
||||
description: translate(
|
||||
'settingsProfileTabVerifiedSectionVerificationToast'
|
||||
),
|
||||
})
|
||||
)
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
Toast.show({
|
||||
description: translate('settingsProfileTabVerifiedSectionVerificationToastError')
|
||||
description: translate(
|
||||
'settingsProfileTabVerifiedSectionVerificationToastError'
|
||||
),
|
||||
});
|
||||
}),
|
||||
},
|
||||
@@ -94,12 +106,18 @@ const ProfileSettings = () => {
|
||||
.then(() => {
|
||||
userQuery.refetch();
|
||||
Toast.show({
|
||||
description: translate('settingsProfileTabAvatarSectionUpdateToast'),
|
||||
description: translate(
|
||||
'settingsProfileTabAvatarSectionUpdateToast'
|
||||
),
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
Toast.show({ description: translate('settingsProfileTabAvatarSectionUpdateToastError')});
|
||||
Toast.show({
|
||||
description: translate(
|
||||
'settingsProfileTabAvatarSectionUpdateToastError'
|
||||
),
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -125,7 +143,9 @@ const ProfileSettings = () => {
|
||||
icon: <PasswordCheck size="24" color={color} style={{ minWidth: 24 }} />,
|
||||
type: 'sectionDropdown',
|
||||
title: translate('settingsProfileTabChangePasswordSectionTitle'),
|
||||
description: translate('settingsProfileTabChangePasswordSectionDescription'),
|
||||
description: translate(
|
||||
'settingsProfileTabChangePasswordSectionDescription'
|
||||
),
|
||||
data: {
|
||||
value: true,
|
||||
section: [
|
||||
@@ -140,7 +160,11 @@ const ProfileSettings = () => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<LogoutButtonCC isGuest={user.isGuest} style={{with: 'fit-content'}} buttonType={'filled'}/>
|
||||
<LogoutButtonCC
|
||||
isGuest={user.isGuest}
|
||||
style={{ with: 'fit-content' }}
|
||||
buttonType={'filled'}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -89,8 +89,8 @@ const SetttingsNavigator = (props: RouteProps<{}>) => {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: colors.primary[500],
|
||||
}}
|
||||
activeColor={ colorScheme === 'light' ? '#000' : '#fff'}
|
||||
inactiveColor={ colorScheme === 'light' ? 'rgba(0,0,0,0.7)' : 'rgba(255,255,255,0.7)'}
|
||||
activeColor={colorScheme === 'light' ? '#000' : '#fff'}
|
||||
inactiveColor={colorScheme === 'light' ? 'rgba(0,0,0,0.7)' : 'rgba(255,255,255,0.7)'}
|
||||
indicatorStyle={{ backgroundColor: colors.primary[500] }}
|
||||
renderIcon={(
|
||||
scene: Scene<Route> & {
|
||||
@@ -100,13 +100,25 @@ const SetttingsNavigator = (props: RouteProps<{}>) => {
|
||||
) => {
|
||||
const tabHeader = getTabData(scene.route!.key);
|
||||
return (
|
||||
<tabHeader.icon size="18" color="#6075F9" variant={scene.focused ? "Bold" : "Outline"} />
|
||||
<tabHeader.icon
|
||||
size="18"
|
||||
color="#6075F9"
|
||||
variant={scene.focused ? 'Bold' : 'Outline'}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
renderLabel={({ route, color }) =>
|
||||
layout.width > 1100 && (
|
||||
<Text style={{ color: color, paddingLeft: 10, overflow: 'hidden' }}>
|
||||
{translate(route.title as 'settingsTabProfile' | 'settingsTabPremium' | 'settingsTabPreferences' | 'settingsTabNotifications' | 'settingsTabPrivacy' | 'settingsTabPiano')}
|
||||
{translate(
|
||||
route.title as
|
||||
| 'settingsTabProfile'
|
||||
| 'settingsTabPremium'
|
||||
| 'settingsTabPreferences'
|
||||
| 'settingsTabNotifications'
|
||||
| 'settingsTabPrivacy'
|
||||
| 'settingsTabPiano'
|
||||
)}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
@@ -123,7 +135,7 @@ const SetttingsNavigator = (props: RouteProps<{}>) => {
|
||||
paddingTop: 32,
|
||||
padding: 20,
|
||||
maxWidth: 850,
|
||||
width: '100%'
|
||||
width: '100%',
|
||||
}}
|
||||
style={{ height: 'fit-content' }}
|
||||
renderTabBar={renderTabBar}
|
||||
|
||||
Reference in New Issue
Block a user