242 lines
8.9 KiB
TypeScript
242 lines
8.9 KiB
TypeScript
import React, { useEffect, useRef, useState } from 'react';
|
|
import { Image, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, View, Dimensions } from 'react-native';
|
|
import Mapbox, { Callout, Camera, MapView, PointAnnotation } from "@rnmapbox/maps";
|
|
import { useSelector } from 'react-redux';
|
|
import { useIsFocused } from '@react-navigation/core';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
Mapbox.setAccessToken( "pk.eyJ1IjoibWFhcnRlbnZyOTgiLCJhIjoiY2x6ZDFqMGp1MGVyejJrczhqcXpvYm9iYiJ9.XvYcL62dWiJQiFmG6mOoug" );
|
|
|
|
import { ThemedText } from '@/src/components/themed/ThemedText';
|
|
import { ThemedView } from '@/src/components/themed/ThemedView';
|
|
import { Colors } from '@/src/constants/Colors';
|
|
import { useColorScheme } from '@/src/hooks/useColorScheme';
|
|
import List from '@/src/components/List';
|
|
import { Request } from '@/src/services/request';
|
|
|
|
export default function MapScreen() {
|
|
const colorScheme = useColorScheme() ?? 'light';
|
|
const isFocused = useIsFocused();
|
|
const session = useSelector( (state: any) => state.data.session );
|
|
const { t } = useTranslation();
|
|
const mapRef = useRef<MapView>( null );
|
|
|
|
const [ types, setTypes ] = useState<any>( [] );
|
|
const [ typesSet, setTypesSet ] = useState( false );
|
|
const [ coordinates, setCoordinates ] = useState<any>( [] );
|
|
const [ markers, setMarkers ] = useState<any>( [] );
|
|
const [ activeCalloutId, setActiveCalloutId ] = useState( null );
|
|
|
|
// Load session
|
|
useEffect( () => {
|
|
setCoordinates( [ session.coordinates.longitude, session.coordinates.latitude ] );
|
|
}, [ session, isFocused ] );
|
|
|
|
// Setup mapbox
|
|
useEffect( () => {
|
|
Mapbox.setTelemetryEnabled( false );
|
|
}, [] );
|
|
|
|
// Load markers and types
|
|
const loadMarkers = () => {
|
|
handleCloseAllCallouts();
|
|
|
|
mapRef.current?.getVisibleBounds().then( (bounds) => {
|
|
Request
|
|
.post( 'locations', {
|
|
topLeft: bounds[ 0 ],
|
|
bottomRight: bounds[ 1 ],
|
|
} )
|
|
.then( (response) => {
|
|
const { locations, types } = response;
|
|
|
|
// Set types
|
|
if (!typesSet) {
|
|
const typesList: any[] = [];
|
|
types.forEach( (type: any) => {
|
|
typesList.push( {
|
|
name: type.name,
|
|
image: type.image,
|
|
isEnabled: false,
|
|
type: type.config_name,
|
|
} );
|
|
} )
|
|
setTypes( typesList );
|
|
setTypesSet( true );
|
|
}
|
|
|
|
// Set markers
|
|
setMarkers( locations );
|
|
} )
|
|
} );
|
|
}
|
|
|
|
// Enable/disable type
|
|
const toggleSwitch = (index: any) => {
|
|
handleCloseAllCallouts();
|
|
|
|
const newData = types.map( (item: any, key: any) =>
|
|
key === index ? { ...item, isEnabled: !item.isEnabled } : item
|
|
);
|
|
|
|
setTypes( newData );
|
|
};
|
|
|
|
// Get all types that are active
|
|
const getActiveTypes = (): Array<String> => {
|
|
const list: any[] = [];
|
|
|
|
types.forEach( (type: any) => {
|
|
if (type.isEnabled) {
|
|
list.push( type.type );
|
|
}
|
|
} );
|
|
|
|
return list;
|
|
}
|
|
|
|
const handleCalloutPress = (id: any) => {
|
|
console.log('set active', id);
|
|
setActiveCalloutId( id );
|
|
};
|
|
|
|
const handleCloseAllCallouts = () => {
|
|
setActiveCalloutId( null );
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={{ flex: 1, backgroundColor: Colors[ colorScheme ].background }}>
|
|
<ThemedView style={styles.container}>
|
|
<ThemedView>
|
|
<ThemedView style={styles.titleContainer}>
|
|
<ThemedText type="title">{t( "garbage-bins" )}</ThemedText>
|
|
</ThemedView>
|
|
|
|
<ThemedView style={styles.titleContainer}>
|
|
<ThemedText type="title" style={{ color: Colors[ colorScheme ].tint }}>{t( "nearby" )}</ThemedText>
|
|
</ThemedView>
|
|
|
|
<ThemedView style={styles.mapContainer}>
|
|
<MapView
|
|
ref={mapRef}
|
|
style={styles.map}
|
|
logoEnabled={false}
|
|
scaleBarEnabled={false}
|
|
attributionEnabled={false}
|
|
onMapIdle={loadMarkers}
|
|
>
|
|
<Camera
|
|
centerCoordinate={coordinates.length == 2 ? coordinates : undefined}
|
|
zoomLevel={13}
|
|
animationMode={'none'}
|
|
/>
|
|
{markers.filter( (marker: any) => getActiveTypes().includes( marker.waste_type ) ).map( (marker: any, index: any) => (
|
|
<PointAnnotation
|
|
key={marker.id.toString()}
|
|
id={marker.id.toString()}
|
|
coordinate={marker.coordinate}
|
|
onSelected={() => handleCalloutPress( marker.id )}
|
|
>
|
|
<>
|
|
<View style={{ width: 50, height: 50 }}>
|
|
<Image
|
|
source={{ uri: marker.marker_image }}
|
|
style={{ width: 50, height: 50 }}
|
|
resizeMode="contain"
|
|
/>
|
|
</View>
|
|
|
|
{/*{activeCalloutId === marker.id && (*/}
|
|
{/* <Callout title={marker.number}>*/}
|
|
{/* <ThemedView style={styles.callout}>*/}
|
|
{/* <ThemedText>{marker.description + ' - ' + marker.street}</ThemedText>*/}
|
|
{/* </ThemedView>*/}
|
|
{/* </Callout>*/}
|
|
{/*)}*/}
|
|
</>
|
|
</PointAnnotation>
|
|
) )}
|
|
</MapView>
|
|
</ThemedView>
|
|
</ThemedView>
|
|
|
|
<ScrollView style={styles.listContainer}>
|
|
<List
|
|
data={types}
|
|
renderItem={(item: any, index: any) => (
|
|
<ThemedView style={styles.listItem} key={index}>
|
|
<View style={styles.listItemTitle}>
|
|
<Image source={{ uri: item.image }} style={styles.listImage}/>
|
|
<ThemedText type="default">{item.name}</ThemedText>
|
|
</View>
|
|
|
|
<Switch
|
|
trackColor={{ false: '#767577', true: Colors[ colorScheme ].tint }}
|
|
thumbColor={'#fff'}
|
|
value={item.isEnabled}
|
|
onValueChange={() => toggleSwitch( index )}
|
|
/>
|
|
</ThemedView>
|
|
)}/>
|
|
</ScrollView>
|
|
</ThemedView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create( {
|
|
container: {
|
|
marginTop: StatusBar.currentHeight,
|
|
paddingTop: 25,
|
|
flex: 1,
|
|
},
|
|
titleContainer: {
|
|
paddingLeft: 25,
|
|
paddingRight: 25,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
paddingBottom: 8,
|
|
},
|
|
mapContainer: {
|
|
marginTop: 15,
|
|
height: Dimensions.get( 'window' ).height / 3,
|
|
},
|
|
map: {
|
|
width: Dimensions.get( 'window' ).width,
|
|
height: Dimensions.get( 'window' ).height / 3,
|
|
flex: 1,
|
|
},
|
|
listContainer: {
|
|
flex: 1,
|
|
paddingTop: 20,
|
|
paddingLeft: 25,
|
|
paddingRight: 25,
|
|
},
|
|
listItem: {
|
|
flex: 1,
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingBottom: 20,
|
|
marginBottom: 20,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#f2f2f2',
|
|
},
|
|
listItemTitle: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
listImage: {
|
|
width: 30,
|
|
height: 30,
|
|
borderRadius: 5,
|
|
marginRight: 8,
|
|
},
|
|
callout: {
|
|
width: 200,
|
|
padding: 5,
|
|
},
|
|
} );
|