Setup socket.io

feature/app-setup
vinay kumar 2025-07-01 18:19:21 +05:30
parent 189f8bd673
commit a1accb4ca5
7 changed files with 220 additions and 22 deletions

View File

@ -12,6 +12,9 @@
</intent>
</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">
<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.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>

View File

@ -1,8 +1,17 @@
import React from "react";
import React, { useEffect } from "react";
import { Tabs } from "expo-router";
import { TAB_CONFIG } from "@/constants/config";
import { useSelector } from "react-redux";
import { RootState } from "@/store";
import { initSocket } from "@/services/socket";
export default function TabLayout() {
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
if (!isLoggedIn) return null;
useEffect(() => {
initSocket();
}, [isLoggedIn]);
return (
<Tabs
screenOptions={{

View File

@ -10,14 +10,17 @@ import CustomerCareIcon from "../../assets/icons/customer-care.svg";
import ServiceReminderCard from "@/components/home/ServiceReminderCard";
import MetricCard from "@/components/home/MetricCard";
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 CustomerSupportModal from "@/components/home/CustomerSupportModal";
import { useSelector } from "react-redux";
import { RootState } from "@/store";
export default function HomeScreen() {
const { t } = useTranslation();
const navigation = useNavigation();
const [isSupportModalVisible, setIsSupportModalVisible] = useState(false);
const { SoC, SoH } = useSelector((state: RootState) => state.telemetry);
useLayoutEffect(() => {
navigation.setOptions({
@ -51,10 +54,10 @@ export default function HomeScreen() {
subMessage="Service Reminder"
/>
<View style={styles.iconContainer}>
<SemiCircleProgress progress={90} status={1} />
<SemiCircleProgress progress={SoC} status={1} />
</View>
<View style={styles.metrics}>
<MetricCard heading="SoH" value="90%" />
<MetricCard heading="SoH" value={`${SoH}%`} />
<MetricCard heading="Total Distance" value="20009 km" />
</View>
<PaymentDueCard
@ -62,7 +65,31 @@ export default function HomeScreen() {
amount="$2,000"
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
totalWarrantyYears={8}
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({
mapContainer: {
flex: 1,
borderRadius: 10,
overflow: "hidden",
},
mapStyle: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
},
metrics: {
flexDirection: "row",
justifyContent: "space-between",

View File

@ -1,6 +1,4 @@
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 * as SplashScreen from "expo-splash-screen";
import { useEffect, useState } from "react";
@ -18,11 +16,6 @@ export const unstable_settings = {
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [loaded, error] = useFonts({
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
...FontAwesome.font,
});
const [appIsReady, setAppIsReady] = useState(false);
const [shouldRedirect, setShouldRedirect] = useState(false);
const router = useRouter();
@ -31,7 +24,6 @@ export default function RootLayout() {
const initLang = async () => {
const lang = await getLanguage();
if (!lang) {
console.log("Redirecting to language init...");
setShouldRedirect(true);
}
@ -42,15 +34,11 @@ export default function RootLayout() {
}, []);
useEffect(() => {
if (error) throw error;
}, [error]);
useEffect(() => {
if (loaded && appIsReady) {
if (appIsReady) {
SplashScreen.hideAsync();
router.replace("/auth/login");
}
}, [loaded, appIsReady]);
}, [appIsReady]);
useEffect(() => {
if (appIsReady && shouldRedirect) {
@ -59,8 +47,7 @@ export default function RootLayout() {
}
}, [appIsReady, shouldRedirect]);
if (!loaded || !appIsReady) {
console.log("!loaded || !appIsReady", loaded, appIsReady);
if (!appIsReady) {
return null;
}
@ -73,7 +60,6 @@ function RootLayoutNav() {
<I18nextProvider i18n={i18next}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
</Stack>
</I18nextProvider>
</Provider>

54
services/socket.ts Normal file
View File

@ -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);
}
};

View File

@ -1,8 +1,10 @@
import { combineReducers } from "@reduxjs/toolkit";
import authreducer from "./authSlice";
import telemetryReducer from "./telemetrySlice";
const rootReducer = combineReducers({
auth: authreducer,
telemetry: telemetryReducer,
});
export default rootReducer;

24
store/telemetrySlice.ts Normal file
View File

@ -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;