Setup socket.io
parent
189f8bd673
commit
a1accb4ca5
|
|
@ -12,6 +12,9 @@
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true">
|
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.geo.API_KEY"
|
||||||
|
android:value="AIzaSyBfz20PwTeQkeMonsgXETOViBAy9LOBlXY"/>
|
||||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
|
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Tabs } from "expo-router";
|
import { Tabs } from "expo-router";
|
||||||
import { TAB_CONFIG } from "@/constants/config";
|
import { TAB_CONFIG } from "@/constants/config";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "@/store";
|
||||||
|
import { initSocket } from "@/services/socket";
|
||||||
|
|
||||||
export default function TabLayout() {
|
export default function TabLayout() {
|
||||||
|
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
|
||||||
|
if (!isLoggedIn) return null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initSocket();
|
||||||
|
}, [isLoggedIn]);
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,17 @@ import CustomerCareIcon from "../../assets/icons/customer-care.svg";
|
||||||
import ServiceReminderCard from "@/components/home/ServiceReminderCard";
|
import ServiceReminderCard from "@/components/home/ServiceReminderCard";
|
||||||
import MetricCard from "@/components/home/MetricCard";
|
import MetricCard from "@/components/home/MetricCard";
|
||||||
import PaymentDueCard from "@/components/home/PaymentDueCard";
|
import PaymentDueCard from "@/components/home/PaymentDueCard";
|
||||||
import LocationMap from "@/components/home/LocationMap";
|
import MapView, { Marker } from "react-native-maps";
|
||||||
import BatteryWarrantyCard from "@/components/home/BatteryWarrantyCars";
|
import BatteryWarrantyCard from "@/components/home/BatteryWarrantyCars";
|
||||||
import CustomerSupportModal from "@/components/home/CustomerSupportModal";
|
import CustomerSupportModal from "@/components/home/CustomerSupportModal";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "@/store";
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const [isSupportModalVisible, setIsSupportModalVisible] = useState(false);
|
const [isSupportModalVisible, setIsSupportModalVisible] = useState(false);
|
||||||
|
const { SoC, SoH } = useSelector((state: RootState) => state.telemetry);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
|
|
@ -51,10 +54,10 @@ export default function HomeScreen() {
|
||||||
subMessage="Service Reminder"
|
subMessage="Service Reminder"
|
||||||
/>
|
/>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<SemiCircleProgress progress={90} status={1} />
|
<SemiCircleProgress progress={SoC} status={1} />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.metrics}>
|
<View style={styles.metrics}>
|
||||||
<MetricCard heading="SoH" value="90%" />
|
<MetricCard heading="SoH" value={`${SoH}%`} />
|
||||||
<MetricCard heading="Total Distance" value="20009 km" />
|
<MetricCard heading="Total Distance" value="20009 km" />
|
||||||
</View>
|
</View>
|
||||||
<PaymentDueCard
|
<PaymentDueCard
|
||||||
|
|
@ -62,7 +65,31 @@ export default function HomeScreen() {
|
||||||
amount="$2,000"
|
amount="$2,000"
|
||||||
onPress={() => {}}
|
onPress={() => {}}
|
||||||
/>
|
/>
|
||||||
{/* <LocationMap latitude={12.9716} longitude={77.5946} /> */}
|
<View style={styles.mapContainer}>
|
||||||
|
<MapView
|
||||||
|
style={styles.mapStyle}
|
||||||
|
initialRegion={{
|
||||||
|
latitude: 28.54,
|
||||||
|
longitude: 77.32,
|
||||||
|
latitudeDelta: 0.0922,
|
||||||
|
longitudeDelta: 0.0421,
|
||||||
|
}}
|
||||||
|
// customMapStyle={mapStyle}
|
||||||
|
>
|
||||||
|
<Marker
|
||||||
|
draggable
|
||||||
|
coordinate={{
|
||||||
|
latitude: 37.78825,
|
||||||
|
longitude: -122.4324,
|
||||||
|
}}
|
||||||
|
anchor={{ x: 0.5, y: 0.5 }}
|
||||||
|
onDragEnd={(e) => alert(JSON.stringify(e.nativeEvent.coordinate))}
|
||||||
|
title={"Test Marker"}
|
||||||
|
description={"This is a description of the marker"}
|
||||||
|
/>
|
||||||
|
</MapView>
|
||||||
|
</View>
|
||||||
|
|
||||||
<BatteryWarrantyCard
|
<BatteryWarrantyCard
|
||||||
totalWarrantyYears={8}
|
totalWarrantyYears={8}
|
||||||
batteryPurchaseEpoch={Math.floor(
|
batteryPurchaseEpoch={Math.floor(
|
||||||
|
|
@ -78,7 +105,100 @@ export default function HomeScreen() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStyle = [
|
||||||
|
{ elementType: "geometry", stylers: [{ color: "#242f3e" }] },
|
||||||
|
{ elementType: "labels.text.fill", stylers: [{ color: "#746855" }] },
|
||||||
|
{ elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] },
|
||||||
|
{
|
||||||
|
featureType: "administrative.locality",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#d59563" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "poi",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#d59563" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "poi.park",
|
||||||
|
elementType: "geometry",
|
||||||
|
stylers: [{ color: "#263c3f" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "poi.park",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#6b9a76" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "road",
|
||||||
|
elementType: "geometry",
|
||||||
|
stylers: [{ color: "#38414e" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "road",
|
||||||
|
elementType: "geometry.stroke",
|
||||||
|
stylers: [{ color: "#212a37" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "road",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#9ca5b3" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "road.highway",
|
||||||
|
elementType: "geometry",
|
||||||
|
stylers: [{ color: "#746855" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "road.highway",
|
||||||
|
elementType: "geometry.stroke",
|
||||||
|
stylers: [{ color: "#1f2835" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "road.highway",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#f3d19c" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "transit",
|
||||||
|
elementType: "geometry",
|
||||||
|
stylers: [{ color: "#2f3948" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "transit.station",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#d59563" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "water",
|
||||||
|
elementType: "geometry",
|
||||||
|
stylers: [{ color: "#17263c" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "water",
|
||||||
|
elementType: "labels.text.fill",
|
||||||
|
stylers: [{ color: "#515c6d" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
featureType: "water",
|
||||||
|
elementType: "labels.text.stroke",
|
||||||
|
stylers: [{ color: "#17263c" }],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
mapContainer: {
|
||||||
|
flex: 1,
|
||||||
|
borderRadius: 10,
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
|
mapStyle: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
},
|
||||||
metrics: {
|
metrics: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import i18next, { getLanguage } from "../services/i18n/index";
|
import i18next, { getLanguage } from "../services/i18n/index";
|
||||||
import FontAwesome from "@expo/vector-icons/FontAwesome";
|
|
||||||
import { useFonts } from "expo-font";
|
|
||||||
import { Stack, useRouter } from "expo-router";
|
import { Stack, useRouter } from "expo-router";
|
||||||
import * as SplashScreen from "expo-splash-screen";
|
import * as SplashScreen from "expo-splash-screen";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
@ -18,11 +16,6 @@ export const unstable_settings = {
|
||||||
SplashScreen.preventAutoHideAsync();
|
SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
const [loaded, error] = useFonts({
|
|
||||||
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
|
|
||||||
...FontAwesome.font,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [appIsReady, setAppIsReady] = useState(false);
|
const [appIsReady, setAppIsReady] = useState(false);
|
||||||
const [shouldRedirect, setShouldRedirect] = useState(false);
|
const [shouldRedirect, setShouldRedirect] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -31,7 +24,6 @@ export default function RootLayout() {
|
||||||
const initLang = async () => {
|
const initLang = async () => {
|
||||||
const lang = await getLanguage();
|
const lang = await getLanguage();
|
||||||
if (!lang) {
|
if (!lang) {
|
||||||
console.log("Redirecting to language init...");
|
|
||||||
setShouldRedirect(true);
|
setShouldRedirect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,15 +34,11 @@ export default function RootLayout() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (error) throw error;
|
if (appIsReady) {
|
||||||
}, [error]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (loaded && appIsReady) {
|
|
||||||
SplashScreen.hideAsync();
|
SplashScreen.hideAsync();
|
||||||
router.replace("/auth/login");
|
router.replace("/auth/login");
|
||||||
}
|
}
|
||||||
}, [loaded, appIsReady]);
|
}, [appIsReady]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (appIsReady && shouldRedirect) {
|
if (appIsReady && shouldRedirect) {
|
||||||
|
|
@ -59,8 +47,7 @@ export default function RootLayout() {
|
||||||
}
|
}
|
||||||
}, [appIsReady, shouldRedirect]);
|
}, [appIsReady, shouldRedirect]);
|
||||||
|
|
||||||
if (!loaded || !appIsReady) {
|
if (!appIsReady) {
|
||||||
console.log("!loaded || !appIsReady", loaded, appIsReady);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +60,6 @@ function RootLayoutNav() {
|
||||||
<I18nextProvider i18n={i18next}>
|
<I18nextProvider i18n={i18next}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { io, Socket } from "socket.io-client";
|
||||||
|
import { store } from "../store";
|
||||||
|
import { updateTelemetry } from "../store/telemetrySlice";
|
||||||
|
|
||||||
|
const SERVER_URL =
|
||||||
|
"http://dev.vec-tr.ai:8089/?dashboardId=deviceDashboardSocket&assetId=V16000868651064644504";
|
||||||
|
const TOKEN =
|
||||||
|
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiYWN0aW9uIjoiYXV0aCIsInRva2VuLXZlcnNpb24iOjAsImlhdCI6MTc1MTM0OTUwMSwiZXhwIjoxNzUxNDM1OTAxfQ.0BB0vRcFSMFDC6aILuc__pG3Ycy8EndYOAwLwCHji1M";
|
||||||
|
|
||||||
|
let socket: Socket | null = null;
|
||||||
|
|
||||||
|
export const initSocket = () => {
|
||||||
|
socket = io(SERVER_URL, {
|
||||||
|
transports: ["websocket"],
|
||||||
|
extraHeaders: {
|
||||||
|
Authorization: `Bearer ${TOKEN}`,
|
||||||
|
controllingServer: "http://dev.vec-tr.ai:8082",
|
||||||
|
},
|
||||||
|
reconnection: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("connect", () => {
|
||||||
|
console.log("Socket connected:", socket?.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("connect_error", (error) => {
|
||||||
|
console.error("Socket connection error:", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("disconnect", (reason) => {
|
||||||
|
console.log("Socket disconnecteddddd", reason, "abc");
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("dataUpdate", handleSocketData);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const disconnectSocket = () => {
|
||||||
|
if (socket) {
|
||||||
|
socket.disconnect();
|
||||||
|
console.log("Socket disconnected");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSocketData = (data: any) => {
|
||||||
|
try {
|
||||||
|
const SoH =
|
||||||
|
data.dataSeries.assetData[0].bms[0].bmsSpecific.ivecSpecific.soh;
|
||||||
|
const SoC = data?.dataSeries?.assetData?.[0]?.bms?.[0]?.batterySoc;
|
||||||
|
// console.log(SoC, SoH);
|
||||||
|
store.dispatch(updateTelemetry({ SoH, SoC }));
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error handling socket data:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { combineReducers } from "@reduxjs/toolkit";
|
import { combineReducers } from "@reduxjs/toolkit";
|
||||||
import authreducer from "./authSlice";
|
import authreducer from "./authSlice";
|
||||||
|
import telemetryReducer from "./telemetrySlice";
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
auth: authreducer,
|
auth: authreducer,
|
||||||
|
telemetry: telemetryReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
interface TelemetryState {
|
||||||
|
SoH: number;
|
||||||
|
SoC: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: TelemetryState = {
|
||||||
|
SoH: 0,
|
||||||
|
SoC: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const telemetrySlice = createSlice({
|
||||||
|
name: "telemetry",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
updateTelemetry: (state, action: PayloadAction<TelemetryState>) => {
|
||||||
|
return { ...state, ...action.payload };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { updateTelemetry } = telemetrySlice.actions;
|
||||||
|
export default telemetrySlice.reducer;
|
||||||
Loading…
Reference in New Issue