sửa lỗi hiển thị polyline, polygon ở map, thêm component ScanQrCode

This commit is contained in:
Tran Anh Tuan
2025-11-05 16:23:47 +07:00
parent 300271fce7
commit 62b18e5bc0
11 changed files with 351 additions and 40 deletions

228
components/ScanQRCode.tsx Normal file
View 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 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",
},
});

View File

@@ -61,10 +61,10 @@ const GPSInfoPanel = ({ gpsData }: GPSInfoPanelProps) => {
{/* Nút toggle ở top-right */}
<TouchableOpacity
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
name={isExpanded ? "expand-more" : "expand-less"}
name={isExpanded ? "close" : "close"}
size={20}
color="#666"
/>

View File

@@ -55,7 +55,6 @@ export const PolygonWithLabel: React.FC<PolygonWithLabelProps> = ({
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);
markerRef.current?.showCallout();
return (
<>
<Polygon
@@ -70,7 +69,7 @@ export const PolygonWithLabel: React.FC<PolygonWithLabelProps> = ({
ref={markerRef}
coordinate={centerPoint}
zIndex={50}
tracksViewChanges={false}
tracksViewChanges={platform === ANDROID_PLATFORM ? false : true}
anchor={{ x: 0.5, y: 0.5 }}
title={platform === ANDROID_PLATFORM ? label : undefined}
description={platform === ANDROID_PLATFORM ? content : undefined}
@@ -78,7 +77,6 @@ export const PolygonWithLabel: React.FC<PolygonWithLabelProps> = ({
<View style={styles.markerContainer}>
<View
style={[
styles.labelContainer,
{
paddingHorizontal: 5 * paddingScale,
paddingVertical: 5 * paddingScale,

View File

@@ -47,7 +47,6 @@ export const PolylineWithLabel: React.FC<PolylineWithLabelProps> = ({
? ` (${distance.toFixed(2)}km)`
: `${distance.toFixed(2)}km`;
}
markerRef.current?.showCallout();
return (
<>
<Polyline
@@ -61,7 +60,7 @@ export const PolylineWithLabel: React.FC<PolylineWithLabelProps> = ({
ref={markerRef}
coordinate={middlePoint}
zIndex={zIndex + 10}
tracksViewChanges={false}
tracksViewChanges={platform === ANDROID_PLATFORM ? false : true}
anchor={{ x: 0.5, y: 0.5 }}
title={platform === ANDROID_PLATFORM ? label : undefined}
description={platform === ANDROID_PLATFORM ? content : undefined}

View File

@@ -90,13 +90,11 @@ const SosButton = () => {
const handleClickButton = async (isActive: boolean) => {
if (isActive) {
console.log("Active");
const resp = await queryDeleteSos();
if (resp.status === 200) {
await getSosData();
}
} else {
console.log("No Active");
setSelectedSosMessage(11); // Mặc định chọn lý do ma: 11
setShowConfirmSosDialog(true);
}
@@ -255,7 +253,6 @@ const SosButton = () => {
onPress={() => {
setSelectedSosMessage(item.ma);
setShowDropdown(false);
// Clear custom message nếu chọn khác lý do
if (item.ma !== 999) {
setCustomMessage("");