hiển thị thuyền thông tin tàu
This commit is contained in:
@@ -1,18 +1,28 @@
|
||||
import DraggablePanel from "@/components/DraggablePanel";
|
||||
import type { PolygonWithLabelProps } from "@/components/map/PolygonWithLabel";
|
||||
import type { PolylineWithLabelProps } from "@/components/map/PolylineWithLabel";
|
||||
import { IOS_PLATFORM, LIGHT_THEME } from "@/constants";
|
||||
import ShipInfo from "@/components/map/ShipInfo";
|
||||
import { TagState, TagStateCallbackPayload } from "@/components/map/TagState";
|
||||
import { EVENT_SEARCH_THINGS, IOS_PLATFORM, LIGHT_THEME } from "@/constants";
|
||||
import { usePlatform } from "@/hooks/use-platform";
|
||||
import { useThemeContext } from "@/hooks/use-theme-context";
|
||||
import { useRef, useState } from "react";
|
||||
import { Animated, StyleSheet, View } from "react-native";
|
||||
import MapView from "react-native-maps";
|
||||
import { searchThingEventBus } from "@/services/device_events";
|
||||
import { getShipIcon } from "@/services/map_service";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Animated,
|
||||
Image,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import MapView, { Marker } from "react-native-maps";
|
||||
|
||||
export default function HomeScreen() {
|
||||
const [gpsData, setGpsData] = useState<Model.GPSResponse | null>(null);
|
||||
const [alarmData, setAlarmData] = useState<Model.AlarmResponse | null>(null);
|
||||
const [entityData, setEntityData] = useState<
|
||||
Model.TransformedEntity[] | null
|
||||
>(null);
|
||||
const [banzoneData, setBanzoneData] = useState<Model.Zone[] | null>(null);
|
||||
const [trackPointsData, setTrackPointsData] = useState<
|
||||
Model.ShipTrackPoint[] | null
|
||||
@@ -26,74 +36,64 @@ export default function HomeScreen() {
|
||||
const [polygonCoordinates, setPolygonCoordinates] = useState<
|
||||
PolygonWithLabelProps[]
|
||||
>([]);
|
||||
const [things, setThings] = useState<Model.ThingsResponse | null>(null);
|
||||
const platform = usePlatform();
|
||||
const theme = useThemeContext().colorScheme;
|
||||
const scale = useRef(new Animated.Value(0)).current;
|
||||
const opacity = useRef(new Animated.Value(1)).current;
|
||||
|
||||
// useEffect(() => {
|
||||
// getGpsEventBus();
|
||||
// getAlarmEventBus();
|
||||
// getEntitiesEventBus();
|
||||
// getBanzonesEventBus();
|
||||
// getTrackPointsEventBus();
|
||||
// const queryGpsData = (gpsData: Model.GPSResponse) => {
|
||||
// if (gpsData) {
|
||||
// // console.log("GPS Data: ", gpsData);
|
||||
// setGpsData(gpsData);
|
||||
// } else {
|
||||
// setGpsData(null);
|
||||
// setPolygonCoordinates([]);
|
||||
// setPolylineCoordinates([]);
|
||||
// }
|
||||
// };
|
||||
// const queryAlarmData = (alarmData: Model.AlarmResponse) => {
|
||||
// // console.log("Alarm Data: ", alarmData.alarms.length);
|
||||
// setAlarmData(alarmData);
|
||||
// };
|
||||
// const queryEntityData = (entityData: Model.TransformedEntity[]) => {
|
||||
// // console.log("Entities Length Data: ", entityData.length);
|
||||
// setEntityData(entityData);
|
||||
// };
|
||||
// const queryBanzonesData = (banzoneData: Model.Zone[]) => {
|
||||
// // console.log("Banzone Data: ", banzoneData.length);
|
||||
// Thêm state để quản lý tracksViewChanges
|
||||
const [tracksViewChanges, setTracksViewChanges] = useState(true);
|
||||
|
||||
// setBanzoneData(banzoneData);
|
||||
// };
|
||||
// const queryTrackPointsData = (TrackPointsData: Model.ShipTrackPoint[]) => {
|
||||
// // console.log("TrackPoints Data: ", TrackPointsData.length);
|
||||
// if (TrackPointsData && TrackPointsData.length > 0) {
|
||||
// setTrackPointsData(TrackPointsData);
|
||||
// } else {
|
||||
// setTrackPointsData([]);
|
||||
// }
|
||||
// };
|
||||
const bodySearchThings: Model.SearchThingBody = {
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
order: "name",
|
||||
sort: "asc",
|
||||
metadata: {
|
||||
not_empty: "ship_id",
|
||||
},
|
||||
};
|
||||
|
||||
// eventBus.on(EVENT_GPS_DATA, queryGpsData);
|
||||
// // console.log("Registering event handlers in HomeScreen");
|
||||
// eventBus.on(EVENT_GPS_DATA, queryGpsData);
|
||||
// // console.log("Subscribed to EVENT_GPS_DATA");
|
||||
// eventBus.on(EVENT_ALARM_DATA, queryAlarmData);
|
||||
// // console.log("Subscribed to EVENT_ALARM_DATA");
|
||||
// eventBus.on(EVENT_ENTITY_DATA, queryEntityData);
|
||||
// // console.log("Subscribed to EVENT_ENTITY_DATA");
|
||||
// eventBus.on(EVENT_TRACK_POINTS_DATA, queryTrackPointsData);
|
||||
// // console.log("Subscribed to EVENT_TRACK_POINTS_DATA");
|
||||
// eventBus.once(EVENT_BANZONE_DATA, queryBanzonesData);
|
||||
// // console.log("Subscribed once to EVENT_BANZONE_DATA");
|
||||
useEffect(() => {
|
||||
searchThingEventBus(bodySearchThings);
|
||||
const querySearchThingsData = (thingsData: Model.ThingsResponse) => {
|
||||
const sortedThings: Model.Thing[] = (thingsData.things ?? []).sort(
|
||||
(a, b) => {
|
||||
const stateLevelA = a.metadata?.state_level || 0;
|
||||
const stateLevelB = b.metadata?.state_level || 0;
|
||||
return stateLevelB - stateLevelA; // Giảm dần
|
||||
}
|
||||
);
|
||||
const sortedThingsResponse: Model.ThingsResponse = {
|
||||
...thingsData,
|
||||
things: sortedThings,
|
||||
};
|
||||
console.log("Things Updated: ", sortedThingsResponse.things?.length);
|
||||
|
||||
// return () => {
|
||||
// // console.log("Unregistering event handlers in HomeScreen");
|
||||
// eventBus.off(EVENT_GPS_DATA, queryGpsData);
|
||||
// // console.log("Unsubscribed EVENT_GPS_DATA");
|
||||
// eventBus.off(EVENT_ALARM_DATA, queryAlarmData);
|
||||
// // console.log("Unsubscribed EVENT_ALARM_DATA");
|
||||
// eventBus.off(EVENT_ENTITY_DATA, queryEntityData);
|
||||
// // console.log("Unsubscribed EVENT_ENTITY_DATA");
|
||||
// eventBus.off(EVENT_TRACK_POINTS_DATA, queryTrackPointsData);
|
||||
// // console.log("Unsubscribed EVENT_TRACK_POINTS_DATA");
|
||||
// };
|
||||
// }, []);
|
||||
setThings(sortedThingsResponse);
|
||||
};
|
||||
eventBus.on(EVENT_SEARCH_THINGS, querySearchThingsData);
|
||||
|
||||
return () => {
|
||||
eventBus.off(EVENT_SEARCH_THINGS, querySearchThingsData);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (things) {
|
||||
// console.log("Things Updated: ", things.things?.length);
|
||||
// const gpsDatas: Model.GPSResponse[] = [];
|
||||
// for (const thing of things.things || []) {
|
||||
// if (thing.metadata?.gps) {
|
||||
// const gps: Model.GPSResponse = JSON.parse(thing.metadata.gps);
|
||||
// gpsDatas.push(gps);
|
||||
// }
|
||||
// }
|
||||
// console.log("GPS Lenght: ", gpsDatas.length);
|
||||
// setGpsData(gpsDatas);
|
||||
}
|
||||
}, [things]);
|
||||
|
||||
// useEffect(() => {
|
||||
// setPolylineCoordinates([]);
|
||||
@@ -203,7 +203,7 @@ export default function HomeScreen() {
|
||||
// Sau lần đầu, return undefined để không force region
|
||||
return undefined;
|
||||
}
|
||||
if (!gpsData) {
|
||||
if (things?.things?.length === 0) {
|
||||
return {
|
||||
latitude: 15.70581,
|
||||
longitude: 116.152685,
|
||||
@@ -213,8 +213,12 @@ export default function HomeScreen() {
|
||||
}
|
||||
|
||||
return {
|
||||
latitude: gpsData.lat,
|
||||
longitude: gpsData.lon,
|
||||
latitude: things?.things?.[0]?.metadata?.gps
|
||||
? JSON.parse(things.things[0].metadata.gps).lat
|
||||
: 15.70581,
|
||||
longitude: things?.things?.[0]?.metadata?.gps
|
||||
? JSON.parse(things.things[0].metadata.gps).lon
|
||||
: 116.152685,
|
||||
latitudeDelta: 0.05,
|
||||
longitudeDelta: 0.05,
|
||||
};
|
||||
@@ -226,184 +230,263 @@ export default function HomeScreen() {
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// if (alarmData?.level === 3) {
|
||||
// const loop = Animated.loop(
|
||||
// Animated.sequence([
|
||||
// Animated.parallel([
|
||||
// Animated.timing(scale, {
|
||||
// toValue: 3, // nở to 3 lần
|
||||
// duration: 1500,
|
||||
// useNativeDriver: true,
|
||||
// }),
|
||||
// Animated.timing(opacity, {
|
||||
// toValue: 0, // mờ dần
|
||||
// duration: 1500,
|
||||
// useNativeDriver: true,
|
||||
// }),
|
||||
// ]),
|
||||
// Animated.parallel([
|
||||
// Animated.timing(scale, {
|
||||
// toValue: 0,
|
||||
// duration: 0,
|
||||
// useNativeDriver: true,
|
||||
// }),
|
||||
// Animated.timing(opacity, {
|
||||
// toValue: 1,
|
||||
// duration: 0,
|
||||
// useNativeDriver: true,
|
||||
// }),
|
||||
// ]),
|
||||
// ])
|
||||
// );
|
||||
// loop.start();
|
||||
// return () => loop.stop();
|
||||
// }
|
||||
// }, [alarmData?.level, scale, opacity]);
|
||||
useEffect(() => {
|
||||
if (alarmData?.level === 3) {
|
||||
const loop = Animated.loop(
|
||||
Animated.sequence([
|
||||
Animated.parallel([
|
||||
Animated.timing(scale, {
|
||||
toValue: 3, // nở to 3 lần
|
||||
duration: 1500,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(opacity, {
|
||||
toValue: 0, // mờ dần
|
||||
duration: 1500,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
]),
|
||||
Animated.parallel([
|
||||
Animated.timing(scale, {
|
||||
toValue: 0,
|
||||
duration: 0,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(opacity, {
|
||||
toValue: 1,
|
||||
duration: 0,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
]),
|
||||
])
|
||||
);
|
||||
loop.start();
|
||||
return () => loop.stop();
|
||||
}
|
||||
}, [things, scale, opacity]);
|
||||
|
||||
// Tắt tracksViewChanges sau khi map đã load xong
|
||||
useEffect(() => {
|
||||
if (!isFirstLoad) {
|
||||
// Delay một chút để đảm bảo markers đã render xong
|
||||
const timer = setTimeout(() => {
|
||||
setTracksViewChanges(false);
|
||||
}, 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [isFirstLoad]);
|
||||
|
||||
const handleOnPressState = (state: TagStateCallbackPayload) => {
|
||||
// Xây dựng query state dựa trên logic bạn cung cấp
|
||||
const stateNormalQuery = state.isNormal ? "normal" : "";
|
||||
const stateSosQuery = state.isSos ? "sos" : "";
|
||||
const stateWarningQuery = state.isWarning
|
||||
? stateNormalQuery + ",warning"
|
||||
: stateNormalQuery;
|
||||
const stateCriticalQuery = state.isDangerous
|
||||
? stateWarningQuery + ",critical"
|
||||
: stateWarningQuery;
|
||||
|
||||
// Nếu bật tất cả filter thì không cần truyền stateQuery
|
||||
const stateQuery =
|
||||
state.isNormal && state.isWarning && state.isDangerous && state.isSos
|
||||
? ""
|
||||
: [stateCriticalQuery, stateSosQuery].filter(Boolean).join(",");
|
||||
let metaFormQuery = {};
|
||||
if (state.isDisconected)
|
||||
metaFormQuery = { ...metaFormQuery, connected: false };
|
||||
|
||||
// Tạo metadata query
|
||||
const metaStateQuery =
|
||||
stateQuery !== "" ? { state_level: stateQuery } : null;
|
||||
|
||||
// Tạo body search với filter
|
||||
const searchParams: Model.SearchThingBody = {
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
order: "name",
|
||||
sort: "asc",
|
||||
metadata: {
|
||||
...metaFormQuery,
|
||||
...metaStateQuery,
|
||||
not_empty: "ship_id",
|
||||
},
|
||||
};
|
||||
|
||||
// Gọi API tìm kiếm
|
||||
searchThingEventBus(searchParams);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
// edges={["top"]}
|
||||
style={styles.container}
|
||||
>
|
||||
<MapView
|
||||
onMapReady={handleMapReady}
|
||||
onRegionChangeComplete={handleRegionChangeComplete}
|
||||
style={styles.map}
|
||||
// initialRegion={getMapRegion()}
|
||||
region={getMapRegion()}
|
||||
userInterfaceStyle={theme === LIGHT_THEME ? "light" : "dark"}
|
||||
showsBuildings={false}
|
||||
showsIndoors={false}
|
||||
loadingEnabled={true}
|
||||
mapType={platform === IOS_PLATFORM ? "mutedStandard" : "standard"}
|
||||
rotateEnabled={false}
|
||||
>
|
||||
{/* {trackPointsData &&
|
||||
trackPointsData.length > 0 &&
|
||||
trackPointsData.map((point, index) => {
|
||||
// console.log(`Rendering circle ${index}:`, point);
|
||||
return (
|
||||
<Circle
|
||||
key={`circle-${index}`}
|
||||
center={{
|
||||
latitude: point.lat,
|
||||
longitude: point.lon,
|
||||
}}
|
||||
// zIndex={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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{polylineCoordinates.length > 0 && (
|
||||
<>
|
||||
{polylineCoordinates.map((polyline, index) => (
|
||||
<PolylineWithLabel
|
||||
key={`polyline-${index}-${gpsData?.lat || 0}-${
|
||||
gpsData?.lon || 0
|
||||
}`}
|
||||
coordinates={polyline.coordinates}
|
||||
label={polyline.label}
|
||||
content={polyline.content}
|
||||
strokeColor="#FF5733"
|
||||
strokeWidth={4}
|
||||
showDistance={false}
|
||||
// zIndex={50}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{polygonCoordinates.length > 0 && (
|
||||
<>
|
||||
{polygonCoordinates.map((polygon, index) => {
|
||||
return (
|
||||
<PolygonWithLabel
|
||||
key={`polygon-${index}-${gpsData?.lat || 0}-${
|
||||
gpsData?.lon || 0
|
||||
}`}
|
||||
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}
|
||||
// zIndex={50}
|
||||
zoomLevel={zoomLevel}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)} */}
|
||||
{/* {gpsData !== null && (
|
||||
<Marker
|
||||
key={
|
||||
platform === IOS_PLATFORM
|
||||
? `${gpsData.lat}-${gpsData.lon}`
|
||||
: "gps-data"
|
||||
}
|
||||
coordinate={{
|
||||
latitude: gpsData.lat,
|
||||
longitude: gpsData.lon,
|
||||
}}
|
||||
zIndex={20}
|
||||
anchor={
|
||||
platform === IOS_PLATFORM
|
||||
? { x: 0.5, y: 0.5 }
|
||||
: { x: 0.6, y: 0.4 }
|
||||
}
|
||||
tracksViewChanges={platform === IOS_PLATFORM ? true : undefined}
|
||||
>
|
||||
<View className="w-8 h-8 items-center justify-center">
|
||||
<View style={styles.pingContainer}>
|
||||
{alarmData?.level === 3 && (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.pingCircle,
|
||||
{
|
||||
transform: [{ scale }],
|
||||
opacity,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<RNImage
|
||||
source={(() => {
|
||||
const icon = getShipIcon(
|
||||
alarmData?.level || 0,
|
||||
gpsData.fishing
|
||||
);
|
||||
// console.log("Ship icon:", icon, "for level:", alarmData?.level, "fishing:", gpsData.fishing);
|
||||
return typeof icon === "string" ? { uri: icon } : icon;
|
||||
})()}
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
transform: [
|
||||
{
|
||||
rotate: `${
|
||||
typeof gpsData.h === "number" && !isNaN(gpsData.h)
|
||||
? gpsData.h
|
||||
: 0
|
||||
}deg`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Marker>
|
||||
)} */}
|
||||
</MapView>
|
||||
<GestureHandlerRootView style={styles.container}>
|
||||
<View style={styles.container}>
|
||||
<MapView
|
||||
onMapReady={handleMapReady}
|
||||
onRegionChangeComplete={handleRegionChangeComplete}
|
||||
style={styles.map}
|
||||
region={getMapRegion()}
|
||||
userInterfaceStyle={theme === LIGHT_THEME ? "light" : "dark"}
|
||||
showsBuildings={false}
|
||||
showsIndoors={false}
|
||||
loadingEnabled={true}
|
||||
mapType={platform === IOS_PLATFORM ? "mutedStandard" : "standard"}
|
||||
rotateEnabled={false}
|
||||
>
|
||||
{things?.things && things.things.length > 0 && (
|
||||
<>
|
||||
{things.things
|
||||
.filter((thing) => thing.metadata?.gps) // Filter trước để tránh null check
|
||||
.map((thing, index) => {
|
||||
const gpsData: Model.GPSResponse = JSON.parse(
|
||||
thing.metadata!.gps!
|
||||
);
|
||||
|
||||
{/* <View className="absolute top-14 right-2 shadow-md">
|
||||
<SosButton />
|
||||
// Tạo unique key dựa trên thing.id hoặc tọa độ
|
||||
const uniqueKey = thing.id
|
||||
? `marker-${thing.id}-${index}`
|
||||
: `marker-${gpsData.lat.toFixed(6)}-${gpsData.lon.toFixed(
|
||||
6
|
||||
)}-${index}`;
|
||||
|
||||
return (
|
||||
<Marker
|
||||
key={uniqueKey}
|
||||
coordinate={{
|
||||
latitude: gpsData.lat,
|
||||
longitude: gpsData.lon,
|
||||
}}
|
||||
zIndex={50}
|
||||
anchor={{ x: 0.5, y: 0.5 }}
|
||||
// Chỉ tracks changes khi cần thiết
|
||||
tracksViewChanges={
|
||||
platform === IOS_PLATFORM ? tracksViewChanges : true
|
||||
}
|
||||
// Thêm identifier để iOS optimize
|
||||
identifier={uniqueKey}
|
||||
>
|
||||
<View className="w-8 h-8 items-center justify-center">
|
||||
<View style={styles.pingContainer}>
|
||||
{thing.metadata?.state_level === 3 && (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.pingCircle,
|
||||
{
|
||||
transform: [{ scale }],
|
||||
opacity,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<Image
|
||||
source={(() => {
|
||||
const icon = getShipIcon(
|
||||
thing.metadata?.state_level || 0,
|
||||
gpsData.fishing
|
||||
);
|
||||
// console.log("Ship icon:", icon, "for level:", alarmData?.level, "fishing:", gpsData.fishing);
|
||||
return typeof icon === "string"
|
||||
? { uri: icon }
|
||||
: icon;
|
||||
})()}
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
transform: [
|
||||
{
|
||||
rotate: `${
|
||||
typeof gpsData.h === "number" &&
|
||||
!isNaN(gpsData.h)
|
||||
? gpsData.h
|
||||
: 0
|
||||
}deg`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Marker>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</MapView>
|
||||
|
||||
{/* <View className="absolute top-14 right-2 shadow-md">
|
||||
<SosButton />
|
||||
</View>
|
||||
<GPSInfoPanel gpsData={gpsData!} /> */}
|
||||
|
||||
{/* Draggable Panel */}
|
||||
<DraggablePanel
|
||||
minHeightPct={0.1}
|
||||
maxHeightPct={0.5}
|
||||
initialState="min"
|
||||
onExpandedChange={(expanded) => {
|
||||
console.log("Panel expanded:", expanded);
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<View className="flex flex-row gap-4 items-center justify-center">
|
||||
<TagState
|
||||
normalCount={things?.metadata?.total_state_level_0 || 0}
|
||||
warningCount={things?.metadata?.total_state_level_1 || 0}
|
||||
dangerousCount={things?.metadata?.total_state_level_2 || 0}
|
||||
sosCount={things?.metadata?.total_sos || 0}
|
||||
disconnectedCount={
|
||||
(things?.metadata?.total_thing ?? 0) -
|
||||
(things?.metadata?.total_connected ?? 0) || 0
|
||||
}
|
||||
onTagPress={handleOnPressState}
|
||||
/>
|
||||
</View>
|
||||
<View style={{ width: "100%", paddingVertical: 8, flex: 1 }}>
|
||||
<Text
|
||||
style={{ fontSize: 20, textAlign: "center", fontWeight: "600" }}
|
||||
>
|
||||
Danh sách tàu thuyền
|
||||
</Text>
|
||||
|
||||
<ScrollView
|
||||
style={{ width: "100%", marginTop: 8, flex: 1 }}
|
||||
contentContainerStyle={{
|
||||
alignItems: "center",
|
||||
paddingBottom: 24,
|
||||
// backgroundColor: "green",
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{things && things.things && things.things.length > 0 && (
|
||||
<>
|
||||
{things.things.map((thing, index) => {
|
||||
return (
|
||||
<View
|
||||
key={thing.id ?? index}
|
||||
style={{ width: "100%", alignItems: "center" }}
|
||||
>
|
||||
<ShipInfo thingMetadata={thing.metadata} />
|
||||
{index < (things.things?.length ?? 0) - 1 && (
|
||||
<View
|
||||
style={{
|
||||
width: "50%",
|
||||
height: 1,
|
||||
backgroundColor: "#E5E7EB",
|
||||
marginVertical: 8,
|
||||
borderRadius: 1,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</>
|
||||
</DraggablePanel>
|
||||
</View>
|
||||
<GPSInfoPanel gpsData={gpsData!} /> */}
|
||||
</View>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import ShipSearchForm from "@/components/ShipSearchForm";
|
||||
import { useState } from "react";
|
||||
import { Platform, ScrollView, StyleSheet, Text, View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function warning() {
|
||||
const [shipSearchFormOpen, setShipSearchFormOpen] = useState(true);
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1 }}>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.titleText}>Cảnh báo</Text>
|
||||
</View>
|
||||
<ShipSearchForm
|
||||
isOpen={shipSearchFormOpen}
|
||||
onClose={() => setShipSearchFormOpen(false)}
|
||||
/>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,10 @@ import { toastConfig } from "@/config";
|
||||
import { setRouterInstance } from "@/config/auth";
|
||||
import "@/global.css";
|
||||
import { I18nProvider } from "@/hooks/use-i18n";
|
||||
import { ThemeProvider as AppThemeProvider, useThemeContext } from "@/hooks/use-theme-context";
|
||||
import {
|
||||
ThemeProvider as AppThemeProvider,
|
||||
useThemeContext,
|
||||
} from "@/hooks/use-theme-context";
|
||||
import Toast from "react-native-toast-message";
|
||||
import "../global.css";
|
||||
function AppContent() {
|
||||
|
||||
Reference in New Issue
Block a user