reamed user metrics into user data and added avatar picture and started to implement the user settings page
This commit is contained in:
73
front/API.ts
73
front/API.ts
@@ -33,9 +33,9 @@ export class APIError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public status: number,
|
||||
// Set the message to the correct error this is a placeholder
|
||||
// Set the message to the correct error this is a placeholder
|
||||
// when the error is only used internally (middleman)
|
||||
public userMessage : keyof typeof en = "unknownError"
|
||||
public userMessage: keyof typeof en = "unknownError"
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
@@ -54,7 +54,8 @@ const dummyIllustrations = [
|
||||
"https://upload.wikimedia.org/wikipedia/en/b/ba/David_Guetta_2U.jpg",
|
||||
];
|
||||
|
||||
const getDummyIllustration = () => dummyIllustrations[Math.floor(Math.random() * dummyIllustrations.length)];
|
||||
const getDummyIllustration = () =>
|
||||
dummyIllustrations[Math.floor(Math.random() * dummyIllustrations.length)];
|
||||
|
||||
// we will need the same thing for the scorometer API url
|
||||
const baseAPIUrl =
|
||||
@@ -63,7 +64,6 @@ const baseAPIUrl =
|
||||
: Constants.manifest?.extra?.apiUrl;
|
||||
|
||||
export default class API {
|
||||
|
||||
public static async fetch(params: FetchParams) {
|
||||
const jwtToken = store.getState().user.accessToken;
|
||||
const header = {
|
||||
@@ -110,7 +110,8 @@ export default class API {
|
||||
.catch((e) => {
|
||||
if (!(e instanceof APIError)) throw e;
|
||||
|
||||
if (e.status == 401) throw new APIError("invalidCredentials", 401, "invalidCredentials");
|
||||
if (e.status == 401)
|
||||
throw new APIError("invalidCredentials", 401, "invalidCredentials");
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
@@ -127,7 +128,7 @@ export default class API {
|
||||
body: registrationInput,
|
||||
method: "POST",
|
||||
});
|
||||
// In the Future we should move autheticate out of this function
|
||||
// In the Future we should move autheticate out of this function
|
||||
// and maybe create a new function to create and login in one go
|
||||
return API.authenticate({
|
||||
username: registrationInput.username,
|
||||
@@ -140,7 +141,8 @@ export default class API {
|
||||
route: "/auth/guest",
|
||||
method: "POST",
|
||||
});
|
||||
if (!response.access_token) throw new APIError("No access token", response.status);
|
||||
if (!response.access_token)
|
||||
throw new APIError("No access token", response.status);
|
||||
return response.access_token;
|
||||
}
|
||||
|
||||
@@ -148,14 +150,8 @@ export default class API {
|
||||
* Retrieve information of the currently authentified user
|
||||
*/
|
||||
public static async getUserInfo(): Promise<User> {
|
||||
let me = await API.fetch({
|
||||
route: "/auth/me",
|
||||
});
|
||||
|
||||
// /auth/me only returns username and id (it needs to be changed)
|
||||
|
||||
let user = await API.fetch({
|
||||
route: `/users/${me.id}`,
|
||||
route: "/auth/me",
|
||||
});
|
||||
|
||||
// this a dummy settings object, we will need to fetch the real one from the API
|
||||
@@ -163,10 +159,12 @@ export default class API {
|
||||
id: user.id as number,
|
||||
name: (user.username ?? user.name) as string,
|
||||
email: user.email as string,
|
||||
xp: 0,
|
||||
premium: false,
|
||||
metrics: {
|
||||
data: {
|
||||
partyPlayed: user.partyPlayed as number,
|
||||
xp: 0,
|
||||
avatar:
|
||||
"https://imgs.search.brave.com/RnQpFhmAFvuQsN_xTw7V-CN61VeHDBg2tkEXnKRYHAE/rs:fit:768:512:1/g:ce/aHR0cHM6Ly96b29h/c3Ryby5jb20vd3At/Y29udGVudC91cGxv/YWRzLzIwMjEvMDIv/Q2FzdG9yLTc2OHg1/MTIuanBn",
|
||||
},
|
||||
settings: {
|
||||
preferences: {
|
||||
@@ -217,16 +215,19 @@ export default class API {
|
||||
});
|
||||
|
||||
// this is a dummy illustration, we will need to fetch the real one from the API
|
||||
return songs.data.map((song: any) => ({
|
||||
id: song.id as number,
|
||||
name: song.name as string,
|
||||
artistId: song.artistId as number,
|
||||
albumId: song.albumId as number,
|
||||
genreId: song.genreId as number,
|
||||
details: song.difficulties,
|
||||
cover: getDummyIllustration(),
|
||||
metrics: {},
|
||||
} as Song));
|
||||
return songs.data.map(
|
||||
(song: any) =>
|
||||
({
|
||||
id: song.id as number,
|
||||
name: song.name as string,
|
||||
artistId: song.artistId as number,
|
||||
albumId: song.albumId as number,
|
||||
genreId: song.genreId as number,
|
||||
details: song.difficulties,
|
||||
cover: getDummyIllustration(),
|
||||
metrics: {},
|
||||
} as Song)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,7 +248,6 @@ export default class API {
|
||||
genreId: song.genreId as number,
|
||||
details: song.difficulties,
|
||||
cover: getDummyIllustration(),
|
||||
metrics: {},
|
||||
} as Song;
|
||||
}
|
||||
/**
|
||||
@@ -316,7 +316,7 @@ export default class API {
|
||||
*/
|
||||
public static async searchSongs(query: string): Promise<Song[]> {
|
||||
return API.fetch({
|
||||
route: `/search/guess/song/${query}`
|
||||
route: `/search/guess/song/${query}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -340,7 +340,10 @@ export default class API {
|
||||
*/
|
||||
public static async getSearchHistory(): Promise<Song[]> {
|
||||
const queryClient = new QueryClient();
|
||||
let songs = await queryClient.fetchQuery(["API", "allsongs"], API.getAllSongs);
|
||||
let songs = await queryClient.fetchQuery(
|
||||
["API", "allsongs"],
|
||||
API.getAllSongs
|
||||
);
|
||||
const shuffled = [...songs].sort(() => 0.5 - Math.random());
|
||||
|
||||
return shuffled.slice(0, 2);
|
||||
@@ -359,7 +362,10 @@ export default class API {
|
||||
*/
|
||||
public static async getUserPlayHistory(): Promise<Song[]> {
|
||||
const queryClient = new QueryClient();
|
||||
let songs = await queryClient.fetchQuery(["API", "allsongs"], API.getAllSongs);
|
||||
let songs = await queryClient.fetchQuery(
|
||||
["API", "allsongs"],
|
||||
API.getAllSongs
|
||||
);
|
||||
const shuffled = [...songs].sort(() => 0.5 - Math.random());
|
||||
|
||||
return shuffled.slice(0, 3);
|
||||
@@ -447,7 +453,10 @@ export default class API {
|
||||
return rep;
|
||||
}
|
||||
|
||||
public static async updateUserPassword(oldPassword: string, newPassword: string): Promise<User> {
|
||||
public static async updateUserPassword(
|
||||
oldPassword: string,
|
||||
newPassword: string
|
||||
): Promise<User> {
|
||||
const rep = await API.fetch({
|
||||
route: "/auth/me",
|
||||
method: "PUT",
|
||||
@@ -461,5 +470,5 @@ export default class API {
|
||||
throw new Error(rep.error);
|
||||
}
|
||||
return rep;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
interface Metrics {
|
||||
partyPlayed: number;
|
||||
}
|
||||
|
||||
export default Metrics;
|
||||
@@ -1,4 +1,3 @@
|
||||
import Metrics from "./Metrics";
|
||||
import Model from "./Model";
|
||||
import SongDetails from "./SongDetails";
|
||||
|
||||
@@ -8,7 +7,6 @@ interface Song extends Model {
|
||||
albumId: number | null
|
||||
genreId: number | null;
|
||||
cover: string;
|
||||
metrics: Metrics;
|
||||
details: SongDetails;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Metrics from "./Metrics";
|
||||
import UserData from "./UserData";
|
||||
import Model from "./Model";
|
||||
import UserSettings from "./UserSettings";
|
||||
|
||||
@@ -6,9 +6,8 @@ interface User extends Model {
|
||||
name: string;
|
||||
email: string;
|
||||
isGuest: boolean;
|
||||
xp: number;
|
||||
premium: boolean;
|
||||
metrics: Metrics;
|
||||
data: UserData;
|
||||
settings: UserSettings;
|
||||
}
|
||||
|
||||
|
||||
7
front/models/UserData.ts
Normal file
7
front/models/UserData.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
interface UserData {
|
||||
partyPlayed: number;
|
||||
xp: number;
|
||||
avatar: string | undefined;
|
||||
}
|
||||
|
||||
export default UserData;
|
||||
@@ -3,9 +3,33 @@ import { useDispatch } from "react-redux";
|
||||
import { unsetAccessToken } from "../../state/UserSlice";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Column, Text, Button, Icon, Box, Center, Heading } from "native-base";
|
||||
import {
|
||||
Column,
|
||||
Text,
|
||||
Button,
|
||||
Icon,
|
||||
Box,
|
||||
IconButton,
|
||||
Flex,
|
||||
Row,
|
||||
Center,
|
||||
Heading,
|
||||
Avatar,
|
||||
} from "native-base";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import User from "../../models/User";
|
||||
import TextButton from "../../components/TextButton";
|
||||
import LoadingComponent from "../../components/Loading";
|
||||
|
||||
const getInitials = (name: string) => {
|
||||
const names = name.split(" ");
|
||||
if (names.length === 1) return names[0]?.charAt(0);
|
||||
let initials = [];
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
initials.push(names[i]?.charAt(0));
|
||||
}
|
||||
return initials.join("");
|
||||
};
|
||||
|
||||
const ProfileSettings = ({ navigation }: { navigation: any }) => {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
@@ -17,25 +41,65 @@ const ProfileSettings = ({ navigation }: { navigation: any }) => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Center style={{ flex: 1 }}>
|
||||
<LoadingComponent />
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Center style={{ flex: 1}}>
|
||||
{user && (
|
||||
<Column>
|
||||
<Heading>Profile Settings</Heading>
|
||||
<Flex
|
||||
style={{
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
paddingTop: 40,
|
||||
}}
|
||||
>
|
||||
<Column>
|
||||
<Center>
|
||||
<Avatar size="2xl" source={{ uri: user.data.avatar }}>
|
||||
{getInitials(user.name)}
|
||||
<Avatar.Badge bg="gray.300" size={35}>
|
||||
<IconButton size={"sm"} icon={<Icon as={FontAwesome5} name="edit" />} />
|
||||
</Avatar.Badge>
|
||||
</Avatar>
|
||||
</Center>
|
||||
<Row
|
||||
style={{
|
||||
paddingTop: 20,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Heading>{user.name}</Heading>
|
||||
<Button
|
||||
ml={2}
|
||||
size="sm"
|
||||
leftIcon={<Icon as={FontAwesome5} name="edit" size="sm" />}
|
||||
variant="ghost"
|
||||
colorScheme="primary"
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
}}
|
||||
></Button>
|
||||
</Row>
|
||||
|
||||
<Text>Username: {user.name}</Text>
|
||||
<Text>ID: {user.id}</Text>
|
||||
<Text>Email: {user.email}</Text>
|
||||
<Text>Party played: {user.metrics.partyPlayed}</Text>
|
||||
<Text>Username: {user.name}</Text>
|
||||
<Text>ID: {user.id}</Text>
|
||||
<Text>Email: {user.email}</Text>
|
||||
<Text>Party played: {user.data.partyPlayed}</Text>
|
||||
|
||||
<Text>XP: {user.xp}</Text>
|
||||
</Column>
|
||||
)}
|
||||
<Text>XP: {user.data.xp}</Text>
|
||||
</Column>
|
||||
|
||||
<TextButton onPress={() => dispatch(unsetAccessToken())} translate={{
|
||||
translationKey: "signOutBtn",
|
||||
}} />
|
||||
</Center>
|
||||
<TextButton
|
||||
onPress={() => dispatch(unsetAccessToken())}
|
||||
translate={{
|
||||
translationKey: "signOutBtn",
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user