thêm chức năng Sos và thêm glustack-ui

This commit is contained in:
Tran Anh Tuan
2025-11-04 16:24:54 +07:00
parent e535aaa1e8
commit 2137925ba9
33 changed files with 5533 additions and 171 deletions

View File

@@ -1,7 +1,7 @@
import AlarmList from "@/components/AlarmList";
import { Link } from "expo-router";
import {
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
@@ -9,20 +9,49 @@ import {
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
const alarmExample = {
alarms: [
{
name: "Ngập nước có cảnh báo",
t: 1762226488,
level: 1,
id: "0:8:1",
},
{
name: "Tầu cảnh báo sos",
t: 1762226596,
level: 3,
id: "50:15",
},
{
name: "Khói có cảnh báo",
t: 1762226589,
level: 1,
id: "0:1:1",
},
{
name: "Cửa có cảnh báo",
t: 1762226547,
level: 1,
id: "0:7:1",
},
],
level: 3,
};
export default function Warning() {
return (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView contentContainerStyle={styles.scrollContent}>
<View style={styles.container}>
<Text style={styles.titleText}>Nhật Chuyến Đi</Text>
<View style={styles.container}>
<Text style={styles.titleText}>Nhật Chuyến Đi</Text>
<Link href="/modal" asChild>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Mở Modal</Text>
</TouchableOpacity>
</Link>
</View>
</ScrollView>
<Link href="/modal" asChild>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Mở Modal</Text>
</TouchableOpacity>
</Link>
<AlarmList alarmsData={alarmExample.alarms} />
</View>
</SafeAreaView>
);
}

View File

@@ -1,5 +1,9 @@
import GPSInfoPanel from "@/components/map/GPSInfoPanel";
import type { PolygonWithLabelProps } from "@/components/map/PolygonWithLabel";
import { PolygonWithLabel } from "@/components/map/PolygonWithLabel";
import type { PolylineWithLabelProps } from "@/components/map/PolylineWithLabel";
import { PolylineWithLabel } from "@/components/map/PolylineWithLabel";
import SosButton from "@/components/map/SosButton";
import {
ENTITY,
EVENT_ALARM_DATA,
@@ -30,12 +34,10 @@ import {
Animated,
Image as RNImage,
StyleSheet,
Text,
TouchableOpacity,
View,
View
} from "react-native";
import MapView, { Circle, Marker } from "react-native-maps";
import { SafeAreaView } from "react-native-safe-area-context";
export default function HomeScreen() {
const [gpsData, setGpsData] = useState<Model.GPSResonse | undefined>(
@@ -53,15 +55,16 @@ export default function HomeScreen() {
const [zoomLevel, setZoomLevel] = useState(10);
const [isFirstLoad, setIsFirstLoad] = useState(true);
const [polylineCoordinates, setPolylineCoordinates] = useState<
number[][] | undefined
PolylineWithLabelProps | undefined
>(undefined);
const [polygonCoordinates, setPolygonCoordinates] = useState<
number[][][] | undefined
PolygonWithLabelProps[] | undefined
>(undefined);
const platform = usePlatform();
const theme = useColorScheme();
const scale = useRef(new Animated.Value(0)).current;
const opacity = useRef(new Animated.Value(1)).current;
// console.log("Platform: ", platform);
// console.log("Theme: ", theme);
@@ -74,7 +77,12 @@ export default function HomeScreen() {
getBanzonesEventBus();
getTrackPointsEventBus();
const queryGpsData = (gpsData: Model.GPSResonse) => {
setGpsData(gpsData);
if (gpsData) {
// console.log("GPS Data: ", gpsData);
setGpsData(gpsData);
} else {
setGpsData(undefined);
}
};
const queryAlarmData = (alarmData: Model.AlarmResponse) => {
// console.log("Alarm Data: ", alarmData.alarms.length);
@@ -82,7 +90,6 @@ export default function HomeScreen() {
};
const queryEntityData = (entityData: Model.TransformedEntity[]) => {
// console.log("Entities Length Data: ", entityData.length);
setEntityData(entityData);
};
const queryBanzonesData = (banzoneData: Model.Zone[]) => {
@@ -92,32 +99,36 @@ export default function HomeScreen() {
};
const queryTrackPointsData = (TrackPointsData: Model.ShipTrackPoint[]) => {
// console.log("TrackPoints Data: ", TrackPointsData.length);
setTrackPointsData(TrackPointsData);
if (TrackPointsData && TrackPointsData.length > 0) {
setTrackPointsData(TrackPointsData);
} else {
setTrackPointsData(null);
}
};
eventBus.on(EVENT_GPS_DATA, queryGpsData);
console.log("Registering event handlers in HomeScreen");
// console.log("Registering event handlers in HomeScreen");
eventBus.on(EVENT_GPS_DATA, queryGpsData);
console.log("Subscribed to EVENT_GPS_DATA");
// console.log("Subscribed to EVENT_GPS_DATA");
eventBus.on(EVENT_ALARM_DATA, queryAlarmData);
console.log("Subscribed to EVENT_ALARM_DATA");
// console.log("Subscribed to EVENT_ALARM_DATA");
eventBus.on(EVENT_ENTITY_DATA, queryEntityData);
console.log("Subscribed to EVENT_ENTITY_DATA");
// console.log("Subscribed to EVENT_ENTITY_DATA");
eventBus.on(EVENT_TRACK_POINTS_DATA, queryTrackPointsData);
console.log("Subscribed to EVENT_TRACK_POINTS_DATA");
// console.log("Subscribed to EVENT_TRACK_POINTS_DATA");
eventBus.once(EVENT_BANZONE_DATA, queryBanzonesData);
console.log("Subscribed once to EVENT_BANZONE_DATA");
// console.log("Subscribed once to EVENT_BANZONE_DATA");
return () => {
console.log("Unregistering event handlers in HomeScreen");
// console.log("Unregistering event handlers in HomeScreen");
eventBus.off(EVENT_GPS_DATA, queryGpsData);
console.log("Unsubscribed EVENT_GPS_DATA");
// console.log("Unsubscribed EVENT_GPS_DATA");
eventBus.off(EVENT_ALARM_DATA, queryAlarmData);
console.log("Unsubscribed EVENT_ALARM_DATA");
// console.log("Unsubscribed EVENT_ALARM_DATA");
eventBus.off(EVENT_ENTITY_DATA, queryEntityData);
console.log("Unsubscribed EVENT_ENTITY_DATA");
// console.log("Unsubscribed EVENT_ENTITY_DATA");
eventBus.off(EVENT_TRACK_POINTS_DATA, queryTrackPointsData);
console.log("Unsubscribed EVENT_TRACK_POINTS_DATA");
// console.log("Unsubscribed EVENT_TRACK_POINTS_DATA");
};
}, []);
@@ -170,14 +181,31 @@ export default function HomeScreen() {
geom_lines || ""
);
if (coordinates.length > 0) {
setPolylineCoordinates(coordinates);
setPolylineCoordinates({
coordinates: coordinates.map((coord) => ({
latitude: coord[0],
longitude: coord[1],
})),
label: zone?.zone_name ?? "",
content: zone?.message ?? "",
});
}
} else if (geom_type === 1) {
// foundPolygon = true;
const coordinates = convertWKTtoLatLngString(geom_poly || "");
if (coordinates.length > 0) {
console.log("Polygon Coordinate: ", coordinates);
setPolygonCoordinates(coordinates);
setPolygonCoordinates(
coordinates.map((polygon) => ({
coordinates: polygon.map((coord) => ({
latitude: coord[0],
longitude: coord[1],
})),
label: zone?.zone_name ?? "",
content: zone?.message ?? "",
}))
);
}
}
}
@@ -271,7 +299,9 @@ export default function HomeScreen() {
}, [alarmData?.level, scale, opacity]);
return (
<SafeAreaView edges={["top"]} style={styles.container}>
<View
// edges={["top"]}
style={styles.container}>
<MapView
onMapReady={handleMapReady}
onRegionChangeComplete={handleRegionChangeComplete}
@@ -297,7 +327,8 @@ export default function HomeScreen() {
longitude: point.lon,
}}
zIndex={50}
radius={platform === IOS_PLATFORM ? 200 : 50}
// radius={platform === IOS_PLATFORM ? 200 : 50}
radius={circleRadius}
strokeColor="rgba(16, 85, 201, 0.7)"
fillColor="rgba(16, 85, 201, 0.7)"
strokeWidth={2}
@@ -306,11 +337,9 @@ export default function HomeScreen() {
})}
{polylineCoordinates !== undefined && (
<PolylineWithLabel
coordinates={polylineCoordinates.map((coord) => ({
latitude: coord[0],
longitude: coord[1],
}))}
label="Tuyến bờ"
coordinates={polylineCoordinates.coordinates}
label={polylineCoordinates.label}
content={polylineCoordinates.content}
strokeColor="#FF5733"
strokeWidth={4}
showDistance={false}
@@ -322,30 +351,16 @@ export default function HomeScreen() {
{polygonCoordinates.map((polygon, index) => {
// Tạo key ổn định từ tọa độ đầu tiên của polygon
const polygonKey =
polygon.length > 0
? `polygon-${polygon[0][0]}-${polygon[0][1]}-${index}`
polygon.coordinates.length > 0
? `polygon-${polygon.coordinates[0].latitude}-${polygon.coordinates[0].longitude}-${index}`
: `polygon-${index}`;
return (
// <Polygon
// key={polygonKey}
// coordinates={polygon.map((coords) => ({
// latitude: coords[0],
// longitude: coords[1],
// }))}
// fillColor="rgba(16, 85, 201, 0.6)"
// strokeColor="rgba(16, 85, 201, 0.8)"
// strokeWidth={2}
// zIndex={50}
// />
<PolygonWithLabel
key={polygonKey}
coordinates={polygon.map((coords) => ({
latitude: coords[0],
longitude: coords[1],
}))}
label="Test khu đánh bắt"
content="Thời gian cấm (từ tháng 1 đến tháng 12)"
coordinates={polygon.coordinates}
label={polygon.label}
content={polygon.content}
fillColor="rgba(16, 85, 201, 0.6)"
strokeColor="rgba(16, 85, 201, 0.8)"
strokeWidth={2}
@@ -371,7 +386,7 @@ export default function HomeScreen() {
anchor={
platform === IOS_PLATFORM
? { x: 0.5, y: 0.5 }
: { x: 0.5, y: 0.4 }
: { x: 0.6, y: 0.4 }
}
>
<View className="w-8 h-8 items-center justify-center">
@@ -400,11 +415,10 @@ export default function HomeScreen() {
height: 32,
transform: [
{
rotate: `${
typeof gpsData.h === "number" && !isNaN(gpsData.h)
rotate: `${typeof gpsData.h === "number" && !isNaN(gpsData.h)
? gpsData.h
: 0
}deg`,
}deg`,
},
],
}}
@@ -414,16 +428,12 @@ export default function HomeScreen() {
</Marker>
)}
</MapView>
<TouchableOpacity
style={styles.button}
onPress={() => {
setPolygonCoordinates(undefined);
setPolylineCoordinates(undefined);
}}
>
<Text style={styles.buttonText}>Get GPS Data</Text>
</TouchableOpacity>
</SafeAreaView>
<View className="absolute top-14 right-2 shadow-md">
<SosButton />
</View>
<GPSInfoPanel gpsData={gpsData} />
</View>
);
}

View File

@@ -9,9 +9,12 @@ import { useEffect } from "react";
import "react-native-reanimated";
import Toast from "react-native-toast-message";
import { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider/gluestack-ui-provider";
import { setRouterInstance } from "@/config/auth";
import "@/global.css";
import { useColorScheme } from "@/hooks/use-color-scheme";
import "../global.css";
export default function RootLayout() {
const colorScheme = useColorScheme();
const router = useRouter();
@@ -21,34 +24,36 @@ export default function RootLayout() {
}, [router]);
return (
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<Stack
screenOptions={{ headerShown: false }}
initialRouteName="auth/login"
>
<Stack.Screen
name="auth/login"
options={{
title: "Login",
headerShown: false,
}}
/>
<GluestackUIProvider>
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<Stack
screenOptions={{ headerShown: false }}
initialRouteName="auth/login"
>
<Stack.Screen
name="auth/login"
options={{
title: "Login",
headerShown: false,
}}
/>
<Stack.Screen
name="(tabs)"
options={{
title: "Home",
headerShown: false,
}}
/>
<Stack.Screen
name="(tabs)"
options={{
title: "Home",
headerShown: false,
}}
/>
<Stack.Screen
name="modal"
options={{ presentation: "formSheet", title: "Modal" }}
/>
</Stack>
<StatusBar style="auto" />
<Toast />
</ThemeProvider>
<Stack.Screen
name="modal"
options={{ presentation: "formSheet", title: "Modal" }}
/>
</Stack>
<StatusBar style="auto" />
<Toast />
</ThemeProvider>
</GluestackUIProvider>
);
}