Kliko/app/(tabs)/map.tsx
2024-08-13 13:11:28 +02:00

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,
},
} );