sửa lỗi hiển thị polyline, polygon ở map, thêm component ScanQrCode
This commit is contained in:
6
app.json
6
app.json
@@ -39,6 +39,12 @@
|
|||||||
"backgroundColor": "#000000"
|
"backgroundColor": "#000000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"expo-camera",
|
||||||
|
{
|
||||||
|
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ import MapView, { Circle, Marker } from "react-native-maps";
|
|||||||
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const [gpsData, setGpsData] = useState<Model.GPSResonse | undefined>(
|
const [gpsData, setGpsData] = useState<Model.GPSResonse | null>(
|
||||||
undefined
|
null
|
||||||
);
|
);
|
||||||
const [alarmData, setAlarmData] = useState<Model.AlarmResponse | null>(null);
|
const [alarmData, setAlarmData] = useState<Model.AlarmResponse | null>(null);
|
||||||
const [entityData, setEntityData] = useState<
|
const [entityData, setEntityData] = useState<
|
||||||
@@ -81,7 +81,7 @@ export default function HomeScreen() {
|
|||||||
// console.log("GPS Data: ", gpsData);
|
// console.log("GPS Data: ", gpsData);
|
||||||
setGpsData(gpsData);
|
setGpsData(gpsData);
|
||||||
} else {
|
} else {
|
||||||
setGpsData(undefined);
|
setGpsData(null);
|
||||||
setPolygonCoordinates([]);
|
setPolygonCoordinates([]);
|
||||||
setPolylineCoordinates(null);
|
setPolylineCoordinates(null);
|
||||||
}
|
}
|
||||||
@@ -133,23 +133,6 @@ export default function HomeScreen() {
|
|||||||
// console.log("Unsubscribed EVENT_TRACK_POINTS_DATA");
|
// console.log("Unsubscribed EVENT_TRACK_POINTS_DATA");
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (polylineCoordinates !== null) {
|
|
||||||
console.log("Polyline Khac null");
|
|
||||||
} else {
|
|
||||||
console.log("Polyline null");
|
|
||||||
}
|
|
||||||
}, [polylineCoordinates]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (polygonCoordinates.length > 0) {
|
|
||||||
console.log("Polygon Khac null");
|
|
||||||
} else {
|
|
||||||
console.log("Polygon null");
|
|
||||||
}
|
|
||||||
}, [polygonCoordinates]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPolylineCoordinates(null);
|
setPolylineCoordinates(null);
|
||||||
setPolygonCoordinates([]);
|
setPolygonCoordinates([]);
|
||||||
@@ -336,7 +319,7 @@ export default function HomeScreen() {
|
|||||||
latitude: point.lat,
|
latitude: point.lat,
|
||||||
longitude: point.lon,
|
longitude: point.lon,
|
||||||
}}
|
}}
|
||||||
zIndex={50}
|
// zIndex={50}
|
||||||
// radius={platform === IOS_PLATFORM ? 200 : 50}
|
// radius={platform === IOS_PLATFORM ? 200 : 50}
|
||||||
radius={circleRadius}
|
radius={circleRadius}
|
||||||
strokeColor="rgba(16, 85, 201, 0.7)"
|
strokeColor="rgba(16, 85, 201, 0.7)"
|
||||||
@@ -347,13 +330,14 @@ export default function HomeScreen() {
|
|||||||
})}
|
})}
|
||||||
{polylineCoordinates && (
|
{polylineCoordinates && (
|
||||||
<PolylineWithLabel
|
<PolylineWithLabel
|
||||||
|
key={`polyline-${gpsData?.lat || 0}-${gpsData?.lon || 0}`}
|
||||||
coordinates={polylineCoordinates.coordinates}
|
coordinates={polylineCoordinates.coordinates}
|
||||||
label={polylineCoordinates.label}
|
label={polylineCoordinates.label}
|
||||||
content={polylineCoordinates.content}
|
content={polylineCoordinates.content}
|
||||||
strokeColor="#FF5733"
|
strokeColor="#FF5733"
|
||||||
strokeWidth={4}
|
strokeWidth={4}
|
||||||
showDistance={false}
|
showDistance={false}
|
||||||
zIndex={50}
|
// zIndex={50}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{polygonCoordinates.length > 0 && (
|
{polygonCoordinates.length > 0 && (
|
||||||
@@ -367,22 +351,23 @@ export default function HomeScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PolygonWithLabel
|
<PolygonWithLabel
|
||||||
key={polygonKey}
|
key={`polygon-${index}-${gpsData?.lat || 0}-${gpsData?.lon || 0}`}
|
||||||
coordinates={polygon.coordinates}
|
coordinates={polygon.coordinates}
|
||||||
label={polygon.label}
|
label={polygon.label}
|
||||||
content={polygon.content}
|
content={polygon.content}
|
||||||
fillColor="rgba(16, 85, 201, 0.6)"
|
fillColor="rgba(16, 85, 201, 0.6)"
|
||||||
strokeColor="rgba(16, 85, 201, 0.8)"
|
strokeColor="rgba(16, 85, 201, 0.8)"
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
zIndex={50}
|
// zIndex={50}
|
||||||
zoomLevel={zoomLevel}
|
zoomLevel={zoomLevel}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{gpsData !== undefined && (
|
{gpsData !== null && (
|
||||||
<Marker
|
<Marker
|
||||||
|
key={platform === IOS_PLATFORM ? `${gpsData.lat}-${gpsData.lon}` : "gps-data"}
|
||||||
coordinate={{
|
coordinate={{
|
||||||
latitude: gpsData.lat,
|
latitude: gpsData.lat,
|
||||||
longitude: gpsData.lon,
|
longitude: gpsData.lon,
|
||||||
@@ -392,12 +377,13 @@ export default function HomeScreen() {
|
|||||||
? "Tàu của mình - iOS"
|
? "Tàu của mình - iOS"
|
||||||
: "Tàu của mình - Android"
|
: "Tàu của mình - Android"
|
||||||
}
|
}
|
||||||
zIndex={200}
|
zIndex={20}
|
||||||
anchor={
|
anchor={
|
||||||
platform === IOS_PLATFORM
|
platform === IOS_PLATFORM
|
||||||
? { x: 0.5, y: 0.5 }
|
? { x: 0.5, y: 0.5 }
|
||||||
: { x: 0.6, y: 0.4 }
|
: { x: 0.6, y: 0.4 }
|
||||||
}
|
}
|
||||||
|
tracksViewChanges={platform === IOS_PLATFORM ? true : undefined}
|
||||||
>
|
>
|
||||||
<View className="w-8 h-8 items-center justify-center">
|
<View className="w-8 h-8 items-center justify-center">
|
||||||
<View style={styles.pingContainer}>
|
<View style={styles.pingContainer}>
|
||||||
@@ -418,6 +404,7 @@ export default function HomeScreen() {
|
|||||||
alarmData?.level || 0,
|
alarmData?.level || 0,
|
||||||
gpsData.fishing
|
gpsData.fishing
|
||||||
);
|
);
|
||||||
|
// console.log("Ship icon:", icon, "for level:", alarmData?.level, "fishing:", gpsData.fishing);
|
||||||
return typeof icon === "string" ? { uri: icon } : icon;
|
return typeof icon === "string" ? { uri: icon } : icon;
|
||||||
})()}
|
})()}
|
||||||
style={{
|
style={{
|
||||||
@@ -442,7 +429,7 @@ export default function HomeScreen() {
|
|||||||
<View className="absolute top-14 right-2 shadow-md">
|
<View className="absolute top-14 right-2 shadow-md">
|
||||||
<SosButton />
|
<SosButton />
|
||||||
</View>
|
</View>
|
||||||
<GPSInfoPanel gpsData={gpsData} />
|
<GPSInfoPanel gpsData={gpsData!} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,51 @@
|
|||||||
import { Platform, ScrollView, StyleSheet, Text, View } from "react-native";
|
import ScanQRCode from "@/components/ScanQRCode";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Platform,
|
||||||
|
Pressable,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
export default function Sensor() {
|
export default function Sensor() {
|
||||||
|
const [scanModalVisible, setScanModalVisible] = useState(false);
|
||||||
|
const [scannedData, setScannedData] = useState<string | null>(null);
|
||||||
|
const handleQRCodeScanned = (data: string) => {
|
||||||
|
setScannedData(data);
|
||||||
|
// Alert.alert("QR Code Scanned", `Result: ${data}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScanPress = () => {
|
||||||
|
setScanModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1 }}>
|
<SafeAreaView style={{ flex: 1 }}>
|
||||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.titleText}>Cảm biến trên tàu</Text>
|
<Text style={styles.titleText}>Cảm biến trên tàu</Text>
|
||||||
|
|
||||||
|
<Pressable style={styles.scanButton} onPress={handleScanPress}>
|
||||||
|
<Text style={styles.scanButtonText}>📱 Scan QR Code</Text>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
{scannedData && (
|
||||||
|
<View style={styles.resultContainer}>
|
||||||
|
<Text style={styles.resultLabel}>Last Scanned:</Text>
|
||||||
|
<Text style={styles.resultText}>{scannedData}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
<ScanQRCode
|
||||||
|
visible={scanModalVisible}
|
||||||
|
onClose={() => setScanModalVisible(false)}
|
||||||
|
onScanned={handleQRCodeScanned}
|
||||||
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -25,11 +62,48 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
fontWeight: "700",
|
fontWeight: "700",
|
||||||
lineHeight: 40,
|
lineHeight: 40,
|
||||||
marginBottom: 10,
|
marginBottom: 30,
|
||||||
fontFamily: Platform.select({
|
fontFamily: Platform.select({
|
||||||
ios: "System",
|
ios: "System",
|
||||||
android: "Roboto",
|
android: "Roboto",
|
||||||
default: "System",
|
default: "System",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
scanButton: {
|
||||||
|
backgroundColor: "#007AFF",
|
||||||
|
paddingVertical: 14,
|
||||||
|
paddingHorizontal: 30,
|
||||||
|
borderRadius: 10,
|
||||||
|
marginVertical: 15,
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.25,
|
||||||
|
shadowRadius: 3.84,
|
||||||
|
elevation: 5,
|
||||||
|
},
|
||||||
|
scanButtonText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
resultContainer: {
|
||||||
|
marginTop: 30,
|
||||||
|
padding: 15,
|
||||||
|
backgroundColor: "#f0f0f0",
|
||||||
|
borderRadius: 10,
|
||||||
|
minWidth: "80%",
|
||||||
|
},
|
||||||
|
resultLabel: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: "600",
|
||||||
|
color: "#666",
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
resultText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#333",
|
||||||
|
fontFamily: "Menlo",
|
||||||
|
fontWeight: "500",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
228
components/ScanQRCode.tsx
Normal file
228
components/ScanQRCode.tsx
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import { CameraView, useCameraPermissions } from "expo-camera";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Modal,
|
||||||
|
Pressable,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
|
interface ScanQRCodeProps {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onScanned: (data: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ScanQRCode({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
onScanned,
|
||||||
|
}: ScanQRCodeProps) {
|
||||||
|
const [permission, requestPermission] = useCameraPermissions();
|
||||||
|
const [scanned, setScanned] = useState(false);
|
||||||
|
const cameraRef = useRef(null);
|
||||||
|
|
||||||
|
// Request camera permission when component mounts or when visible changes to true
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && !permission?.granted) {
|
||||||
|
requestPermission();
|
||||||
|
}
|
||||||
|
}, [visible, permission, requestPermission]);
|
||||||
|
|
||||||
|
// Reset scanned state when modal opens
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
setScanned(false);
|
||||||
|
}
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
const handleBarCodeScanned = ({
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
type: string;
|
||||||
|
data: string;
|
||||||
|
}) => {
|
||||||
|
if (!scanned) {
|
||||||
|
setScanned(true);
|
||||||
|
onScanned(data);
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!permission) {
|
||||||
|
return (
|
||||||
|
<Modal visible={visible} transparent animationType="slide">
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
<ActivityIndicator size="large" color="#0000ff" />
|
||||||
|
<Text style={styles.loadingText}>
|
||||||
|
Requesting camera permission...
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!permission.granted) {
|
||||||
|
return (
|
||||||
|
<Modal visible={visible} transparent animationType="slide">
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.permissionContainer}>
|
||||||
|
<Text style={styles.permissionTitle}>
|
||||||
|
Camera Permission Required
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.permissionText}>
|
||||||
|
This app needs camera access to scan QR codes. Please allow camera
|
||||||
|
access in your settings.
|
||||||
|
</Text>
|
||||||
|
<Pressable
|
||||||
|
style={styles.button}
|
||||||
|
onPress={() => requestPermission()}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Request Permission</Text>
|
||||||
|
</Pressable>
|
||||||
|
<Pressable
|
||||||
|
style={[styles.button, styles.cancelButton]}
|
||||||
|
onPress={onClose}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Cancel</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal visible={visible} transparent animationType="slide">
|
||||||
|
<CameraView
|
||||||
|
ref={cameraRef}
|
||||||
|
style={styles.camera}
|
||||||
|
onBarcodeScanned={handleBarCodeScanned}
|
||||||
|
barcodeScannerSettings={{
|
||||||
|
barcodeTypes: ["qr"],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={styles.overlay}>
|
||||||
|
<View style={styles.unfocusedContainer} />
|
||||||
|
<View style={styles.focusedRow}>
|
||||||
|
<View style={styles.focusedContainer} />
|
||||||
|
</View>
|
||||||
|
<View style={styles.unfocusedContainer} />
|
||||||
|
|
||||||
|
<View style={styles.bottomContainer}>
|
||||||
|
<Text style={styles.scanningText}>
|
||||||
|
{/* Align QR code within the frame */}
|
||||||
|
Đặt mã QR vào khung hình
|
||||||
|
</Text>
|
||||||
|
<Pressable style={styles.closeButton} onPress={onClose}>
|
||||||
|
<Text style={styles.closeButtonText}>✕ Đóng</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</CameraView>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
loadingContainer: {
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
loadingText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
permissionContainer: {
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
marginHorizontal: 20,
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 20,
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 15,
|
||||||
|
},
|
||||||
|
permissionTitle: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: "600",
|
||||||
|
color: "#333",
|
||||||
|
},
|
||||||
|
permissionText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#666",
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: "#007AFF",
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 30,
|
||||||
|
borderRadius: 8,
|
||||||
|
width: "100%",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
cancelButton: {
|
||||||
|
backgroundColor: "#666",
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
},
|
||||||
|
camera: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||||
|
},
|
||||||
|
unfocusedContainer: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
focusedRow: {
|
||||||
|
height: "80%",
|
||||||
|
width: "100%",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
focusedContainer: {
|
||||||
|
aspectRatio: 1,
|
||||||
|
width: "70%",
|
||||||
|
borderColor: "#00ff00",
|
||||||
|
borderWidth: 3,
|
||||||
|
borderRadius: 10,
|
||||||
|
},
|
||||||
|
bottomContainer: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingBottom: 40,
|
||||||
|
gap: 20,
|
||||||
|
},
|
||||||
|
scanningText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "500",
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
closeButtonText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: "600",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -61,10 +61,10 @@ const GPSInfoPanel = ({ gpsData }: GPSInfoPanelProps) => {
|
|||||||
{/* Nút toggle ở top-right */}
|
{/* Nút toggle ở top-right */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={togglePanel}
|
onPress={togglePanel}
|
||||||
className="absolute top-2 right-2 z-10 bg-white rounded-full p-1 shadow-sm"
|
className="absolute top-2 right-2 z-10 bg-white rounded-full p-1"
|
||||||
>
|
>
|
||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name={isExpanded ? "expand-more" : "expand-less"}
|
name={isExpanded ? "close" : "close"}
|
||||||
size={20}
|
size={20}
|
||||||
color="#666"
|
color="#666"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ export const PolygonWithLabel: React.FC<PolygonWithLabelProps> = ({
|
|||||||
|
|
||||||
const paddingScale = Math.max(Math.pow(2, (zoomLevel - 10) * 0.2), 0.5);
|
const paddingScale = Math.max(Math.pow(2, (zoomLevel - 10) * 0.2), 0.5);
|
||||||
const minWidthScale = Math.max(Math.pow(2, (zoomLevel - 10) * 0.25), 0.9);
|
const minWidthScale = Math.max(Math.pow(2, (zoomLevel - 10) * 0.25), 0.9);
|
||||||
markerRef.current?.showCallout();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Polygon
|
<Polygon
|
||||||
@@ -70,7 +69,7 @@ export const PolygonWithLabel: React.FC<PolygonWithLabelProps> = ({
|
|||||||
ref={markerRef}
|
ref={markerRef}
|
||||||
coordinate={centerPoint}
|
coordinate={centerPoint}
|
||||||
zIndex={50}
|
zIndex={50}
|
||||||
tracksViewChanges={false}
|
tracksViewChanges={platform === ANDROID_PLATFORM ? false : true}
|
||||||
anchor={{ x: 0.5, y: 0.5 }}
|
anchor={{ x: 0.5, y: 0.5 }}
|
||||||
title={platform === ANDROID_PLATFORM ? label : undefined}
|
title={platform === ANDROID_PLATFORM ? label : undefined}
|
||||||
description={platform === ANDROID_PLATFORM ? content : undefined}
|
description={platform === ANDROID_PLATFORM ? content : undefined}
|
||||||
@@ -78,7 +77,6 @@ export const PolygonWithLabel: React.FC<PolygonWithLabelProps> = ({
|
|||||||
<View style={styles.markerContainer}>
|
<View style={styles.markerContainer}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.labelContainer,
|
|
||||||
{
|
{
|
||||||
paddingHorizontal: 5 * paddingScale,
|
paddingHorizontal: 5 * paddingScale,
|
||||||
paddingVertical: 5 * paddingScale,
|
paddingVertical: 5 * paddingScale,
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ export const PolylineWithLabel: React.FC<PolylineWithLabelProps> = ({
|
|||||||
? ` (${distance.toFixed(2)}km)`
|
? ` (${distance.toFixed(2)}km)`
|
||||||
: `${distance.toFixed(2)}km`;
|
: `${distance.toFixed(2)}km`;
|
||||||
}
|
}
|
||||||
markerRef.current?.showCallout();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Polyline
|
<Polyline
|
||||||
@@ -61,7 +60,7 @@ export const PolylineWithLabel: React.FC<PolylineWithLabelProps> = ({
|
|||||||
ref={markerRef}
|
ref={markerRef}
|
||||||
coordinate={middlePoint}
|
coordinate={middlePoint}
|
||||||
zIndex={zIndex + 10}
|
zIndex={zIndex + 10}
|
||||||
tracksViewChanges={false}
|
tracksViewChanges={platform === ANDROID_PLATFORM ? false : true}
|
||||||
anchor={{ x: 0.5, y: 0.5 }}
|
anchor={{ x: 0.5, y: 0.5 }}
|
||||||
title={platform === ANDROID_PLATFORM ? label : undefined}
|
title={platform === ANDROID_PLATFORM ? label : undefined}
|
||||||
description={platform === ANDROID_PLATFORM ? content : undefined}
|
description={platform === ANDROID_PLATFORM ? content : undefined}
|
||||||
|
|||||||
@@ -90,13 +90,11 @@ const SosButton = () => {
|
|||||||
|
|
||||||
const handleClickButton = async (isActive: boolean) => {
|
const handleClickButton = async (isActive: boolean) => {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
console.log("Active");
|
|
||||||
const resp = await queryDeleteSos();
|
const resp = await queryDeleteSos();
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
await getSosData();
|
await getSosData();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("No Active");
|
|
||||||
setSelectedSosMessage(11); // Mặc định chọn lý do ma: 11
|
setSelectedSosMessage(11); // Mặc định chọn lý do ma: 11
|
||||||
setShowConfirmSosDialog(true);
|
setShowConfirmSosDialog(true);
|
||||||
}
|
}
|
||||||
@@ -255,7 +253,6 @@ const SosButton = () => {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSelectedSosMessage(item.ma);
|
setSelectedSosMessage(item.ma);
|
||||||
setShowDropdown(false);
|
setShowDropdown(false);
|
||||||
|
|
||||||
// Clear custom message nếu chọn khác lý do
|
// Clear custom message nếu chọn khác lý do
|
||||||
if (item.ma !== 999) {
|
if (item.ma !== 999) {
|
||||||
setCustomMessage("");
|
setCustomMessage("");
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const setRouterInstance = (router: Router) => {
|
|||||||
export const handle401 = () => {
|
export const handle401 = () => {
|
||||||
if (routerInstance) {
|
if (routerInstance) {
|
||||||
removeStorageItem(TOKEN);
|
removeStorageItem(TOKEN);
|
||||||
(routerInstance as any).replace("/login");
|
(routerInstance as any).replace("/auth/login");
|
||||||
} else {
|
} else {
|
||||||
console.warn("Router instance not set, cannot redirect to login");
|
console.warn("Router instance not set, cannot redirect to login");
|
||||||
}
|
}
|
||||||
|
|||||||
21
package-lock.json
generated
21
package-lock.json
generated
@@ -22,6 +22,7 @@
|
|||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
"expo": "~54.0.20",
|
"expo": "~54.0.20",
|
||||||
|
"expo-camera": "~17.0.9",
|
||||||
"expo-constants": "~18.0.10",
|
"expo-constants": "~18.0.10",
|
||||||
"expo-font": "~14.0.9",
|
"expo-font": "~14.0.9",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
@@ -8463,6 +8464,26 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-camera": {
|
||||||
|
"version": "17.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-17.0.9.tgz",
|
||||||
|
"integrity": "sha512-KgticPGurqEsaPBIwbG0T6mzAVnqZasDdM/6OoJt5zPh6tWB09+th6cBF1WafIBMPy8AWbfyUQSqQXqOrNJClg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"invariant": "^2.2.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-web": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-native-web": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-constants": {
|
"node_modules/expo-constants": {
|
||||||
"version": "18.0.10",
|
"version": "18.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.10.tgz",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
"expo": "~54.0.20",
|
"expo": "~54.0.20",
|
||||||
|
"expo-camera": "~17.0.9",
|
||||||
"expo-constants": "~18.0.10",
|
"expo-constants": "~18.0.10",
|
||||||
"expo-font": "~14.0.9",
|
"expo-font": "~14.0.9",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
|
|||||||
Reference in New Issue
Block a user