Front: Use API to get settings

This commit is contained in:
Arthur Jamet
2023-04-23 15:30:37 +01:00
parent 8ccc90eceb
commit bb96d57f27
14 changed files with 152 additions and 174 deletions
+42 -23
View File
@@ -10,7 +10,9 @@ import Constants from "expo-constants";
import store from "./state/Store";
import { Platform } from "react-native";
import { en } from "./i18n/Translations";
import { useQuery, QueryClient } from "react-query";
import { QueryClient } from "react-query";
import UserSettings from "./models/UserSettings";
import { PartialDeep } from "type-fest";
type AuthenticationInput = { username: string; password: string };
type RegistrationInput = AuthenticationInput & { email: string };
@@ -175,31 +177,48 @@ export default class API {
createdAt: new Date("2023-04-09T00:00:00.000Z"),
avatar:
"https://imgs.search.brave.com/RnQpFhmAFvuQsN_xTw7V-CN61VeHDBg2tkEXnKRYHAE/rs:fit:768:512:1/g:ce/aHR0cHM6Ly96b29h/c3Ryby5jb20vd3At/Y29udGVudC91cGxv/YWRzLzIwMjEvMDIv/Q2FzdG9yLTc2OHg1/MTIuanBn",
},
settings: {
preferences: {
deviceId: 1,
micVolume: 10,
theme: "system",
lang: "fr",
difficulty: "beg",
colorBlind: false,
},
notifications: {
pushNotif: false,
emailNotif: false,
trainNotif: false,
newSongNotif: false,
},
privacy: {
dataCollection: true,
customAd: true,
recommendation: true,
},
},
}
} as User;
}
public static async getUserSettings(): Promise<UserSettings> {
const settings = await API.fetch({
route: "/auth/me/settings",
});
// this a dummy settings object, we will need to fetch the real one from the API
return {
notifications: {
pushNotif: settings.pushNotification,
emailNotif: settings.emailNotification,
trainNotif: settings.trainingNotification,
newSongNotif: settings.newSongNotification
},
recommendations: settings.recommendations,
weeklyReport: settings.weeklyReport,
leaderBoard: settings.leaderBoard,
showActivity: settings.showActivity
};
}
public static async updateUserSettings(settings: PartialDeep<UserSettings>): Promise<void> {
const dto = {
pushNotification: settings.notifications?.pushNotif,
emailNotification: settings.notifications?.emailNotif,
trainingNotification: settings.notifications?.trainNotif,
newSongNotification: settings.notifications?.newSongNotif,
recommendations: settings.recommendations,
weeklyReport: settings.weeklyReport,
leaderBoard: settings.leaderBoard,
showActivity: settings.showActivity,
}
return API.fetch({
method: 'PATCH',
route: '/auth/me/settings',
body: dto
});
}
public static async getUserSkills() {
return {
pedalsCompetency: Math.random() * 100,
+1 -2
View File
@@ -2,7 +2,6 @@ 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;
@@ -15,7 +14,7 @@ const cardBorder = (theme: ReturnType<typeof useTheme>) => ({
const Card = (props: Parameters<typeof Box>[0] & { onPress: () => void }) => {
const theme = useTheme();
const colorScheme: SettingsState['colorScheme'] = useSelector((state: RootState) => state.settings.settings.colorScheme);
const colorScheme = useSelector((state: RootState) => state.settings.local.colorScheme);
const systemColorMode = useColorScheme();
return <Pressable onPress={props.onPress}>
+9 -1
View File
@@ -1,7 +1,15 @@
import { useTheme } from "native-base";
import { Spinner } from "native-base";
import { Center, Spinner } from "native-base";
const LoadingComponent = () => {
const theme = useTheme();
return <Spinner color={theme.colors.primary[500]}/>
}
const LoadingView = () => {
return <Center style={{ flexGrow: 1 }}>
<LoadingComponent/>
</Center>
}
export default LoadingComponent;
export { LoadingView }
@@ -1,7 +1,7 @@
import { useTheme, Box, Center } from "native-base";
import React from "react";
import { useQuery } from "react-query";
import LoadingComponent from "../Loading";
import LoadingComponent, { LoadingView } from "../Loading";
import SlideView from "./SlideView";
import API from "../../API";
@@ -13,11 +13,7 @@ const PartitionVisualizer = ({ songId }: PartitionVisualizerProps) => {
if (!partitionRessources.data) {
return (
<Center style={{ flexGrow: 1 }}>
<LoadingComponent />
</Center>
);
return <LoadingView/>;
}
return (
+1 -2
View File
@@ -1,10 +1,9 @@
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 colorScheme = useSelector((state: RootState) => state.settings.local.colorScheme);
const systemColorScheme = Appearance.getColorScheme();
if (colorScheme == 'system') {
+13
View File
@@ -0,0 +1,13 @@
import { useQuery } from "react-query"
import API from "../API"
const useUserSettings = () => {
const queryKey = ['settings'];
const settings = useQuery(queryKey, () => API.getUserSettings())
const updateSettings = (...params: Parameters<typeof API.updateUserSettings>) => API
.updateUserSettings(...params)
.then(() => settings.refetch());
return { settings, updateSettings }
}
export default useUserSettings;
+10
View File
@@ -0,0 +1,10 @@
interface LocalSettings {
deviceId: number,
micVolume: number,
colorScheme: 'light' | 'dark' | 'system',
lang: 'fr' | 'en' | 'sp',
difficulty: 'beg' | 'inter' | 'pro',
colorBlind: boolean,
customAds: boolean,
dataCollection: boolean
}
+4 -13
View File
@@ -1,23 +1,14 @@
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,
customAd: boolean,
recommendation: boolean
}
weeklyReport: boolean,
leaderBoard: boolean,
showActivity: boolean,
recommendations: boolean
}
export default UserSettings
+9 -27
View File
@@ -1,40 +1,22 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export type SettingsState = {
colorScheme: "dark" | "light" | "system",
enablePushNotifications: boolean,
enableMailNotifications: boolean,
enableLessongsReminders: boolean,
enableReleaseAlerts: boolean,
preferedLevel: 'easy' | 'medium' | 'hard',
colorBlind: boolean,
micLevel: number,
preferedInputName?: string,
dataCollection: boolean,
customAds: boolean,
recommandations: boolean,
}
export const settingsSlice = createSlice({
name: 'settings',
initialState: {
settings: <SettingsState>{
enablePushNotifications: true,
enableMailNotifications: true,
enableLessongsReminders: true,
enableReleaseAlerts: true,
preferedLevel: 'easy',
local: <LocalSettings>{
deviceId: 0,
micVolume: 0,
colorScheme: 'system',
lang: 'en',
difficulty: 'beg',
colorBlind: false,
micLevel: 50,
colorScheme: "system",
dataCollection: true,
customAds: true,
recommandations: true,
dataCollection: true
},
},
reducers: {
updateSettings: (state, action: PayloadAction<Partial<SettingsState>>) => {
state.settings = { ...state.settings, ...action.payload };
updateSettings: (state, action: PayloadAction<Partial<LocalSettings>>) => {
state.local = { ...state.local, ...action.payload };
}
}
});
+23 -32
View File
@@ -1,17 +1,16 @@
import React from "react";
import { Center, Heading } from "native-base";
import { translate, Translate } from "../../i18n/i18n";
import { useDispatch } from "react-redux";
import { RootState, useSelector } from "../../state/Store";
import { SettingsState, updateSettings } from "../../state/SettingsSlice";
import ElementList from "../../components/GtkUI/ElementList";
import useUserSettings from "../../hooks/userSettings";
import { LoadingView } from "../../components/Loading";
const NotificationsView = ({ navigation }) => {
const dispatch = useDispatch();
const settings: SettingsState = useSelector(
(state: RootState) => state.settings.settings as SettingsState
);
const NotificationsView = () => {
const { settings, updateSettings } = useUserSettings();
if (!settings.data) {
return <LoadingView/>
}
return (
<Center style={{ flex: 1, justifyContent: "center" }}>
<Heading style={{ textAlign: "center" }}>
@@ -28,13 +27,11 @@ const NotificationsView = ({ navigation }) => {
type: "toggle",
title: translate("SettingsNotificationsPushNotifications"),
data: {
value: settings.enablePushNotifications,
value: settings.data.notifications.pushNotif,
onToggle: () => {
dispatch(
updateSettings({
enablePushNotifications: !settings.enablePushNotifications,
})
);
updateSettings({
notifications: { pushNotif: !settings.data.notifications.pushNotif },
});
},
},
},
@@ -42,13 +39,11 @@ const NotificationsView = ({ navigation }) => {
type: "toggle",
title: translate("SettingsNotificationsEmailNotifications"),
data: {
value: settings.enableMailNotifications,
value: settings.data.notifications.emailNotif,
onToggle: () => {
dispatch(
updateSettings({
enableMailNotifications: !settings.enableMailNotifications,
})
);
updateSettings({
notifications: { emailNotif: !settings.data.notifications.emailNotif },
});
},
},
},
@@ -56,13 +51,11 @@ const NotificationsView = ({ navigation }) => {
type: "toggle",
title: translate("SettingsNotificationsTrainingReminder"),
data: {
value: settings.enableLessongsReminders,
value: settings.data.notifications.trainNotif,
onToggle: () => {
dispatch(
updateSettings({
enableLessongsReminders: !settings.enableLessongsReminders,
})
);
updateSettings({
notifications: { trainNotif: !settings.data.notifications.trainNotif },
});
},
},
},
@@ -70,13 +63,11 @@ const NotificationsView = ({ navigation }) => {
type: "toggle",
title: translate("SettingsNotificationsReleaseAlert"),
data: {
value: settings.enableReleaseAlerts,
value: settings.data.notifications.newSongNotif,
onToggle: () => {
dispatch(
updateSettings({
enableReleaseAlerts: !settings.enableReleaseAlerts,
})
);
updateSettings({
notifications: { newSongNotif: !settings.data.notifications.newSongNotif },
});
},
},
},
+13 -19
View File
@@ -1,33 +1,27 @@
import React from "react";
import { View } from "react-native";
import { useDispatch } from "react-redux";
import {
Center,
Button,
Text,
Switch,
Slider,
Select,
Heading,
} from "native-base";
import { useLanguage } from "../../state/LanguageSlice";
import i18n, {
import {
AvailableLanguages,
DefaultLanguage,
translate,
Translate,
} from "../../i18n/i18n";
import { RootState, useSelector } from "../../state/Store";
import { SettingsState, updateSettings } from "../../state/SettingsSlice";
import { updateSettings } from "../../state/SettingsSlice";
import ElementList from "../../components/GtkUI/ElementList";
const PreferencesView = ({ navigation }) => {
const PreferencesView = () => {
const dispatch = useDispatch();
const language: AvailableLanguages = useSelector(
(state: RootState) => state.language.value
);
const settings = useSelector(
(state: RootState) => state.settings.settings as SettingsState
(state: RootState) => state.settings.local
);
return (
<Center style={{ flex: 1 }}>
@@ -79,15 +73,15 @@ const PreferencesView = ({ navigation }) => {
type: "dropdown",
title: translate("SettingsPreferencesDifficulty"),
data: {
value: settings.preferedLevel,
value: settings.difficulty,
defaultValue: "medium",
onSelect: (itemValue) => {
dispatch(updateSettings({ preferedLevel: itemValue as any }));
dispatch(updateSettings({ difficulty: itemValue as any }));
},
options: [
{ label: translate("easy"), value: "easy" },
{ label: translate("medium"), value: "medium" },
{ label: translate("hard"), value: "hard" },
{ label: translate("easy"), value: "beg" },
{ label: translate("medium"), value: "inter" },
{ label: translate("hard"), value: "pro" },
],
},
},
@@ -123,16 +117,16 @@ const PreferencesView = ({ navigation }) => {
type: "range",
title: translate("SettingsPreferencesMicVolume"),
data: {
value: settings.micLevel,
value: settings.micVolume,
min: 0,
max: 1000,
step: 10,
onChange: (value) => {
dispatch(updateSettings({ micLevel: value }));
dispatch(updateSettings({ micVolume: value }));
},
},
},
{
/*{
type: "dropdown",
title: translate("SettingsPreferencesDevice"),
data: {
@@ -147,7 +141,7 @@ const PreferencesView = ({ navigation }) => {
{ label: "Mic_2", value: "2" },
],
},
},
},*/
]}
/>
</Center>
+10 -8
View File
@@ -4,14 +4,18 @@ import { translate } from "../../i18n/i18n";
import ElementList from "../../components/GtkUI/ElementList";
import { useDispatch } from "react-redux";
import { RootState, useSelector } from "../../state/Store";
import { SettingsState, updateSettings } from "../../state/SettingsSlice";
import { updateSettings } from "../../state/SettingsSlice";
import useUserSettings from "../../hooks/userSettings";
import { LoadingView } from "../../components/Loading";
const PrivacyView = () => {
const dispatch = useDispatch();
const settings: SettingsState = useSelector(
(state: RootState) => state.settings.settings as SettingsState
);
const settings = useSelector((state: RootState) => state.settings.local);
const { settings: userSettings, updateSettings: updateUserSettings } = useUserSettings();
if (!userSettings.data) {
return <LoadingView/>;
}
return (
<Center style={{ flex: 1 }}>
<Heading style={{ textAlign: "center" }}>{translate("privBtn")}</Heading>
@@ -47,11 +51,9 @@ const PrivacyView = () => {
type: "toggle",
title: translate("recommendations"),
data: {
value: settings.recommandations,
value: userSettings.data.recommendations,
onToggle: () =>
dispatch(
updateSettings({ recommandations: !settings.recommandations })
),
updateUserSettings({ recommendations: !userSettings.data.recommendations })
},
},
]}
+10 -29
View File
@@ -1,26 +1,20 @@
import API from "../../API";
import { useDispatch } from "react-redux";
import { unsetAccessToken } from "../../state/UserSlice";
import React, { useEffect, useState } from "react";
import React from "react";
import {
Column,
Text,
Button,
Icon,
Box,
IconButton,
Flex,
Row,
Center,
Heading,
Avatar,
Popover,
} from "native-base";
import { FontAwesome5 } from "@expo/vector-icons";
import User from "../../models/User";
import TextButton from "../../components/TextButton";
import LoadingComponent from "../../components/Loading";
import { LoadingView } from "../../components/Loading";
import ElementList from "../../components/GtkUI/ElementList";
import { translate } from "../../i18n/i18n";
import { useQuery } from "react-query";
@@ -30,26 +24,13 @@ const getInitials = (name: string) => {
};
const ProfileSettings = ({ navigation }: { navigation: any }) => {
const userQuery = useQuery(["appSettings", "user"], API.getUserInfo);
const userQuery = useQuery(['user'], () => API.getUserInfo());
const dispatch = useDispatch();
if (!userQuery.data || userQuery.isLoading) {
return <LoadingView/>
}
const user = userQuery.data;
if (userQuery.isError) {
return (
<Center style={{ flex: 1 }}>
<Text>{translate("errorLoadingUser")}</Text>
</Center>
);
}
if (!userQuery || userQuery.isLoading || !userQuery.data) {
return (
<Center style={{ flex: 1 }}>
<LoadingComponent />
</Center>
);
}
return (
<Flex
style={{
@@ -108,14 +89,14 @@ const ProfileSettings = ({ navigation }: { navigation: any }) => {
title: "ID",
helperText: "This is your unique ID, be proud of it!",
data: {
text: user.id,
text: user.id.toString(),
},
},
{
type: "text",
title: translate("nbGamesPlayed"),
data: {
text: user.data.gamesPlayed,
text: user.data.gamesPlayed.toString(),
},
},
{
@@ -123,7 +104,7 @@ const ProfileSettings = ({ navigation }: { navigation: any }) => {
title: "XP",
description: translate("XPDescription"),
data: {
text: user.data.xp,
text: user.data.xp.toString(),
},
},
{
+5 -12
View File
@@ -1,16 +1,11 @@
import React from 'react';
import { View } from 'react-native';
import { Center, Button, Text, Switch, Slider, Select, Heading, Box } from "native-base";
import { Center, Button, Text, Heading, Box } from "native-base";
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { unsetAccessToken } from '../../state/UserSlice';
import { useDispatch } from "react-redux";
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';
import { translate, Translate } from "../../i18n/i18n";
import createTabRowNavigator from '../../components/navigators/TabRowNavigator';
import { FontAwesome, MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons';
import { MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons';
import ChangePasswordForm from '../../components/forms/changePasswordForm';
import ChangeEmailForm from '../../components/forms/changeEmailForm';
import ProfileSettings from './SettingsProfileView';
@@ -19,9 +14,7 @@ import PrivacyView from './PrivacyView';
import PreferencesView from './PreferencesView';
import GuestToUserView from './GuestToUserView';
import { useQuery } from 'react-query';
import API, { APIError } from '../../API';
import User from '../../models/User';
import API from '../../API';
const SettingsStack = createNativeStackNavigator();
@@ -117,7 +110,7 @@ export const PianoSettingsView = ({navigation}) => {
const TabRow = createTabRowNavigator();
const SetttingsNavigator = () => {
const userQuery = useQuery(["appSettings", 'user'], API.getUserInfo);
const userQuery = useQuery(['user'], () => API.getUserInfo());
const user = userQuery.data;
if (userQuery.isError) {