From fb2fd984d47986e2b060499ec48c9c27f93ee9e5 Mon Sep 17 00:00:00 2001 From: vitaliy Date: Thu, 2 Aug 2018 20:32:49 +0300 Subject: [PATCH] add camera to take a photo and upload it to firebase --- MobileApp/src/components/app-navigator.js | 4 + .../src/components/people/people-list.js | 6 +- .../src/components/people/person-card.js | 10 ++- MobileApp/src/components/photo/photo.js | 79 ++++++++++++++++++ .../src/components/screens/people-list.js | 8 +- MobileApp/src/components/screens/photo.js | 31 +++++++ MobileApp/src/stores/index.js | 2 + MobileApp/src/stores/navigation.js | 2 + MobileApp/src/stores/people.js | 2 + MobileApp/src/stores/photo.js | 83 +++++++++++++++++++ 10 files changed, 219 insertions(+), 8 deletions(-) create mode 100644 MobileApp/src/components/photo/photo.js create mode 100644 MobileApp/src/components/screens/photo.js create mode 100644 MobileApp/src/stores/photo.js diff --git a/MobileApp/src/components/app-navigator.js b/MobileApp/src/components/app-navigator.js index d698712..5b9c575 100644 --- a/MobileApp/src/components/app-navigator.js +++ b/MobileApp/src/components/app-navigator.js @@ -4,6 +4,7 @@ import EventList from './screens/event-list' import PeopleList from './screens/people-list' import EventScreen from './screens/event' import EventMapScreen from './screens/event-map' +import PhotoScreen from './screens/photo' const ListsNavigator = createBottomTabNavigator({ events: { @@ -24,5 +25,8 @@ export default createStackNavigator({ }, event: { screen: EventMapScreen + }, + photo: { + screen: PhotoScreen } }) \ No newline at end of file diff --git a/MobileApp/src/components/people/people-list.js b/MobileApp/src/components/people/people-list.js index e90c07a..422ee6d 100644 --- a/MobileApp/src/components/people/people-list.js +++ b/MobileApp/src/components/people/people-list.js @@ -16,17 +16,19 @@ class PeopleList extends Component { } render() { - const {onPersonPress, people} = this.props + const {people} = this.props if (people.loading) return return {section.title}} - renderItem = {({item}) => + renderItem = {({item}) => } /> } + + handlePersonPress = (person) => () => this.props.onPersonPress(person) } const styles = StyleSheet.create({ diff --git a/MobileApp/src/components/people/person-card.js b/MobileApp/src/components/people/person-card.js index 1c6bac7..a643294 100644 --- a/MobileApp/src/components/people/person-card.js +++ b/MobileApp/src/components/people/person-card.js @@ -8,10 +8,11 @@ class PersonCard extends Component { }; render() { - const { email, firstName, lastName } = this.props.person + const { email, firstName, lastName, image } = this.props.person + const uri = image ? image : 'https://www.aramsco.com//ASSETS/IMAGES/ITEMS/DETAIL_PAGE/NoImage.png'; return ( - + {email} {firstName} {lastName} @@ -27,8 +28,9 @@ const styles = StyleSheet.create({ }, avatar: { width: 200, - height: 100, - margin: 5 + height: 200, + margin: 5, + alignSelf: 'center', }, content: { flexDirection: 'column', diff --git a/MobileApp/src/components/photo/photo.js b/MobileApp/src/components/photo/photo.js new file mode 100644 index 0000000..1303c58 --- /dev/null +++ b/MobileApp/src/components/photo/photo.js @@ -0,0 +1,79 @@ +import React from 'react'; +import { Text, View, TouchableOpacity } from 'react-native'; +import { Camera, Permissions } from 'expo'; + +export default class Photo extends React.Component { + static defaultProps = { + onPhoto: () => {} + }; + + state = { + hasCameraPermission: null, + type: Camera.Constants.Type.back, + }; + + async componentWillMount() { + const { status } = await Permissions.askAsync(Permissions.CAMERA); + this.setState({ hasCameraPermission: status === 'granted' }); + } + + snap = async () => { + if (this.camera) { + let photo = await this.camera.takePictureAsync(); + this.props.onSnap(photo) + } + }; + + render() { + const { hasCameraPermission } = this.state; + if (hasCameraPermission === null) { + return ; + } else if (hasCameraPermission === false) { + return No access to camera; + } else { + return ( + + { this.camera = ref; }}> + + { + this.setState({ + type: this.state.type === Camera.Constants.Type.back + ? Camera.Constants.Type.front + : Camera.Constants.Type.back, + }); + }}> + + {' '}Flip{' '} + + + { + this.snap() + }}> + + {' '}SHOOT{' '} + + + + + + ); + } + } +} \ No newline at end of file diff --git a/MobileApp/src/components/screens/people-list.js b/MobileApp/src/components/screens/people-list.js index ec90519..6dbfa17 100644 --- a/MobileApp/src/components/screens/people-list.js +++ b/MobileApp/src/components/screens/people-list.js @@ -3,7 +3,7 @@ import {observer, inject} from 'mobx-react' import {View, StyleSheet, ActivityIndicator} from 'react-native' import PeopleList from '../people/people-list' -@inject('people') @observer +@inject('people', 'navigation') @observer class PeopleListScreen extends Component { static propTypes = { @@ -21,12 +21,16 @@ class PeopleListScreen extends Component { render() { const {people} = this.props if (people.loading) return this.getLoader() - return + return } getLoader() { return } + + handlePersonPress = ({uid}) => { + this.props.navigation.navigate('photo', { uid, type: 'people' }) + } } const styles = StyleSheet.create({ diff --git a/MobileApp/src/components/screens/photo.js b/MobileApp/src/components/screens/photo.js new file mode 100644 index 0000000..3e134ae --- /dev/null +++ b/MobileApp/src/components/screens/photo.js @@ -0,0 +1,31 @@ +import React, { Component } from 'react' +import {observer, inject} from 'mobx-react' +import {View, ActivityIndicator} from 'react-native' + +import Photo from '../photo/photo' + +@inject('photo') +@observer +class PhotoScreen extends Component { + static propTypes = { + + }; + + static navigationOptions = { + title: 'photo' + } + + render() { + if(this.props.photo.loading) { + return + } + return + } + + handleSnap = ({uri}) => { + const {uid, type} = this.props.navigation.state.params + this.props.photo.loadPhoto(uri, type, uid) + } +} + +export default PhotoScreen \ No newline at end of file diff --git a/MobileApp/src/stores/index.js b/MobileApp/src/stores/index.js index 88e0221..a42a782 100644 --- a/MobileApp/src/stores/index.js +++ b/MobileApp/src/stores/index.js @@ -3,6 +3,7 @@ import AuthStore from './auth' import NavigationStore from './navigation' import PeopleStore from './people' import EventsStore from './events' +import PhotoStore from './photo' const stores = {} @@ -10,5 +11,6 @@ stores.auth = new AuthStore(stores) stores.navigation = new NavigationStore(stores) stores.people = new PeopleStore(stores) stores.events = new EventsStore(stores) +stores.photo = new PhotoStore(stores) export default stores \ No newline at end of file diff --git a/MobileApp/src/stores/navigation.js b/MobileApp/src/stores/navigation.js index 3e61d42..dd380b8 100644 --- a/MobileApp/src/stores/navigation.js +++ b/MobileApp/src/stores/navigation.js @@ -33,6 +33,8 @@ class NavigationStore extends BasicStore { routeName })) + back = () => this.ref.dispatch(NavigationActions.back()) + reset = routeName => this.ref.dispatch(StackActions.reset({ index: 0, actions: [ diff --git a/MobileApp/src/stores/people.js b/MobileApp/src/stores/people.js index c1877f6..42954e6 100644 --- a/MobileApp/src/stores/people.js +++ b/MobileApp/src/stores/people.js @@ -13,6 +13,8 @@ class PeopleStore extends EntitiesStore { } @action loadAll = loadAllHelper('people') + + @action updateImage = (key, data) => this.entities[key] = data } export default PeopleStore \ No newline at end of file diff --git a/MobileApp/src/stores/photo.js b/MobileApp/src/stores/photo.js new file mode 100644 index 0000000..5690d7c --- /dev/null +++ b/MobileApp/src/stores/photo.js @@ -0,0 +1,83 @@ +import {observable, action} from 'mobx' +import firebase from 'firebase/app' +import BasicStore from './basic-store' + +class PhotoStore extends BasicStore { + @observable loading = false + + @action loadPhoto = async (uri, type, key) => { + this.loading = true + + const store = this.getStore(type) + const entities = store.entities + const entity = entities[key]; + + if(!entity) { + return + } + + const {uid, ...data} = entity + + const file = await fetch(uri).then(response => response.blob()) + + const storageRef = firebase.storage().ref(); + + const metadata = { + contentType: 'image/jpeg' + }; + + const uploadTask = storageRef.child(`images/${type}/${file.data.name}`).put(file, metadata); + + uploadTask.on( + firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed' + (snapshot) => { + // Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded + const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; + console.log('Upload is ' + progress + '% done'); + switch (snapshot.state) { + case firebase.storage.TaskState.PAUSED: // or 'paused' + console.log('Upload is paused'); + break; + case firebase.storage.TaskState.RUNNING: // or 'running' + console.log('Upload is running'); + break; + } + }, + (error) => { + // A full list of error codes is available at + // https://firebase.google.com/docs/storage/web/handle-errors + switch (error.code) { + case 'storage/unauthorized': + // User doesn't have permission to access the object + console.error('User doesn\'t have permission to access the object') + break; + + case 'storage/canceled': + // User canceled the upload + console.error('User canceled the upload') + break; + case 'storage/unknown': + // Unknown error occurred, inspect error.serverResponse + console.error('User canceled the upload') + break; + } + }, + () => { + // Upload completed successfully, now we can get the download URL + uploadTask.snapshot.ref.getDownloadURL().then(action((downloadURL) => { + console.log('File available at', downloadURL); + firebase.database().ref(`${type}/${uid}`).set({ + ...data, + image: downloadURL + }); + store.updateImage(uid, { ...entity, image: downloadURL}) + + this.loading = false + + this.getStore('navigation').back() + })); + }); + } +} + +export default PhotoStore \ No newline at end of file