Feature/adc/#55 settings screen (#77)

* #55 - created settings views with sub navigation + preference page

* #55 - navigation done

* #55 - views and translations done

* #55 - merge main

* #55 - user settings interface update

* #55 - mobile view fix

Co-authored-by: Arthi-chaud <arthur.jamet@gmail.com>
This commit is contained in:
Amaury
2022-10-18 19:14:08 +02:00
committed by GitHub
parent ca552c9535
commit 0d17119cf4
10 changed files with 348 additions and 86 deletions
+3 -1
View File
@@ -2,6 +2,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import React from 'react';
import AuthenticationView from './views/AuthenticationView';
import HomeView from './views/HomeView';
import SetttingsNavigator from './views/SettingsView';
import { NavigationContainer } from '@react-navigation/native';
import { useSelector } from './state/Store';
import SongLobbyView from './views/SongLobbyView';
@@ -10,7 +11,8 @@ import { translate } from './i18n/i18n';
const Stack = createNativeStackNavigator();
export const protectedRoutes = <>
<Stack.Screen name="Home" component={HomeView} options={{ title: translate('welcome') }}/>
<Stack.Screen name="Home" component={HomeView} options={{ title: translate('welcome') }} />
<Stack.Screen name="Settings" component={SetttingsNavigator} options={{ title: 'Settings' }} />
<Stack.Screen name="Song" component={SongLobbyView} options={{ title: translate('play') }} />
</>;
+77 -1
View File
@@ -11,6 +11,22 @@ export const en = {
chapters: 'Chapters',
bestScore: 'Best Score',
lastScore: 'Last Score',
langBtn: 'Language',
diffBtn: 'Difficulty',
backBtn: 'Back',
settingsBtn: 'Settings',
prefBtn: 'Preferences',
notifBtn: 'Notifications',
privBtn: 'Privacy',
changepasswdBtn: 'Change Password',
changeemailBtn: 'Change Email',
googleacctBtn: 'Google Account',
easy: 'Beginner',
medium: 'Intermediate',
hard: 'Pro',
dark: 'Dark',
system: 'System',
light: 'Light',
play: 'Play',
goNextStep: 'Step Up!',
mySkillsToImprove: "My Competencies to work on",
@@ -42,10 +58,60 @@ export const fr: typeof en = {
chapters: 'Chapitres',
bestScore: 'Meilleur Score',
lastScore: 'Dernier Score',
langBtn: 'Langage',
diffBtn: 'Difficulté',
backBtn: 'Retour',
prefBtn: 'Préférences',
notifBtn: 'Notifications',
privBtn: 'Confidentialité',
changepasswdBtn: 'Changer le mot de pass',
changeemailBtn: 'Change l\'email',
googleacctBtn: 'Compte Google',
easy: 'Débutant',
medium: 'Intermédiaire',
hard: 'Avancé',
dark: 'Foncé',
system: 'Système',
light: 'Clair',
play: 'Jouer',
settingsBtn: "Réglages",
goNextStep: "Prochaine Etape",
mySkillsToImprove: "Mes Skills",
recentlyPlayed: "Joués récemment",
search: "Rechercher",
lastSearched: "Dernières recherches",
levelProgress: "Niveau",
pedalsCompetency: "Pédales",
rightHandCompetency: "Main droite",
leftHandCompetency: "Main gauche",
accuracyCompetency: "Justesse",
arpegeCompetency: "Arpege",
chordsCompetency: "Accords"
};
export const sp: typeof en = {
welcome: 'Benvenido a Chromacase',
signoutBtn: 'Desconectarse',
signinBtn: 'Connectarse',
langBtn: 'Langua',
diffBtn: 'Dificultad',
backBtn: 'Volver',
settingsBtn: 'Settings',
prefBtn: 'Préférences',
notifBtn: 'Notifications',
privBtn: 'Confidentialité',
changepasswdBtn: 'Changer le mot de pass',
changeemailBtn: 'Change l\'email',
googleacctBtn: 'Compte Google',
easy: 'Principiante',
medium: 'Intermedio',
hard: 'Avanzado',
dark: 'Oscuro',
system: 'Sistema',
light: 'Claro',
play: 'Jouer',
goNextStep: "Passer à l'étape supérieure",
mySkillsToImprove: 'Mes Competences à améliorer',
recentlyPlayed: "Récemment joués",
lastSearched: "Dernières recherches",
search: 'Rechercher',
levelProgress: 'bonnes notes',
@@ -57,4 +123,14 @@ export const fr: typeof en = {
accuracyCompetency: 'Précision',
arpegeCompetency: 'Arpèges',
chordsCompetency: 'Accords',
welcomeMessage: "Bienvenue",
changeLanguageBtn: "Changer de langue",
searchBtn: "Rechercher",
playBtn: "Jouer",
songPageBtn: "Chanson",
level: "Niveau",
chapters: "Chapitres",
bestScore: "Meilleur Score",
lastScore: "Dernier score",
recentlyPlayed: "Joués récemment"
};
+5 -2
View File
@@ -1,9 +1,9 @@
import { en, fr } from './Translations';
import { en, fr, sp } from './Translations';
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
export type AvailableLanguages = 'en' | 'fr';
export type AvailableLanguages = 'en' | 'fr' | 'sp';
export const DefaultLanguage: AvailableLanguages = 'en';
i18n
@@ -15,6 +15,9 @@ i18n
},
fr: {
translation: fr
},
sp: {
translation: sp
}
},
lng: DefaultLanguage,
+19 -1
View File
@@ -1,5 +1,23 @@
interface UserSettings {
preferences: {
deviceId: number,
micVolume: number,
theme: 'light' | 'dark' | 'system',
lang: 'fr' | 'en' | 'sp',
difficulty: 'beg' | 'inter' | 'pro',
colorBlind: boolean
},
notifications: {
pushNotif: boolean,
emailNotif: boolean,
trainNotif: boolean,
newSongNotif: boolean
},
privacy: {
dataCollection: boolean,
customAdd: boolean,
recommendation: boolean
}
}
export default UserSettings
+1 -1
View File
@@ -22,7 +22,7 @@
"@types/react-query": "^1.2.9",
"@types/react-test-renderer": "^18.0.0",
"expo": "~45.0.0",
"expo-asset": "^8.6.1",
"expo-asset": "~8.5.0",
"expo-dev-client": "~1.0.0",
"expo-status-bar": "~1.3.0",
"format-duration": "^2.0.0",
+4 -7
View File
@@ -74,8 +74,6 @@ const HomeView = () => {
</Box>
</Box>
<Divider orientation="vertical" w="1"/>
<Box flex={1} padding={5}>
<SongCardGrid
heading={translate('recentlyPlayed')}
@@ -92,16 +90,12 @@ const HomeView = () => {
</Box>
<VStack padding={5} flex={1} space={10}>
<Box style={{flexDirection: 'row'}}>
<Divider orientation="vertical" w="1"/>
<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('search')}</Button>
</Box>
<SongCardGrid
maxItemPerRow={2}
heading={translate('lastSearched')}
@@ -114,6 +108,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('settingsBtn')}</Button>
</Box>
</VStack>
</Box>
</Box>
+13
View File
@@ -0,0 +1,13 @@
import React from 'react';
import { Provider } from 'react-redux';
import TestRenderer from 'react-test-renderer';
import store from '../state/Store';
import AuthenticationView from '../views/AuthenticationView';
describe('<AuthenticationView />', () => {
it('has 3 children', () => {
const tree = TestRenderer.create(<Provider store={store}><AuthenticationView /></Provider>).toJSON();
expect(tree.children.length).toBe(3);
});
});
+225
View File
@@ -0,0 +1,225 @@
import React from 'react';
import { View } from 'react-native';
import { Center, Button, Text, Switch, Slider, Select, Heading } from "native-base";
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { unsetUserToken } from '../state/UserSlice';
import { useDispatch, useSelector } from "react-redux";
import { useLanguage } from "../state/LanguageSlice";
import i18n, { AvailableLanguages, DefaultLanguage, translate } from "../i18n/i18n";
const SettingsStack = createNativeStackNavigator();
const MainView = ({navigation}) => {
const dispatch = useDispatch();
return (
<Center style={{ flex: 1}}>
<Button variant='ghost' onPress={() => navigation.navigate('Preferences')}>
{ translate('prefBtn')}
</Button>
<Button variant='ghost' onPress={() => navigation.navigate('Notifications')}>
{ translate('notifBtn')}
</Button>
<Button variant='ghost' onPress={() => navigation.navigate('Privacy')}>
{ translate('privBtn')}
</Button>
<Button variant='ghost' onPress={() => navigation.navigate('ChangePassword')}>
{ translate('changepasswdBtn')}
</Button>
<Button variant='ghost' onPress={() => navigation.navigate('ChangeEmail')}>
{ translate('changeemailBtn')}
</Button>
<Button variant='ghost' onPress={() => navigation.navigate('GoogleAccount')}>
{ translate('googleacctBtn')}
</Button>
<Button variant='ghost' onPress={() => dispatch(unsetUserToken())} >
{ translate('signoutBtn')}
</Button>
</Center>
)
}
const PreferencesView = ({navigation}) => {
const dispatch = useDispatch();
const language: AvailableLanguages = useSelector((state) => state.language.value);
return (
<Center style={{ flex: 1}}>
<Heading style={{ textAlign: "center" }}>{ translate('prefBtn')}</Heading>
<Button onPress={() => navigation.navigate('Main')} style={{ margin: 10}}>{ translate('backBtn') }</Button>
<View style={{margin: 20, maxHeight: 100, maxWidth: 500, width: '80%'}}>
<Select selectedValue={undefined}
placeholder={'Theme'}
style={{ alignSelf: 'center'}}
// onValueChange={(itemValue, itemIndex) => switch themes}
>
<Select.Item label={ translate('dark') } value='dark'/>
<Select.Item label={ translate('light') } value='light'/>
<Select.Item label={ translate('system') } value='system'/>
</Select>
</View>
<View style={{margin: 20, maxHeight: 100, maxWidth: 500, width: '80%'}}>
<Select selectedValue={language}
placeholder={translate('langBtn')}
style={{ alignSelf: 'center'}}
onValueChange={(itemValue: AvailableLanguages, itemIndex) => {
let newLanguage = DefaultLanguage;
newLanguage = itemValue;Heading
dispatch(useLanguage(newLanguage));
}}>
<Select.Item label='Français' value='fr'/>
<Select.Item label='English' value='en'/>
<Select.Item label='Italiano' value='it'/>
<Select.Item label='Espanol' value='sp'/>
</Select>
</View>
<View style={{margin: 20, maxHeight: 100, maxWidth: 500, width: '80%'}}>
<Select selectedValue={undefined}
placeholder={ translate('diffBtn') }
style={{ height: 50, width: 150, alignSelf: 'center'}}
// onValueChange={(itemValue, itemIndex) => change level}
>
<Select.Item label={ translate('easy') } value='easy'/>
<Select.Item label={ translate('medium') } value='medium'/>
<Select.Item label={ translate('hard') } value='hard'/>
</Select>
</View>
<View style={{margin: 20}}>
<Text style={{ textAlign: "center" }}>Color blind mode</Text>
<Switch style={{ alignSelf: 'center'}} colorScheme="primary"/>
</View>
<View style={{margin: 20, maxHeight: 100, maxWidth: 500, width: '80%'}}>
<Text style={{ textAlign: "center" }}>Mic volume</Text>
<Slider defaultValue={50} minValue={0} maxValue={1000} accessibilityLabel="hello world" step={10}>
<Slider.Track>
<Slider.FilledTrack/>
</Slider.Track>
<Slider.Thumb/>
</Slider>
</View>
<View style={{margin: 20, maxHeight: 100, maxWidth: 500, width: '80%'}}>
<Select selectedValue={undefined}
placeholder={'Device'}
style={{ height: 50, width: 150, alignSelf: 'center'}}
// onValueChange={(itemValue, itemIndex) => change device}
>
<Select.Item label='Mic_0' value='0'/>
<Select.Item label='Mic_1' value='1'/>
<Select.Item label='Mic_2' value='2'/>
</Select>
</View>
</Center>
)
}
const NotificationsView = ({navigation}) => {
return (
<Center style={{ flex: 1, justifyContent: 'center' }}>
<Heading style={{ textAlign: "center" }}>{ translate('notifBtn')}</Heading>
<Button style={{ margin: 10}} onPress={() => navigation.navigate('Main')} >{ translate('backBtn') }</Button>
<View style={{margin: 20}} >
<Text style={{ textAlign: "center" }}>Push notifications</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
<View style={{margin: 20}}>
<Text style={{ textAlign: "center" }}>Email notifications</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
<View style={{margin: 20}}>
<Text style={{ textAlign: "center" }}>Training reminder</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
<View style={{margin: 20}}>
<Text style={{ textAlign: "center" }}>New songs</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
</Center>
)
}
const PrivacyView = ({navigation}) => {
return (
<Center style={{ flex: 1}}>
<Heading style={{ textAlign: "center" }}>{ translate('privBtn')}</Heading>
<Button onPress={() => navigation.navigate('Main')} style={{ margin: 10 }}>{ translate('backBtn') }</Button>
<View style={{margin: 20}} >
<Text style={{ textAlign: "center" }}>Data Collection</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
<View style={{margin: 20}}>
<Text style={{ textAlign: "center" }}>Custom Adds</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
<View style={{margin: 20}}>
<Text style={{ textAlign: "center" }}>Recommendations</Text>
<Switch style={{ alignSelf: 'center', margin: 10 }} colorScheme="primary"/>
</View>
</Center>
)
}
const ChangePasswordView = ({navigation}) => {
return (
<Center style={{ flex: 1}}>
<Button onPress={() => navigation.navigate('Main')}>Back</Button>
<Text>ChangePassword</Text>
</Center>
)
}
const ChangeEmailView = ({navigation}) => {
return (
<Center style={{ flex: 1}}>
<Button onPress={() => navigation.navigate('Main')}>Back</Button>
<Text>ChangeEmail</Text>
</Center>
)
}
const GoogleAccountView = ({navigation}) => {
return (
<Center style={{ flex: 1}}>
<Button onPress={() => navigation.navigate('Main')}>Back</Button>
<Text>GoogleAccount</Text>
</Center>
)
}
const SetttingsNavigator = () => {
return (
<SettingsStack.Navigator initialRouteName='Main' screenOptions={{headerShown: false}}>
<SettingsStack.Screen name='Main' component={MainView} />
<SettingsStack.Screen name='Preferences' component={PreferencesView} />
<SettingsStack.Screen name='Notifications' component={NotificationsView} />
<SettingsStack.Screen name='Privacy' component={PrivacyView} />
<SettingsStack.Screen name='ChangePassword' component={ChangePasswordView} />
<SettingsStack.Screen name='ChangeEmail' component={ChangeEmailView} />
<SettingsStack.Screen name='GoogleAccount' component={GoogleAccountView} />
</SettingsStack.Navigator>
)
}
export default SetttingsNavigator;
+1 -1
View File
@@ -41,7 +41,7 @@ const SongLobbyView = () => {
<Box flex={1}>
<Text bold fontSize='lg'>{songQuery.data!.title}</Text>
<Text>{'3:20'} - {translate('level')} { chaptersQuery.data!.reduce((a, b) => a + b.difficulty, 0) / chaptersQuery.data!.length }</Text>
<Button width='fit-content' rightIcon={<Icon as={Ionicons} name="play-outline"/>}>{ translate('playBtn') }</Button>
<Button width='auto' rightIcon={<Icon as={Ionicons} name="play-outline"/>}>{ translate('playBtn') }</Button>
</Box>
</Box>
</Box>
-72
View File
@@ -1197,37 +1197,11 @@
xcode "^3.0.1"
xml2js "0.4.23"
"@expo/config-plugins@~5.0.0", "@expo/config-plugins@~5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-5.0.1.tgz#66bc8d15785bdcd3598e466344f8c0518390179d"
integrity sha512-1OfnsOrfeSkB0VZfT01UjQ5Uq6p+yYbq8yNkj0e99K/6NLHpyvIxj+5tZIV0nQXgkOcqBIABL2uA7lwB8CkaBQ==
dependencies:
"@expo/config-types" "^46.0.0"
"@expo/json-file" "8.2.36"
"@expo/plist" "0.0.18"
"@expo/sdk-runtime-versions" "^1.0.0"
"@react-native/normalize-color" "^2.0.0"
chalk "^4.1.2"
debug "^4.3.1"
find-up "~5.0.0"
getenv "^1.0.0"
glob "7.1.6"
resolve-from "^5.0.0"
semver "^7.3.5"
slash "^3.0.0"
xcode "^3.0.1"
xml2js "0.4.23"
"@expo/config-types@^45.0.0":
version "45.0.0"
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-45.0.0.tgz#963c2fdce8fbcbd003758b92ed8a25375f437ef6"
integrity sha512-/QGhhLWyaGautgEyU50UJr5YqKJix5t77ePTwreOVAhmZH+ff3nrrtYTTnccx+qF08ZNQmfAyYMCD3rQfzpiJA==
"@expo/config-types@^46.0.0", "@expo/config-types@^46.0.1":
version "46.0.2"
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-46.0.2.tgz#191f225ebfcbe624868ddc40efae79593f948dd8"
integrity sha512-PXkmOgNwRyBfgVT1HmFZhfh3Qm7WKKyV6mk3/5HJ/LzPh1t+Zs2JrWX8U2YncTLV1QzV7nV8tnkyvszzqnZEzQ==
"@expo/config@6.0.26", "@expo/config@^6.0.14", "@expo/config@~6.0.23":
version "6.0.26"
resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.26.tgz#2c9255ab618a354c8c8bd97366d60992ed21195f"
@@ -1245,23 +1219,6 @@
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/config@~7.0.0":
version "7.0.1"
resolved "https://registry.yarnpkg.com/@expo/config/-/config-7.0.1.tgz#d8e2e5410bb0b8e305690bbc76e6bb76f6a6de31"
integrity sha512-4lu0wr45XXJ2MXiLAm2+fmOyy/jjqF3NuDm92fO6nuulRzEEvTP4w3vsibJ690rT81ohtvhpruKhkRs0wSjKWA==
dependencies:
"@babel/code-frame" "~7.10.4"
"@expo/config-plugins" "~5.0.1"
"@expo/config-types" "^46.0.1"
"@expo/json-file" "8.2.36"
getenv "^1.0.0"
glob "7.1.6"
require-from-string "^2.0.2"
resolve-from "^5.0.0"
semver "7.3.2"
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/dev-server@0.1.116":
version "0.1.116"
resolved "https://registry.yarnpkg.com/@expo/dev-server/-/dev-server-0.1.116.tgz#65774a28cbe1ab22101be4f41626b7530b4f7560"
@@ -4708,19 +4665,6 @@ expo-application@~4.1.0:
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-4.1.0.tgz#e0214ff7cf73db5a5e97e609ffbab3cc98288030"
integrity sha512-Z2kctgVMpYZB1Iwaxd+XcMBq7h8EEY50GGrwxXsb1OHHQKN+WEVGBWxjvtPkAroqCdujLaB5HBay46gvUHRDQg==
expo-asset@^8.6.1:
version "8.6.1"
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.6.1.tgz#86355b3e231e8aa6cf68a456ce9746dff1478b48"
integrity sha512-urbUp1YtwH2J0Qc3inGQJdqTjWKML77SeMNgff+iR9MUE8gDkFqSCDjrBi7i5Oj5DDtq43mmtDg8G8ei6Vchcg==
dependencies:
blueimp-md5 "^2.10.0"
expo-constants "~13.2.2"
expo-file-system "~14.1.0"
invariant "^2.2.4"
md5-file "^3.2.3"
path-browserify "^1.0.0"
url-parse "^1.5.9"
expo-asset@~8.5.0:
version "8.5.0"
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.5.0.tgz#d83ed8e42f1aa3d74aeca67b87c90e17f1661b0f"
@@ -4740,14 +4684,6 @@ expo-constants@~13.1.1:
"@expo/config" "^6.0.14"
uuid "^3.3.2"
expo-constants@~13.2.2:
version "13.2.4"
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-13.2.4.tgz#eab4a553f074b2c60ad7a158d3b82e3484a94606"
integrity sha512-Zobau8EuTk2GgafwkfGnWM6CmSLB7X8qnQXVuXe0nd3v92hfQUmRWGhJwH88uxXj3LrfqctM6PaJ8taG1vxfBw==
dependencies:
"@expo/config" "~7.0.0"
uuid "^3.3.2"
expo-dev-client@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-1.0.1.tgz#88edb3a60b5a758c0cc57b0326bdcb1ad1573e5a"
@@ -4797,14 +4733,6 @@ expo-file-system@~14.0.0:
"@expo/config-plugins" "^4.0.14"
uuid "^3.4.0"
expo-file-system@~14.1.0:
version "14.1.0"
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-14.1.0.tgz#4fa410873ef12ac8bec873593f7489f4305a14b8"
integrity sha512-lJcPGQ8yKXVknVkD5TmcJnR/TpQbEL0JP8hknLejfq3FIqPqI/LBFn31YiP37grxW8lITz1al8pq5T6CSUjAzQ==
dependencies:
"@expo/config-plugins" "~5.0.0"
uuid "^3.4.0"
expo-font@~10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-10.1.0.tgz#2e8f8954943c5afca8444c1ffb1d74623c6a4fb6"