123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- import React, { useEffect, useRef, useState } from 'react';
- import {Image, StyleSheet, View, TouchableOpacity, ScrollView, Text, Alert} from 'react-native';
- import MapboxGL from '@react-native-mapbox-gl/maps';
- import 'react-native-get-random-values'
- import { seedLandmarkData, useMockState } from '../contexts/MockContext';
- import Modal from 'react-native-modalbox';
- import LandmarkDetails from '../components/LandmarkDetails';
- import {Icons} from '../globals.js';
- import Icon from 'react-native-vector-icons/FontAwesome';
- import VoiceView from '../components/VoiceView';
- import RNLocation from 'react-native-location';
- import { polygon, point, booleanPointInPolygon } from '@turf/turf';
- import ModalDropdown from 'react-native-modal-dropdown';
- MapboxGL.setAccessToken('pk.eyJ1IjoiY2Rtb3NzIiwiYSI6ImNrbmhuOXJzcDIyd20ycW1pYm8xaGI0aGUifQ.j04Sp636N9Wg4N9j9t2tXw');
- RNLocation.configure({
- distanceFilter: 1
- });
- const places = {
- type: 'FeatureCollection',
- features: [
- {
- type: 'Feature',
- id: 'test1',
- properties: {
- name: 'University of Alberta'
- },
- geometry: {
- "type": "Polygon",
- "coordinates": [
- [
- [
- -113.53271484375,
- 53.517705925148846
- ],
- [
- -113.52018356323242,
- 53.515511461129655
- ],
- [
- -113.52044105529785,
- 53.52311504872178
- ],
- [
- -113.51529121398926,
- 53.522757863745845
- ],
- [
- -113.5151195526123,
- 53.52576832854355
- ],
- [
- -113.53134155273438,
- 53.53143150445749
- ],
- [
- -113.53271484375,
- 53.517705925148846
- ]
- ]
- ]
- }
- }
- ]
- }
- const Map = ({navigation, route}) => {
- const { dispatch, state } = useMockState();
- //const [center, setCenter] = useState([-113.4982408198804, 53.56484760459073]);
- const [center, setCenter] = useState([]);
- const [tempPoint, setTempPoint] = useState(null);
- const [selectedPlaces, setPlaces] = useState([]);
- const [voiceActive, setVoice] = useState(false);
- const landmarkAddModal = useRef();
- const mapMenuModal = useRef();
- const landmarkDetailsModal = useRef();
- const voiceModal = useRef();
- const map = useRef();
- useEffect(() => {
- // const unsubscribePermission = RNLocation.subscribeToPermissionUpdates(currentPermission => {
- // dispatch({ type:"UPDATE_LOCATION_PERMISSION", payload: currentPermission });
- // });
- // const unsubscribeLocation = RNLocation.subscribeToLocationUpdates(locations => {
- // dispatch({ type:"UPDATE_LOCATION", payload: locations });
- // });
- async function handleLocation() {
- if (state.landmarks == null) {
- const landmarks = seedLandmarkData();
- dispatch({ type:"UPDATE_LANDMARKS", payload: landmarks });
- }
- if (state.location == null && !state.locationPermission) {
- dispatch({type: 'UPDATE_LOCATION_PERMISSION', payload: await RNLocation.requestPermission({
- ios: "whenInUse",
- android: {
- detail: "fine",
- rationale: {
- title: "We need to access your location",
- message: "We use your location to provide convenient features such as geo-activated notifications and adding landmarks via voice",
- buttonPositive: "Grant permission",
- buttonNegative: "No thanks"
- }
- }
- })})
- }
- }
- handleLocation();
- return() => {
- // unsubscribePermission();
- // unsubscribeLocation();
- }
- },[state]);
- const changeSelectedLandmark = (landmark) => {
- dispatch({ type:"UPDATE_SELECTED_LANDMARK", payload: landmark});
- }
- const showLandmarkDetails = (landmark) => {
- changeSelectedLandmark(landmark);
- landmarkDetailsModal.current.open();
- }
- const openVoiceModal = () => {
- if (!state.locationPermission) {
- Alert.alert('You need to provide location permission to use this feature.')
- }
- else {
- RNLocation.getLatestLocation({timeout: 100}).then(location => {
- dispatch({ type:"UPDATE_LOCATION", payload: location});
- voiceModal.current.open();
- setVoice(true);
- })
-
- }
- }
- const handleMapTouch = (e) => {
- setTempPoint(e.geometry.coordinates);
- changeSelectedLandmark({
- longitude: e.geometry.coordinates[0],
- latitude: e.geometry.coordinates[1],
- icon: 'barrier',
- title: '',
- desc: ''
- })
- landmarkAddModal.current.open();
- }
- const handlePlaceTouch = (e) => {
- setTempPoint([e.coordinates.longitude, e.coordinates.latitude]);
- const touchedPlaces = e.features.filter(f => {
- return booleanPointInPolygon(point([e.coordinates.longitude, e.coordinates.latitude]), polygon(f.geometry.coordinates));
- });
- if (touchedPlaces.length == 0) {
- changeSelectedLandmark({
- longitude: e.coordinates.longitude,
- latitude: e.coordinates.latitude,
- icon: 'barrier',
- title: '',
- desc: ''
- });
- landmarkAddModal.current.open();
- }
- else {
- setPlaces(touchedPlaces);
- mapMenuModal.current.open();
- }
- }
- const addLandmark = () => {
- landmarkAddModal.current.close();
- navigation.navigate("LandmarkForm");
- }
- const removeTempPoint = () => {
- setTempPoint(null);
- }
- const editLandmark = () => {
- navigation.navigate("LandmarkForm");
- }
- const closeModal = (message) => {
- landmarkDetailsModal.current.close();
- if (message != null) {
- Alert.alert(message);
- }
- };
- const clearModals = () => {
- landmarkDetailsModal.current.close();
- voiceModal.current.close();
- landmarkAddModal.current.close();
- };
- return(
- <View style={styles.container}>
- <View style={styles.mapHeader}>
- <TouchableOpacity style={{width: '25%', height: '100%', justifyContent: 'center'}} onPress={() => {clearModals(); navigation.jumpTo('Account');}}>
- <Image style={{width: 40, height: 40, borderRadius: 100, marginLeft: 20}} source={require('../assets/default-pfp.png')}/>
- </TouchableOpacity>
- <Text style={{color: 'white', fontSize: 20, marginRight: 20}}>atlas</Text>
- </View>
- <View style={styles.mapContainer}>
- <MapboxGL.MapView
- ref={map}
- style={styles.mapbox}
- onPress={e => handleMapTouch(e)}
- // onLongPress={p => promptAddPlace(p.geometry.coordinates)}
- >
- <MapboxGL.UserLocation/>
- <MapboxGL.Camera followUserLocation followUserMode={'normal'} zoomLevel={9} centerCoordinate={center} />
- {tempPoint &&
- <MapboxGL.PointAnnotation id="temp" coordinate={tempPoint} />
- }
- {state.landmarks.map(landmark => {
- return (
- <MapboxGL.MarkerView key={landmark.id} id={landmark.id} coordinate={[landmark.longitude, landmark.latitude]} >
- <View style={{width: 25, height: 20}}>
- <TouchableOpacity onPress={() => showLandmarkDetails(landmark)} >
- <Image source={Icons[landmark.icon]} style={{width: 22, height: 30}} />
- </TouchableOpacity>
- </View>
- </MapboxGL.MarkerView>
- )})}
- <MapboxGL.ShapeSource onPress={e => handlePlaceTouch(e)} id="test" shape={places}>
- <MapboxGL.FillLayer id="test" style={{fillColor: 'black', fillOpacity: .3}}/>
- </MapboxGL.ShapeSource>
- </MapboxGL.MapView>
- <TouchableOpacity style={styles.micBtn} name="microphone" onPress={openVoiceModal}>
- <Icon name='microphone' size={20} color='white' />
- </TouchableOpacity>
- </View>
- {state.locationPermission ? <VoiceView ref={voiceModal} changeSelectedLandmark={changeSelectedLandmark} editLandmark={editLandmark} style={styles.voiceModal} onPress={openVoiceModal}/> : null}
- <Modal position={"bottom"} ref={landmarkDetailsModal} style={{height: 520}} backButtonClose={true} >
- <LandmarkDetails closeModal={closeModal} editLandmark={editLandmark}/>
- </Modal>
- <Modal position={"bottom"} ref={mapMenuModal} style={styles.menuModal} backButtonClose={true}>
- <Text style={styles.addTitle}>What would you like to do?</Text>
- <View style={{justifyContent: 'space-between', alignItems: 'flex-end',}}>
- <TouchableOpacity style={{marginRight: 30, marginTop: 20}} onPress={addLandmark}>
- <Text style={{color: 'white'}}>Add landmark</Text>
- </TouchableOpacity>
- <ModalDropdown style={{marginRight: 30, marginTop: 20}} defaultValue={selectedPlaces.map(p => p.properties.name)[0]} defaultIndex={0} options={selectedPlaces.map(p => p.properties.name)}/>
- <TouchableOpacity style={{marginRight: 30, marginTop: 20}} onPress={() => landmarkAddModal.current.close()}>
- <Text style={{color: 'white'}}>Cancel</Text>
- </TouchableOpacity>
- </View>
- </Modal>
- <Modal style={styles.addModal} position={"bottom"} ref={landmarkAddModal} onClosed={removeTempPoint} backButtonClose={true}>
- <Text style={styles.addTitle}>Add landmark here?</Text>
- <View style={{flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center', }}>
- <TouchableOpacity style={{marginRight: 40, height: 50}} onPress={() => landmarkAddModal.current.close()}>
- <Text style={{color: 'white'}}>Cancel</Text>
- </TouchableOpacity>
- <TouchableOpacity style={{marginRight: 40, height: 50}} onPress={addLandmark}>
- <Text style={{color: 'white'}}>Add</Text>
- </TouchableOpacity>
- </View>
- </Modal>
- </View>
- )
- }
- const styles = StyleSheet.create({
- container: {
- flex: 1
- },
- mapHeader: {
- justifyContent: 'space-between',
- flexDirection: 'row',
- alignItems: 'center',
- height: '9%',
- zIndex: 5,
- backgroundColor: '#df3f3f',
- },
- mapContainer: {
- height: '100%',
- width: '100%',
- backgroundColor: 'white',
- },
- markerContainer: {
- height: 50,
- width: 50,
- backgroundColor: 'white',
- },
- markerImg: {
- height: 25,
- width: 25
- },
- mapbox: {
- flex: 1,
- },
- micBtn: {
- position: 'absolute',
- backgroundColor: '#df3f3f',
- justifyContent: 'center',
- alignItems: 'center',
- borderRadius: 40,
- bottom: 70,
- right: 20,
- zIndex: 5,
- width: 60,
- height: 60,
- },
- menuModal: {
- backgroundColor: '#df3f3f',
- height: 200,
- },
- addModal: {
- backgroundColor: '#df3f3f',
- height: 100,
- },
- voiceModal: {
- backgroundColor: '#df3f3f',
- height: 200,
- },
- addTitle: {
- color: 'white',
- fontSize: 20,
- marginTop: 20,
- marginLeft: 20
- }
- })
-
- export default Map;
-
|