Added avatar uploading

This commit is contained in:
Keannu Bernasol 2023-07-27 16:00:31 +08:00
parent a3b3bd887f
commit e4d64f3656
7 changed files with 84 additions and 12 deletions

View file

@ -42,7 +42,13 @@
[ [
"expo-location", "expo-location",
{ {
"locationAlwaysAndWhenInUsePermission": "Allow Stud-E to use your location." "locationAlwaysAndWhenInUsePermission": "Allow StudE to use your location."
}
],
[
"expo-image-picker",
{
"photosPermission": "Allow StudE to take and send photos for sharing in-app"
} }
] ]
], ],

28
package-lock.json generated
View file

@ -16,6 +16,8 @@
"@tanstack/react-query": "^4.29.25", "@tanstack/react-query": "^4.29.25",
"axios": "^1.4.0", "axios": "^1.4.0",
"expo": "~48.0.18", "expo": "~48.0.18",
"expo-file-system": "~15.2.2",
"expo-image-picker": "~14.1.1",
"expo-intent-launcher": "~10.5.2", "expo-intent-launcher": "~10.5.2",
"expo-linking": "~4.0.1", "expo-linking": "~4.0.1",
"expo-location": "~15.1.1", "expo-location": "~15.1.1",
@ -41,6 +43,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@types/react": "~18.0.14", "@types/react": "~18.0.14",
"@types/react-native-fetch-blob": "^0.10.7",
"typescript": "^4.9.4" "typescript": "^4.9.4"
} }
}, },
@ -5311,6 +5314,12 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-native-fetch-blob": {
"version": "0.10.7",
"resolved": "https://registry.npmjs.org/@types/react-native-fetch-blob/-/react-native-fetch-blob-0.10.7.tgz",
"integrity": "sha512-9UTvmUvArimShiENeR3xnRO71NcZjpTi7AcFAIbhdTIfqQOO2OK/I/DpUPXcZF/erffLxOoRkoXrZOxyBBWKRQ==",
"dev": true
},
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.3", "version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@ -7458,6 +7467,25 @@
"expo": "*" "expo": "*"
} }
}, },
"node_modules/expo-image-loader": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.1.1.tgz",
"integrity": "sha512-ciEHVokU0f6w0eTxdRxLCio6tskMsjxWIoV92+/ZD37qePUJYMfEphPhu1sruyvMBNR8/j5iyOvPFVGTfO8oxA==",
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-image-picker": {
"version": "14.1.1",
"resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-14.1.1.tgz",
"integrity": "sha512-SvWtnkLW7jp5Ntvk3lVcRQmhFYja8psmiR7O6P/+7S6f4llt3vaFwb4I3+pUXqJxxpi7BHc2+95qOLf0SFOIag==",
"dependencies": {
"expo-image-loader": "~4.1.0"
},
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-intent-launcher": { "node_modules/expo-intent-launcher": {
"version": "10.5.2", "version": "10.5.2",
"resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-10.5.2.tgz", "resolved": "https://registry.npmjs.org/expo-intent-launcher/-/expo-intent-launcher-10.5.2.tgz",

View file

@ -17,6 +17,8 @@
"@tanstack/react-query": "^4.29.25", "@tanstack/react-query": "^4.29.25",
"axios": "^1.4.0", "axios": "^1.4.0",
"expo": "~48.0.18", "expo": "~48.0.18",
"expo-file-system": "~15.2.2",
"expo-image-picker": "~14.1.1",
"expo-intent-launcher": "~10.5.2", "expo-intent-launcher": "~10.5.2",
"expo-linking": "~4.0.1", "expo-linking": "~4.0.1",
"expo-location": "~15.1.1", "expo-location": "~15.1.1",
@ -42,6 +44,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@types/react": "~18.0.14", "@types/react": "~18.0.14",
"@types/react-native-fetch-blob": "^0.10.7",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },
"private": true "private": true

View file

@ -17,7 +17,7 @@ if (__DEV__) {
} }
// Switch this on if you wanna run production URLs while in development // Switch this on if you wanna run production URLs while in development
let use_production = true; let use_production = false;
if (use_production) { if (use_production) {
backendURL = "https://stude.keannu1.duckdns.org"; backendURL = "https://stude.keannu1.duckdns.org";
backendURLWebsocket = "ws://stude.keannu1.duckdns.org"; backendURLWebsocket = "ws://stude.keannu1.duckdns.org";
@ -142,6 +142,7 @@ export async function UserInfo() {
} }
export async function PatchUserInfo(info: PatchStudentData) { export async function PatchUserInfo(info: PatchStudentData) {
console.log("API", JSON.stringify(info));
const config = await GetConfig(); const config = await GetConfig();
return instance return instance
.patch("/api/v1/accounts/users/me/", info, config) .patch("/api/v1/accounts/users/me/", info, config)
@ -153,7 +154,7 @@ export async function PatchUserInfo(info: PatchStudentData) {
let error_message = ""; let error_message = "";
if (error.response) error_message = error.response.data; if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers"; else error_message = "Unable to reach servers";
// console.log(error_message); console.log(error_message);
return [false, error_message]; return [false, error_message];
}); });
} }

