Skip to content

Commit 3ea6d94

Browse files
committed
Implemented image management for user profile picture
Added get photo and post photo endpoints for profile.js and editprofile.js, editphoto.js is a camera to capture and upload to server. Will add an upload button in the future to let user preview first then upload instead of just send it immediately. There is a warning shows which is source.uri cannot be empty that needs to fix. Added a new component that provide a base URL so that IP address can be change globally.
1 parent 70e233e commit 3ea6d94

21 files changed

+220
-37
lines changed

MyApp/android/app/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ android {
130130
defaultConfig {
131131
applicationId "com.myapp"
132132
minSdkVersion rootProject.ext.minSdkVersion
133+
missingDimensionStrategy 'react-native-camera', 'general'
133134
targetSdkVersion rootProject.ext.targetSdkVersion
134135
versionCode 1
135136
versionName "1.0"
@@ -179,6 +180,8 @@ android {
179180
}
180181

181182
dependencies {
183+
implementation project(':react-native-camera')
184+
implementation project(':react-native-image-picker')
182185
implementation project(':react-native-vector-icons')
183186
implementation project(':react-native-gesture-handler')
184187
implementation fileTree(dir: "libs", include: ["*.jar"])

MyApp/android/app/src/main/AndroidManifest.xml

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
package="com.myapp">
33

44
<uses-permission android:name="android.permission.INTERNET" />
5+
<uses-permission android:name="android.permission.CAMERA" />
6+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
57

68
<application
79
android:name=".MainApplication"

MyApp/android/app/src/main/java/com/myapp/MainApplication.java

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import android.content.Context;
55
import com.facebook.react.PackageList;
66
import com.facebook.react.ReactApplication;
7+
import org.reactnative.camera.RNCameraPackage;
8+
import com.imagepicker.ImagePickerPackage;
79
import com.oblador.vectoricons.VectorIconsPackage;
810
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
911
import com.facebook.react.ReactNativeHost;

MyApp/android/settings.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
rootProject.name = 'MyApp'
2+
include ':react-native-camera'
3+
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
4+
include ':react-native-image-picker'
5+
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
26
include ':react-native-vector-icons'
37
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
48
include ':react-native-gesture-handler'

MyApp/components/baseUrl.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import React from 'react'
2+
3+
// Will be changed to 10.0.2.2 in the future for uni
4+
export const baseUrl = 'http://192.168.0.22:3333/api/v0.0.5';

MyApp/ios/Podfile

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ target 'MyApp' do
3838

3939
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
4040

41+
pod 'react-native-image-picker', :path => '../node_modules/react-native-image-picker'
42+
43+
pod 'react-native-camera', :path => '../node_modules/react-native-camera'
44+
4145
target 'MyAppTests' do
4246
inherit! :search_paths
4347
# Pods for testing

MyApp/navigators.js

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {createBottomTabNavigator} from 'react-navigation-tabs';
99
// UserStack screens
1010
import Profile from './screens/Profile'
1111
import EditProfile from './screens/EditProfile'
12+
import EditPhoto from './screens/EditPhoto'
1213

1314
// LoginStack screens
1415
import Login from './screens/Login'
@@ -69,6 +70,9 @@ export const UserStack = createStackNavigator({
6970
},
7071
EditProfile: {
7172
screen: EditProfile
73+
},
74+
EditPhoto: {
75+
screen: EditPhoto
7276
}
7377
})
7478

MyApp/package-lock.json

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MyApp/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
"formik": "^2.1.4",
1515
"react": "16.9.0",
1616
"react-native": "0.61.5",
17+
"react-native-camera": "^3.19.0",
1718
"react-native-elements": "^1.2.7",
1819
"react-native-gesture-handler": "^1.6.0",
20+
"react-native-image-picker": "^2.3.1",
1921
"react-native-reanimated": "^1.7.0",
2022
"react-native-safe-area-context": "^0.7.3",
2123
"react-native-screens": "^2.0.0-beta.10",

MyApp/screens/EditPhoto.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React,{Component, Fragment} from 'react';
2+
import { View, Text, StyleSheet, AsyncStorage, TouchableOpacity, Alert, Image, Button } from 'react-native';
3+
import ImagePicker from 'react-native-image-picker';
4+
import {RNCamera} from 'react-native-camera';
5+
import {baseUrl} from '../components/baseUrl'
6+
7+
export default class EditPhoto extends Component {
8+
9+
// Constructor to set the states
10+
constructor(props){
11+
super(props);
12+
this.state={
13+
photo: null,
14+
auth: {}
15+
}
16+
}
17+
18+
async getUser(){
19+
let response = await AsyncStorage.getItem('auth');
20+
let authKey = await JSON.parse(response) || {};
21+
this.setState({
22+
auth: authKey
23+
});
24+
}
25+
26+
takePicture = async() => {
27+
if(this.camera){
28+
const options = {quality: 0.5, base64: true};
29+
const data = await this.camera.takePictureAsync(options);
30+
console.log(data.uri, this.state.token);
31+
32+
return fetch(baseUrl+'/user/photo',{
33+
method: 'POST',
34+
headers:{
35+
"Content-Type": "image/jpeg",
36+
"X-Authorization": this.state.auth.token
37+
},
38+
body: data
39+
})
40+
.then((response)=>{
41+
Alert.alert('Picture Added!');
42+
})
43+
.catch((error)=>{
44+
console.log(error);
45+
});
46+
}
47+
}
48+
49+
componentDidMount(){
50+
this.getUser();
51+
}
52+
53+
render() {
54+
return (
55+
<View style={styles.container}>
56+
<RNCamera
57+
ref={ref=>{
58+
this.camera = ref;
59+
}}
60+
style={styles.preview}
61+
/>
62+
<View style={{flex:0, flexDirection:'row', justifyContent:'center'}}>
63+
<TouchableOpacity
64+
onPress={this.takePicture.bind(this)}
65+
style={styles.capture}
66+
>
67+
<Text style={{fontSize: 16}}>
68+
CAPTURE
69+
</Text>
70+
</TouchableOpacity>
71+
</View>
72+
</View>
73+
);
74+
}
75+
}
76+
77+
const styles = StyleSheet.create({
78+
container: {flex: 1, flexDirection: 'column'},
79+
preview: {flex: 1, justifyContent: 'flex-end', alignItems: 'center'},
80+
capture: {flex: 0, borderRadius: 5, padding: 15, paddingHorizontal: 20, alignSelf: 'center', margin: 20,}
81+
});

MyApp/screens/EditProfile.js

+36-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import FormInput from '../components/FormInput'
66
import FormButton from '../components/FormButton'
77
import * as yup from 'yup'
88
import ErrorMessage from '../components/ErrorMessage'
9+
import {baseUrl} from '../components/baseUrl'
10+
import {Avatar} from 'react-native-elements';
911

1012
const profilePic = require('../images/default.jpg');
1113

@@ -15,14 +17,15 @@ class EditProfile extends Component {
1517
super(props);
1618
this.state={
1719
isLodaing: true,
20+
photo: '',
1821
userDetails: {},
1922
auth:{}
2023
}
2124
}
2225
// Send patch request to server when save button is pressed
2326
handleSubmit = values => {
2427
if (values.given_name.length > 0 && values.family_name.length > 0 && values.email.length > 0 && values.password.length > 0) {
25-
return fetch('http://192.168.0.22:3333/api/v0.0.5/user/' + this.state.auth.id,
28+
return fetch(baseUrl+'/user/' + this.state.auth.id,
2629
{
2730
method: 'PATCH',
2831
withCredentials: true,
@@ -51,18 +54,41 @@ class EditProfile extends Component {
5154
}
5255
}
5356

57+
imagePressed(){
58+
this.props.navigation.navigate('EditPhoto')
59+
}
60+
5461
async getUser(){
5562
let response = await AsyncStorage.getItem('auth');
5663
let authKey = await JSON.parse(response) || {};
5764
this.setState({
5865
auth: authKey
5966
});
6067
this.getData(this.state.auth.id)
68+
this.getPhoto(this.state.auth.id)
69+
}
70+
71+
getPhoto(id){
72+
return fetch(baseUrl+'/user/'+id+'/photo')
73+
.then(response => response.blob())
74+
.then((image)=>{
75+
var reader = new FileReader();
76+
reader.onload =()=>{
77+
this.setState({
78+
isLoading: false,
79+
photo: reader.result
80+
});
81+
}
82+
reader.readAsDataURL(image);
83+
})
84+
.catch((error)=>{
85+
console.log(error);
86+
});
6187
}
88+
6289
// A function to do GET/user request to retrieve user details
6390
getData(id){
64-
// Will be changed to 10.0.2.2 in the future for uni
65-
return fetch('http://192.168.0.22:3333/api/v0.0.5/user/' + id)
91+
return fetch(baseUrl+'/user/' + id)
6692
.then((response)=>response.json())
6793
.then((responseJson)=>{
6894
this.setState({
@@ -100,7 +126,13 @@ class EditProfile extends Component {
100126
/>
101127
</View>
102128
</View>
103-
<Image style={styles.avatar} source = {profilePic}/>
129+
<Avatar style={styles.avatar}
130+
size="xlarge"
131+
rounded
132+
source={{uri: this.state.photo}}
133+
onPress={()=>this.imagePressed()}
134+
activeOpacity={0.7}
135+
/>
104136
<FormInput
105137
name="Given Name"
106138
value={values.given_name}

MyApp/screens/Followers.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, {Component} from 'react';
22
import {StyleSheet, Text, View, FlatList, AsyncStorage} from 'react-native';
33
import {ListItem} from 'react-native-elements';
4+
import {baseUrl} from '../components/baseUrl'
45

56
const profilePic = require('../images/default.jpg');
67

@@ -30,7 +31,7 @@ export default class Followers extends Component{
3031

3132
async getFollowers(){
3233
let id = JSON.parse(await AsyncStorage.getItem('id'));
33-
return fetch('http://192.168.0.22:3333/api/v0.0.5/user/'+id+'/followers')
34+
return fetch(baseUrl+'/user/'+id+'/followers')
3435
.then((response)=>response.json())
3536
.then((responseJson)=>{
3637
this.setState({

MyApp/screens/Following.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, {Component} from 'react';
22
import {StyleSheet, Text, View, FlatList, AsyncStorage} from 'react-native';
33
import {ListItem} from 'react-native-elements';
4+
import {baseUrl} from '../components/baseUrl'
45

56
const profilePic = require('../images/default.jpg');
67

@@ -29,7 +30,7 @@ export default class Following extends Component{
2930

3031
async getFollowers(){
3132
let id = JSON.parse(await AsyncStorage.getItem('id'));
32-
return fetch('http://192.168.0.22:3333/api/v0.0.5/user/'+id+'/following')
33+
return fetch(baseUrl+'/user/'+id+'/following')
3334
.then((response)=>response.json())
3435
.then((responseJson)=>{
3536
this.setState({

MyApp/screens/HomeScreen.js

+7-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {Component} from 'react';
22
import {FlatList, ActivityIndicator, Text, View} from 'react-native';
33
import {NavigationEvents} from 'react-navigation';
44
import {ListItem} from 'react-native-elements';
5+
import {baseUrl} from '../components/baseUrl';
56

67
// This is the home screen which contains a chit list
78
class HomeScreen extends Component{
@@ -13,25 +14,23 @@ class HomeScreen extends Component{
1314
chitListData: []
1415
}
1516
}
16-
// A function to do GET/chits request
17+
// An async function to do GET/chits request
1718
getData(){
18-
// Will be changed to 10.0.2.2 in the future for uni
19-
return fetch('http://192.168.0.22:3333/api/v0.0.5/chits')
20-
21-
.then((response)=>response.json())
19+
return fetch(baseUrl+'/chits')
20+
.then((response)=> response.json())
2221
.then((responseJson)=>{
2322
this.setState({
2423
isLoading: false,
2524
chitListData: responseJson,
2625
});
2726
})
28-
.catch((error)=>{
29-
console.log(error);
27+
.catch((err)=>{
28+
console.error(err);
3029
});
3130
}
3231

3332
componentDidMount(){
34-
this.getData();
33+
this.getData()
3534
}
3635

3736
render(){

MyApp/screens/Login.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import FormInput from '../components/FormInput'
77
import FormButton from '../components/FormButton'
88
import * as yup from 'yup'
99
import ErrorMessage from '../components/ErrorMessage'
10+
import {baseUrl} from '../components/baseUrl'
1011

1112
export default class Login extends Component{
1213

1314
handleSubmit = values => {
1415

1516
if(values.email.length>0 && values.password.length>0){
16-
return fetch ('http://192.168.0.22:3333/api/v0.0.5/login',
17+
return fetch (baseUrl+'/login',
1718
{
1819
method: 'POST',
1920
headers: {

0 commit comments

Comments
 (0)