Skip to content

Commit b5e7d0c

Browse files
first commit
0 parents  commit b5e7d0c

25 files changed

+8145
-0
lines changed

.expo-shared/assets.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3+
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4+
}

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
node_modules/**/*
2+
.expo/*
3+
npm-debug.*
4+
*.jks
5+
*.p8
6+
*.p12
7+
*.key
8+
*.mobileprovision
9+
*.orig.*
10+
web-build/
11+
web-report/
12+
13+
# macOS
14+
.DS_Store

App.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import {createStore, combineReducers, applyMiddleware} from 'redux';
3+
import {Provider} from 'react-redux';
4+
import ReduxThunk from 'redux-thunk';
5+
6+
import PlacesNavigator from './navigation/PlacesNavigator';
7+
import placesReducer from './store/places-reducer';
8+
import {init} from './helpers/db';
9+
10+
init()
11+
.then(() => {
12+
console.log('Initialized database!');
13+
})
14+
.catch(err => {
15+
console.log('Initializing db failed!');
16+
console.log(err);
17+
});
18+
19+
const rootReducer = combineReducers({
20+
places: placesReducer
21+
});
22+
23+
const store = createStore(rootReducer, applyMiddleware(ReduxThunk));
24+
25+
export default function App() {
26+
return (
27+
<Provider store={store}>
28+
<PlacesNavigator />
29+
</Provider>
30+
);
31+
}

app.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"expo": {
3+
"name": "the-place-app",
4+
"slug": "the-place-app",
5+
"platforms": [
6+
"ios",
7+
"android",
8+
"web"
9+
],
10+
"version": "1.0.0",
11+
"orientation": "portrait",
12+
"icon": "./assets/icon.png",
13+
"splash": {
14+
"image": "./assets/splash.png",
15+
"resizeMode": "contain",
16+
"backgroundColor": "#ffffff"
17+
},
18+
"updates": {
19+
"fallbackToCacheTimeout": 0
20+
},
21+
"assetBundlePatterns": [
22+
"**/*"
23+
],
24+
"ios": {
25+
"supportsTablet": true
26+
}
27+
}
28+
}

assets/icon.png

1.07 KB
Loading

assets/splash.png

7.01 KB
Loading

babel.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = function(api) {
2+
api.cache(true);
3+
return {
4+
presets: ['babel-preset-expo'],
5+
};
6+
};

components/HeaderButton.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { HeaderButton } from 'react-navigation-header-buttons';
3+
import { Ionicons } from '@expo/vector-icons';
4+
import { Platform } from 'react-native';
5+
6+
import Colors from '../constants/Colors';
7+
8+
const CustomHeaderButton = props => {
9+
return (
10+
<HeaderButton
11+
{...props}
12+
IconComponent={Ionicons}
13+
iconSize={23}
14+
color={Platform.OS === 'android' ? 'white' : Colors.primary}
15+
/>
16+
);
17+
};
18+
19+
export default CustomHeaderButton;

components/ImagePicker.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, { useState } from 'react';
2+
import { View, Button, Image, Text, StyleSheet, Alert } from 'react-native';
3+
import * as ImagePicker from 'expo-image-picker';
4+
import * as Permissions from 'expo-permissions';
5+
6+
import Colors from '../constants/Colors';
7+
8+
const ImgPicker = props => {
9+
const [pickedImage, setPickedImage] = useState();
10+
11+
const verifyPermissions = async () => {
12+
const result = await Permissions.askAsync(
13+
Permissions.CAMERA_ROLL,
14+
Permissions.CAMERA
15+
);
16+
if (result.status !== 'granted') {
17+
Alert.alert(
18+
'Insufficient permissions!',
19+
'You need to grant camera permissions to use this app.',
20+
[{ text: 'Okay' }]
21+
);
22+
return false;
23+
}
24+
return true;
25+
};
26+
27+
const takeImageHandler = async () => {
28+
const hasPermission = await verifyPermissions();
29+
if (!hasPermission) {
30+
return;
31+
}
32+
const image = await ImagePicker.launchCameraAsync({
33+
allowsEditing: true,
34+
aspect: [16, 9],
35+
quality: 0.5
36+
});
37+
38+
setPickedImage(image.uri);
39+
props.onImageTaken(image.uri);
40+
};
41+
42+
return (
43+
<View style={styles.imagePicker}>
44+
<View style={styles.imagePreview}>
45+
{!pickedImage ? (
46+
<Text>No image picked yet.</Text>
47+
) : (
48+
<Image style={styles.image} source={{ uri: pickedImage }} />
49+
)}
50+
</View>
51+
<Button
52+
title="Take Image"
53+
color={Colors.primary}
54+
onPress={takeImageHandler}
55+
/>
56+
</View>
57+
);
58+
};
59+
60+
const styles = StyleSheet.create({
61+
imagePicker: {
62+
alignItems: 'center',
63+
marginBottom: 15
64+
},
65+
imagePreview: {
66+
width: '100%',
67+
height: 200,
68+
marginBottom: 10,
69+
justifyContent: 'center',
70+
alignItems: 'center',
71+
borderColor: '#ccc',
72+
borderWidth: 1
73+
},
74+
image: {
75+
width: '100%',
76+
height: '100%'
77+
}
78+
});
79+
80+
export default ImgPicker;