View file

@ -96,6 +96,11 @@ export interface Subject {
export type Subjects = Array<Subject>; export type Subjects = Array<Subject>;
export type SubjectParams = [boolean, Subjects]; export type SubjectParams = [boolean, Subjects];
export type avatar = {
uri: string;
type: string;
name: string;
};
// For dropdown menu // For dropdown menu
export interface OnboardingParams { export interface OnboardingParams {
@ -112,6 +117,7 @@ export interface PatchStudentData {
subjects?: any[] | null; // To-do, replace 'any' with your actual type subjects?: any[] | null; // To-do, replace 'any' with your actual type
year_level?: string | null; year_level?: string | null;
irregular?: boolean | null; irregular?: boolean | null;
avatar?: string | null;
} }
export interface StudentData { export interface StudentData {

View file

@ -6,6 +6,7 @@ import {
TextInput, TextInput,
NativeSyntheticEvent, NativeSyntheticEvent,
TextInputChangeEventData, TextInputChangeEventData,
Pressable,
} from "react-native"; } from "react-native";
import { useState } from "react"; import { useState } from "react";
import { import {
@ -32,13 +33,14 @@ import {
} from "../../components/Api/Api"; } from "../../components/Api/Api";
import { colors } from "../../styles"; import { colors } from "../../styles";
import DropDownPicker from "react-native-dropdown-picker"; import DropDownPicker from "react-native-dropdown-picker";
import { ValueType } from "react-native-dropdown-picker";
import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll"; import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll";
import BouncyCheckbox from "react-native-bouncy-checkbox"; import BouncyCheckbox from "react-native-bouncy-checkbox";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { RootState } from "../../features/redux/Store/Store"; import { RootState } from "../../features/redux/Store/Store";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { setUser as setUserinState } from "../../features/redux/slices/UserSlice/UserSlice"; import { setUser as setUserinState } from "../../features/redux/slices/UserSlice/UserSlice";
import * as ImagePicker from "expo-image-picker";
import * as FileSystem from "expo-file-system";
export default function UserInfoPage() { export default function UserInfoPage() {
const logged_in_user = useSelector((state: RootState) => state.user.user); const logged_in_user = useSelector((state: RootState) => state.user.user);
@ -71,9 +73,9 @@ export default function UserInfoPage() {
year_level: data[1].year_level, year_level: data[1].year_level,
semester: data[1].semester, semester: data[1].semester,
course: data[1].course, course: data[1].course,
avatar: data[1].avatar,
student_id_number: data[1].student_id_number, student_id_number: data[1].student_id_number,
irregular: data[1].irregular, irregular: data[1].irregular,
avatar: data[1].avatar,
}); });
setSelectedCourse(data[1].course); setSelectedCourse(data[1].course);
setSelectedSemester(data[1].semester); setSelectedSemester(data[1].semester);
@ -157,15 +159,39 @@ export default function UserInfoPage() {
}); });
// Profile photo // Profile photo
const pickImage = async () => {
// No permissions request is necessary for launching the image library
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
const encodedImage = await FileSystem.readAsStringAsync(
result.assets[0].uri,
{ encoding: "base64" }
);
mutation.mutate({
avatar: encodedImage,
});
}
};
function Avatar() { function Avatar() {
if (user.avatar) { if (user.avatar) {
return <Image source={{ uri: user.avatar }} style={styles.profile} />; return (
<Pressable onPress={pickImage}>
<Image source={{ uri: user.avatar }} style={styles.profile} />
</Pressable>
);
} else { } else {
return ( return (
<Pressable onPress={pickImage}>
<Image <Image
source={require("../../img/user_profile_placeholder.png")} source={require("../../img/user_profile_placeholder.png")}
style={{ ...styles.profile, ...{ marginRight: 48 } }} style={{ ...styles.profile, ...{ marginRight: 48 } }}
/> />
</Pressable>
); );
} }
} }
@ -350,7 +376,7 @@ export default function UserInfoPage() {
<Text style={styles.text_white_small}>Irregular </Text> <Text style={styles.text_white_small}>Irregular </Text>
</View> </View>
<Button <Button
onPress={() => { onPress={async () => {
setYearLevelOpen(false); setYearLevelOpen(false);
setSemesterOpen(false); setSemesterOpen(false);
setCourseOpen(false); setCourseOpen(false);

View file

@ -182,6 +182,8 @@ const styles = StyleSheet.create({
borderRadius: 150 / 2, borderRadius: 150 / 2,
overflow: "hidden", overflow: "hidden",
padding: 0, padding: 0,
borderColor: colors.primary_2,
borderWidth: 3,
}, },
input: { input: {
paddingHorizontal: 8, paddingHorizontal: 8,