RouteB.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import React, { useEffect, useRef, useState } from 'react';
  2. import { StyleSheet, View } from 'react-native';
  3. import * as Location from 'expo-location';
  4. import * as MapLibreRN from '@maplibre/maplibre-react-native';
  5. import { SheetManager, useSheetRouteParams, useSheetRouter } from 'react-native-actions-sheet';
  6. import { Colors } from 'src/theme';
  7. import { VECTOR_MAP_HOST } from 'src/constants';
  8. import { ButtonVariants } from 'src/types/components';
  9. import { Button } from 'src/components';
  10. const RouteB = () => {
  11. const router = useSheetRouter('chat-attachments');
  12. const params = useSheetRouteParams('chat-attachments', 'route-b');
  13. const {
  14. onSendLocation,
  15. insetsBottom
  16. }: {
  17. onSendLocation: (coords: { latitude: number; longitude: number }) => void;
  18. insetsBottom: number;
  19. } = params;
  20. const [currentLocation, setCurrentLocation] = useState<{
  21. latitude: number;
  22. longitude: number;
  23. } | null>(null);
  24. const [selectedLocation, setSelectedLocation] = useState<{
  25. latitude: number;
  26. longitude: number;
  27. } | null>(null);
  28. const [loading, setLoading] = useState(true);
  29. const cameraRef = useRef<MapLibreRN.CameraRef | null>(null);
  30. const [mapDimensions, setMapDimensions] = useState({ width: 0, height: 0, x: 0, y: 0 });
  31. useEffect(() => {
  32. const getLocation = async () => {
  33. try {
  34. const { status } = await Location.requestForegroundPermissionsAsync();
  35. if (status !== 'granted') {
  36. return;
  37. }
  38. const location = await Location.getCurrentPositionAsync({});
  39. const coords = {
  40. latitude: location.coords.latitude,
  41. longitude: location.coords.longitude
  42. };
  43. setCurrentLocation(coords);
  44. setSelectedLocation(coords);
  45. } catch (err) {
  46. console.warn('Error fetching location:', err);
  47. }
  48. };
  49. getLocation();
  50. }, []);
  51. useEffect(() => {
  52. if (currentLocation) {
  53. const timeoutId = setTimeout(() => {
  54. if (cameraRef.current) {
  55. cameraRef.current.setCamera({
  56. centerCoordinate: [currentLocation.longitude, currentLocation.latitude],
  57. zoomLevel: 14,
  58. animationDuration: 500
  59. });
  60. setLoading(false);
  61. } else {
  62. console.warn('Camera ref is not available.');
  63. }
  64. }, 500);
  65. return () => clearTimeout(timeoutId);
  66. }
  67. }, [currentLocation]);
  68. const confirmLocation = () => {
  69. if (selectedLocation) {
  70. onSendLocation(selectedLocation);
  71. }
  72. SheetManager.hide('location-picker');
  73. SheetManager.hide('chat-attachments');
  74. };
  75. const sendCurrentLocation = () => {
  76. if (currentLocation) {
  77. onSendLocation(currentLocation);
  78. }
  79. SheetManager.hide('location-picker');
  80. SheetManager.hide('chat-attachments');
  81. };
  82. const handleRegionChange = (event: any) => {
  83. const { geometry } = event;
  84. if (geometry) {
  85. const [longitude, latitude] = geometry.coordinates;
  86. setSelectedLocation({ latitude, longitude });
  87. }
  88. };
  89. return (
  90. <View style={[styles.container, { paddingBottom: 8 + insetsBottom }]}>
  91. <View
  92. style={styles.mapContainer}
  93. onLayout={(event) => {
  94. const { x, y, width, height } = event.nativeEvent.layout;
  95. setMapDimensions({ x, y, width, height });
  96. }}
  97. >
  98. <MapLibreRN.MapView
  99. style={{ flex: 1 }}
  100. mapStyle={VECTOR_MAP_HOST + '/nomadmania-maps.json'}
  101. compassEnabled={false}
  102. rotateEnabled={false}
  103. attributionEnabled={false}
  104. onRegionDidChange={handleRegionChange}
  105. >
  106. <MapLibreRN.Camera ref={cameraRef} />
  107. {currentLocation && (
  108. <MapLibreRN.PointAnnotation
  109. id="currentLocation"
  110. coordinate={[currentLocation.longitude, currentLocation.latitude]}
  111. >
  112. <View style={styles.currentLocationMarker} />
  113. </MapLibreRN.PointAnnotation>
  114. )}
  115. </MapLibreRN.MapView>
  116. </View>
  117. <View
  118. style={[
  119. styles.centerMarker,
  120. { left: mapDimensions.width / 2 - 12, top: mapDimensions.height / 2 - 12 }
  121. ]}
  122. >
  123. <View style={styles.selectedLocationMarker} />
  124. </View>
  125. <View style={styles.mapActions}>
  126. <Button
  127. children="Send current location"
  128. onPress={sendCurrentLocation}
  129. variant={ButtonVariants.FILL}
  130. disabled={loading}
  131. />
  132. <Button
  133. children="Send selected location"
  134. onPress={confirmLocation}
  135. variant={ButtonVariants.OPACITY}
  136. containerStyles={{ backgroundColor: Colors.DARK_BLUE, borderColor: Colors.DARK_BLUE, }}
  137. textStyles={{ color: Colors.WHITE }}
  138. />
  139. <Button
  140. children="Close"
  141. onPress={() => router?.goBack()}
  142. variant={ButtonVariants.OPACITY}
  143. containerStyles={{ backgroundColor: Colors.WHITE, borderColor: '#B7C6CB' }}
  144. textStyles={{ color: Colors.DARK_BLUE }}
  145. />
  146. </View>
  147. </View>
  148. );
  149. };
  150. const styles = StyleSheet.create({
  151. container: {
  152. backgroundColor: Colors.FILL_LIGHT,
  153. minHeight: 600,
  154. gap: 16
  155. },
  156. mapContainer: { flex: 1 },
  157. mapActions: {
  158. flexDirection: 'column',
  159. gap: 8,
  160. paddingHorizontal: 16
  161. },
  162. currentLocationMarker: {
  163. width: 20,
  164. height: 20,
  165. backgroundColor: Colors.ORANGE,
  166. borderRadius: 10,
  167. borderWidth: 2,
  168. borderColor: Colors.WHITE
  169. },
  170. selectedLocationMarker: {
  171. width: 24,
  172. height: 24,
  173. backgroundColor: Colors.DARK_BLUE,
  174. borderRadius: 12,
  175. borderWidth: 2,
  176. borderColor: Colors.WHITE
  177. },
  178. centerMarker: {
  179. position: 'absolute',
  180. zIndex: 1000
  181. }
  182. });
  183. export default RouteB;