components/LocationPicker.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React, { useState, useEffect } from 'react';
2+
import {
3+
View,
4+
Button,
5+
Text,
6+
ActivityIndicator,
7+
Alert,
8+
StyleSheet
9+
} from 'react-native';
10+
import * as Location from 'expo-location';
11+
import * as Permissions from 'expo-permissions';
12+
13+
import Colors from '../constants/Colors';
14+
import MapPreview from './MapPreview';
15+
16+
const LocationPicker = props => {
17+
const [isFetching, setIsFetching] = useState(false);
18+
const [pickedLocation, setPickedLocation] = useState();
19+
20+
const mapPickedLocation = props.navigation.getParam('pickedLocation');
21+
22+
const { onLocationPicked } = props;
23+
24+
useEffect(() => {
25+
if (mapPickedLocation) {
26+
setPickedLocation(mapPickedLocation);
27+
onLocationPicked(mapPickedLocation);
28+
}
29+
}, [mapPickedLocation, onLocationPicked]);
30+
31+
const verifyPermissions = async () => {
32+
const result = await Permissions.askAsync(Permissions.LOCATION);
33+
if (result.status !== 'granted') {
34+
Alert.alert(
35+
'Insufficient permissions!',
36+
'You need to grant location permissions to use this app.',
37+
[{ text: 'Okay' }]
38+
);
39+
return false;
40+
}
41+
return true;
42+
};
43+
44+
const getLocationHandler = async () => {
45+
const hasPermission = await verifyPermissions();
46+
if (!hasPermission) {
47+
return;
48+
}
49+
50+
try {
51+
setIsFetching(true);
52+
const location = await Location.getCurrentPositionAsync({
53+
timeout: 5000
54+
});
55+
setPickedLocation({
56+
lat: location.coords.latitude,
57+
lng: location.coords.longitude
58+
});
59+
props.onLocationPicked({
60+
lat: location.coords.latitude,
61+
lng: location.coords.longitude
62+
});
63+
} catch (err) {
64+
Alert.alert(
65+
'Could not fetch location!',
66+
'Please try again later or pick a location on the map.',
67+
[{ text: 'Okay' }]
68+
);
69+
}
70+
setIsFetching(false);
71+
};
72+
73+
const pickOnMapHandler = () => {
74+
props.navigation.navigate('Map');
75+
};
76+
77+
return (
78+
<View style={styles.locationPicker}>
79+
<MapPreview
80+
style={styles.mapPreview}
81+
location={pickedLocation}
82+
onPress={pickOnMapHandler}
83+
>
84+
{isFetching ? (
85+
<ActivityIndicator size="large" color={Colors.primary} />
86+
) : (
87+
<Text>No location chosen yet!</Text>
88+
)}
89+
</MapPreview>
90+
<View style={styles.actions}>
91+
<Button
92+
title="Get User Location"
93+
color={Colors.primary}
94+
onPress={getLocationHandler}
95+
/>
96+
<Button
97+
title="Pick on Map"
98+
color={Colors.primary}
99+
onPress={pickOnMapHandler}
100+
/>
101+
</View>
102+
</View>
103+
);
104+
};
105+
106+
const styles = StyleSheet.create({
107+
locationPicker: {
108+
marginBottom: 15
109+
},
110+
mapPreview: {
111+
marginBottom: 10,
112+
width: '100%',
113+
height: 150,
114+
borderColor: '#ccc',
115+
borderWidth: 1
116+
},
117+
actions: {
118+
flexDirection: 'row',
119+
justifyContent: 'space-around',
120+
width: '100%'
121+
}
122+
});
123+
124+
export default LocationPicker;

components/MapPreview.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import { TouchableOpacity, Image, StyleSheet } from 'react-native';
3+
4+
import ENV from '../env';
5+
6+
const MapPreview = props => {
7+
let imagePreviewUrl;
8+
9+
if (props.location) {
10+
imagePreviewUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${
11+
props.location.lat
12+
},${
13+
props.location.lng
14+
}&zoom=14&size=400x200&maptype=roadmap&markers=color:red%7Clabel:A%7C${
15+
props.location.lat
16+
},${props.location.lng}&key=${ENV.googleApiKey}`;
17+
}
18+
19+
return (
20+
<TouchableOpacity onPress={props.onPress} style={{ ...styles.mapPreview, ...props.style }}>
21+
{props.location ? (
22+
<Image style={styles.mapImage} source={{ uri: imagePreviewUrl }} />
23+
) : (
24+
props.children
25+
)}
26+
</TouchableOpacity>
27+
);
28+
};
29+
30+
const styles = StyleSheet.create({
31+
mapPreview: {
32+
justifyContent: 'center',
33+
alignItems: 'center'
34+
},
35+
mapImage: {
36+
width: '100%',
37+
height: '100%'
38+
}
39+
});
40+
41+
export default MapPreview;

0 commit comments

Comments
 (0)