Add profile screen and fix map's motion
parent
6db5b1355b
commit
75d43a3cd9
2
app.json
2
app.json
|
|
@ -6,7 +6,7 @@
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/images/icon.png",
|
"icon": "./assets/images/icon.png",
|
||||||
"scheme": "batteryasaservice",
|
"scheme": "batteryasaservice",
|
||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "light",
|
||||||
"newArchEnabled": true,
|
"newArchEnabled": true,
|
||||||
"splash": {
|
"splash": {
|
||||||
"image": "./assets/images/splash-icon.png",
|
"image": "./assets/images/splash-icon.png",
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,24 @@ import {
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import SemiCircleProgress from "../../components/home/SemiCircleProgress";
|
import SemiCircleProgress from "../../components/home/SemiCircleProgress";
|
||||||
import { Text, View } from "@/components/Themed";
|
import { Text, View } from "react-native";
|
||||||
import { useNavigation } from "expo-router";
|
import { useNavigation } from "expo-router";
|
||||||
import { useLayoutEffect, useState } from "react";
|
import { useEffect, useLayoutEffect, useState } from "react";
|
||||||
import { StatusBar } from "expo-status-bar";
|
import { StatusBar } from "expo-status-bar";
|
||||||
import Profile from "../../components/home/Profile";
|
|
||||||
import CustomerCareIcon from "../../assets/icons/customer-care.svg";
|
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 MapView, { Marker } from "react-native-maps";
|
import MapView, { Marker, PROVIDER_GOOGLE } 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 { useSelector } from "react-redux";
|
||||||
import { RootState } from "@/store";
|
import { RootState } from "@/store";
|
||||||
import LocationOff from "@/assets/icons/location_off.svg";
|
import LocationOff from "@/assets/icons/location_off.svg";
|
||||||
import { Linking } from "react-native";
|
import { Linking } from "react-native";
|
||||||
|
import { useRouter } from "expo-router";
|
||||||
|
import ProfileImage from "@/components/home/Profile";
|
||||||
|
import { calculateBearing, calculateDistance } from "@/utils/Map";
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -30,6 +32,53 @@ export default function HomeScreen() {
|
||||||
const { SoC, SoH, chargingState, lat, lon, loading, error } = useSelector(
|
const { SoC, SoH, chargingState, lat, lon, loading, error } = useSelector(
|
||||||
(state: RootState) => state.telemetry
|
(state: RootState) => state.telemetry
|
||||||
);
|
);
|
||||||
|
const [prevPosition, setPrevPosition] = useState<{
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
} | null>(null);
|
||||||
|
const [bearing, setBearing] = useState<number>(0);
|
||||||
|
|
||||||
|
const step = 0.001;
|
||||||
|
useEffect(() => {
|
||||||
|
if (lat && lon) {
|
||||||
|
if (prevPosition) {
|
||||||
|
// Calculate bearing between prev and current position
|
||||||
|
const newBearing = calculateBearing(
|
||||||
|
prevPosition.lat,
|
||||||
|
prevPosition.lon,
|
||||||
|
lat,
|
||||||
|
lon
|
||||||
|
);
|
||||||
|
setBearing(newBearing);
|
||||||
|
}
|
||||||
|
setPrevPosition({ lat, lon }); // Update previous position
|
||||||
|
}
|
||||||
|
}, [lat, lon]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lat && lon) {
|
||||||
|
if (prevPosition) {
|
||||||
|
const distance = calculateDistance(
|
||||||
|
prevPosition.lat,
|
||||||
|
prevPosition.lon,
|
||||||
|
lat,
|
||||||
|
lon
|
||||||
|
);
|
||||||
|
if (distance > 5) {
|
||||||
|
// Only update bearing if moved >5 meters
|
||||||
|
const newBearing = calculateBearing(
|
||||||
|
prevPosition.lat,
|
||||||
|
prevPosition.lon,
|
||||||
|
lat,
|
||||||
|
lon
|
||||||
|
);
|
||||||
|
setBearing(newBearing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPrevPosition({ lat, lon });
|
||||||
|
}
|
||||||
|
}, [lat, lon]);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
|
|
@ -43,11 +92,25 @@ export default function HomeScreen() {
|
||||||
<View style={styles.rightContainer}>
|
<View style={styles.rightContainer}>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={styles.iconContainer}
|
style={styles.iconContainer}
|
||||||
onPress={() => setIsSupportModalVisible(true)}
|
onPress={() => {
|
||||||
|
console.log("Support Pressed");
|
||||||
|
setIsSupportModalVisible(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<CustomerCareIcon />
|
<CustomerCareIcon />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<Profile username="Vishal" textSize={16} boxSize={32} />
|
<Pressable
|
||||||
|
onPress={() => {
|
||||||
|
router.push("/user/profile");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProfileImage
|
||||||
|
username="Vivek"
|
||||||
|
onClick={() => router.push("/user/profile")}
|
||||||
|
textSize={20}
|
||||||
|
boxSize={40}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
@ -97,14 +160,14 @@ export default function HomeScreen() {
|
||||||
<>
|
<>
|
||||||
<View style={styles.mapContainer}>
|
<View style={styles.mapContainer}>
|
||||||
<MapView
|
<MapView
|
||||||
|
provider={PROVIDER_GOOGLE}
|
||||||
style={styles.mapStyle}
|
style={styles.mapStyle}
|
||||||
initialRegion={{
|
region={{
|
||||||
latitude: 28.54,
|
latitude: lat,
|
||||||
longitude: 77.32,
|
longitude: lon,
|
||||||
latitudeDelta: 0.0922,
|
latitudeDelta: 0.0922,
|
||||||
longitudeDelta: 0.0421,
|
longitudeDelta: 0.0421,
|
||||||
}}
|
}}
|
||||||
// customMapStyle={mapStyle}
|
|
||||||
>
|
>
|
||||||
<Marker
|
<Marker
|
||||||
draggable
|
draggable
|
||||||
|
|
@ -112,13 +175,10 @@ export default function HomeScreen() {
|
||||||
latitude: lat,
|
latitude: lat,
|
||||||
longitude: lon,
|
longitude: lon,
|
||||||
}}
|
}}
|
||||||
|
rotation={bearing}
|
||||||
anchor={{ x: 0.5, y: 0.5 }}
|
anchor={{ x: 0.5, y: 0.5 }}
|
||||||
onDragEnd={(e) =>
|
tracksViewChanges={false}
|
||||||
alert(JSON.stringify(e.nativeEvent.coordinate))
|
image={require("../../assets/images/marker.png")}
|
||||||
}
|
|
||||||
title={"Test Marker"}
|
|
||||||
description={"This is a description of the marker"}
|
|
||||||
image={require("../../assets/icons/marker.png")}
|
|
||||||
/>
|
/>
|
||||||
</MapView>
|
</MapView>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,11 @@
|
||||||
import { StyleSheet } from "react-native";
|
import { StyleSheet } from "react-native";
|
||||||
|
|
||||||
import EditScreenInfo from "@/components/EditScreenInfo";
|
import { Text, View } from "react-native";
|
||||||
import { Text, View } from "@/components/Themed";
|
|
||||||
|
|
||||||
export default function MyBatteryTabScreen() {
|
export default function MyBatteryTabScreen() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Coming Soon</Text>
|
<Text style={styles.title}>Coming Soon</Text>
|
||||||
<View
|
|
||||||
style={styles.separator}
|
|
||||||
lightColor="#eee"
|
|
||||||
darkColor="rgba(255,255,255,0.1)"
|
|
||||||
/>
|
|
||||||
<EditScreenInfo path="app/(tabs)/two.tsx" />
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
import { StyleSheet } from "react-native";
|
import { StyleSheet } from "react-native";
|
||||||
|
import { Text, View } from "react-native";
|
||||||
import EditScreenInfo from "@/components/EditScreenInfo";
|
|
||||||
import { Text, View } from "@/components/Themed";
|
|
||||||
|
|
||||||
export default function PaymentsTabScreen() {
|
export default function PaymentsTabScreen() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Tab Two</Text>
|
<Text style={styles.title}>Tab Two</Text>
|
||||||
<View
|
|
||||||
style={styles.separator}
|
|
||||||
lightColor="#eee"
|
|
||||||
darkColor="rgba(255,255,255,0.1)"
|
|
||||||
/>
|
|
||||||
<EditScreenInfo path="app/(tabs)/two.tsx" />
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
import { StyleSheet } from "react-native";
|
import { StyleSheet } from "react-native";
|
||||||
|
import { Text, View } from "react-native";
|
||||||
import EditScreenInfo from "@/components/EditScreenInfo";
|
|
||||||
import { Text, View } from "@/components/Themed";
|
|
||||||
|
|
||||||
export default function ServiceTabScreen() {
|
export default function ServiceTabScreen() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Coming Soon</Text>
|
<Text style={styles.title}>Coming Soon</Text>
|
||||||
<View
|
|
||||||
style={styles.separator}
|
|
||||||
lightColor="#eee"
|
|
||||||
darkColor="rgba(255,255,255,0.1)"
|
|
||||||
/>
|
|
||||||
<EditScreenInfo path="app/(tabs)/two.tsx" />
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { Link, Stack } from 'expo-router';
|
import { Link, Stack } from "expo-router";
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from "react-native";
|
||||||
|
|
||||||
import { Text, View } from '@/components/Themed';
|
import { Text, View } from "react-native";
|
||||||
|
|
||||||
export default function NotFoundScreen() {
|
export default function NotFoundScreen() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
<Stack.Screen options={{ title: "Oops!" }} />
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>This screen doesn't exist.</Text>
|
<Text style={styles.title}>This screen doesn't exist.</Text>
|
||||||
|
|
||||||
|
|
@ -21,13 +21,13 @@ export default function NotFoundScreen() {
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
padding: 20,
|
padding: 20,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: 'bold',
|
fontWeight: "bold",
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
marginTop: 15,
|
marginTop: 15,
|
||||||
|
|
@ -35,6 +35,6 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
linkText: {
|
linkText: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: '#2e78b7',
|
color: "#2e78b7",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
import { StatusBar } from 'expo-status-bar';
|
|
||||||
import { Platform, StyleSheet } from 'react-native';
|
|
||||||
|
|
||||||
import EditScreenInfo from '@/components/EditScreenInfo';
|
|
||||||
import { Text, View } from '@/components/Themed';
|
|
||||||
|
|
||||||
export default function ModalScreen() {
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<Text style={styles.title}>Modal</Text>
|
|
||||||
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
|
|
||||||
<EditScreenInfo path="app/modal.tsx" />
|
|
||||||
|
|
||||||
{/* Use a light status bar on iOS to account for the black space above the modal */}
|
|
||||||
<StatusBar style={Platform.OS === 'ios' ? 'light' : 'auto'} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
separator: {
|
|
||||||
marginVertical: 30,
|
|
||||||
height: 1,
|
|
||||||
width: '80%',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { Stack } from "expo-router";
|
||||||
|
import { StatusBar } from "expo-status-bar";
|
||||||
|
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
import GoBack from "@/assets/icons/chevron_left.svg";
|
||||||
|
import { useRouter } from "expo-router";
|
||||||
|
|
||||||
|
export default function AuthLayout() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StatusBar style="dark" />
|
||||||
|
<Stack
|
||||||
|
screenOptions={{
|
||||||
|
headerShown: false,
|
||||||
|
animation: "fade",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="login"
|
||||||
|
options={{
|
||||||
|
headerShown: true,
|
||||||
|
title: "My Account",
|
||||||
|
headerTitleStyle: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: "#252A34",
|
||||||
|
},
|
||||||
|
headerShadowVisible: true,
|
||||||
|
headerLeft: () => {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity onPress={() => router.back()}>
|
||||||
|
<GoBack />
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
headerStyle: {
|
||||||
|
backgroundColor: "#F3F5F8",
|
||||||
|
},
|
||||||
|
animation: "slide_from_right",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#F3F5F8",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
Image,
|
||||||
|
TouchableOpacity,
|
||||||
|
ScrollView,
|
||||||
|
} from "react-native";
|
||||||
|
import { MaterialIcons } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
export default function ProfileScreen() {
|
||||||
|
return (
|
||||||
|
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||||
|
<View style={styles.avatarContainer}>
|
||||||
|
<Image
|
||||||
|
source={require("../../assets/images/user_image.jpg")}
|
||||||
|
style={styles.avatar}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity style={styles.editAvatar}>
|
||||||
|
<MaterialIcons name="edit" size={20} color="#FDFDFD" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<View style={styles.card}>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<View style={styles.textGroup}>
|
||||||
|
<Text style={styles.label}>Name</Text>
|
||||||
|
<Text style={styles.value}>Amar Kesari</Text>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity>
|
||||||
|
<MaterialIcons name="edit" size={20} color="#555C70" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<View style={styles.divider} />
|
||||||
|
<View style={styles.row}>
|
||||||
|
<View style={styles.textGroup}>
|
||||||
|
<Text style={styles.label}>Mobile Number</Text>
|
||||||
|
<Text style={styles.value}>9876543210</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Other Menu Items */}
|
||||||
|
<View style={styles.card}>
|
||||||
|
{menuItem("My Vehicle")}
|
||||||
|
<View style={styles.divider} />
|
||||||
|
{menuItem("Language")}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.card}>
|
||||||
|
{menuItem("About App")}
|
||||||
|
<View style={styles.divider} />
|
||||||
|
{menuItem("Logout")}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuItem = (title: string) => (
|
||||||
|
<TouchableOpacity style={styles.menuRow}>
|
||||||
|
<Text style={styles.menuText}>{title}</Text>
|
||||||
|
<MaterialIcons name="chevron-right" size={20} color="#555C70" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
borderWidth: 1,
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#F3F5F8",
|
||||||
|
},
|
||||||
|
topBar: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 12,
|
||||||
|
backgroundColor: "#F3F5F8",
|
||||||
|
},
|
||||||
|
backButton: {
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
headerText: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
marginLeft: 8,
|
||||||
|
color: "#252A34",
|
||||||
|
},
|
||||||
|
scrollContent: {
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingBottom: 32,
|
||||||
|
},
|
||||||
|
avatarContainer: {
|
||||||
|
alignItems: "center",
|
||||||
|
marginVertical: 24,
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
borderRadius: 60,
|
||||||
|
},
|
||||||
|
editAvatar: {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 0,
|
||||||
|
right: 105,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: "#008866",
|
||||||
|
borderRadius: 20,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: "#FCFCFC",
|
||||||
|
borderRadius: 8,
|
||||||
|
marginBottom: 16,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 8,
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingVertical: 12,
|
||||||
|
},
|
||||||
|
textGroup: {
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#252A34",
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: "600",
|
||||||
|
color: "#252A34",
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
divider: {
|
||||||
|
height: 1,
|
||||||
|
backgroundColor: "#E5E9F0",
|
||||||
|
marginVertical: 4,
|
||||||
|
},
|
||||||
|
menuRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingVertical: 16,
|
||||||
|
},
|
||||||
|
menuText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#252A34",
|
||||||
|
},
|
||||||
|
});
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 707 KiB |
|
|
@ -1,83 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { StyleSheet } from "react-native";
|
|
||||||
|
|
||||||
import { ExternalLink } from "./ExternalLink";
|
|
||||||
import { MonoText } from "./StyledText";
|
|
||||||
import { Text, View } from "./Themed";
|
|
||||||
|
|
||||||
import Colors from "@/constants/Colors";
|
|
||||||
|
|
||||||
export default function EditScreenInfo({ path }: { path: string }) {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<View style={styles.getStartedContainer}>
|
|
||||||
<Text
|
|
||||||
style={styles.getStartedText}
|
|
||||||
lightColor="rgba(0,0,0,0.8)"
|
|
||||||
darkColor="rgba(255,255,255,0.8)"
|
|
||||||
>
|
|
||||||
Open up the code for this screen:
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
|
|
||||||
darkColor="rgba(255,255,255,0.05)"
|
|
||||||
lightColor="rgba(0,0,0,0.05)"
|
|
||||||
>
|
|
||||||
<MonoText>{path}</MonoText>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text
|
|
||||||
style={styles.getStartedText}
|
|
||||||
lightColor="rgba(0,0,0,0.8)"
|
|
||||||
darkColor="rgba(255,255,255,0.8)"
|
|
||||||
>
|
|
||||||
Change any of the text, save the file, and your app will automatically
|
|
||||||
update.
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.helpContainer}>
|
|
||||||
<ExternalLink
|
|
||||||
style={styles.helpLink}
|
|
||||||
href="https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet"
|
|
||||||
>
|
|
||||||
<Text style={styles.helpLinkText} lightColor={Colors.light.tint}>
|
|
||||||
Tap here if your app doesn't automatically update after making
|
|
||||||
changes
|
|
||||||
</Text>
|
|
||||||
</ExternalLink>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
getStartedContainer: {
|
|
||||||
alignItems: "center",
|
|
||||||
marginHorizontal: 50,
|
|
||||||
},
|
|
||||||
homeScreenFilename: {
|
|
||||||
marginVertical: 7,
|
|
||||||
},
|
|
||||||
codeHighlightContainer: {
|
|
||||||
borderRadius: 3,
|
|
||||||
paddingHorizontal: 4,
|
|
||||||
},
|
|
||||||
getStartedText: {
|
|
||||||
fontSize: 17,
|
|
||||||
lineHeight: 24,
|
|
||||||
textAlign: "center",
|
|
||||||
},
|
|
||||||
helpContainer: {
|
|
||||||
marginTop: 15,
|
|
||||||
marginHorizontal: 20,
|
|
||||||
alignItems: "center",
|
|
||||||
},
|
|
||||||
helpLink: {
|
|
||||||
paddingVertical: 15,
|
|
||||||
},
|
|
||||||
helpLinkText: {
|
|
||||||
textAlign: "center",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"development": {
|
||||||
|
"env": {
|
||||||
|
"ENV": "development",
|
||||||
|
"BASE_URL": "https://dev-api-service.vecmocon.com/service-buddy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"env": {
|
||||||
|
"ENV": "production",
|
||||||
|
"BASE_URL": "https://dev-api-service.vecmocon.com/service-buddy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import { BmsState } from "@/constants/types";
|
||||||
const SERVER_URL =
|
const SERVER_URL =
|
||||||
"http://dev.vec-tr.ai:8089/?dashboardId=deviceDashboardSocket&assetId=V16000868651064644504";
|
"http://dev.vec-tr.ai:8089/?dashboardId=deviceDashboardSocket&assetId=V16000868651064644504";
|
||||||
const TOKEN =
|
const TOKEN =
|
||||||
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2NCIsImFjdGlvbiI6ImF1dGgiLCJ0b2tlbi12ZXJzaW9uIjowLCJpYXQiOjE3NTE0NTgxNDQsImV4cCI6MTc1MTU0NDU0NH0.ETM2hCU_5EtVcKAABd_69fyxS-FNuJ5Mv-QbY014sBY";
|
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI2NCIsImFjdGlvbiI6ImF1dGgiLCJ0b2tlbi12ZXJzaW9uIjowLCJpYXQiOjE3NTE1Mzg0MDAsImV4cCI6MTc1MTYyNDgwMH0.QIGyV9_jbtv0F8YzbzIgn_669HJz2ftI8KckpPGN0UU";
|
||||||
|
|
||||||
let socket: Socket | null = null;
|
let socket: Socket | null = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,24 @@
|
||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { BmsState } from "@/constants/types";
|
||||||
|
import { createSlice, PayloadAction, createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
interface TelemetryState {
|
interface TelemetryState {
|
||||||
SoH: number;
|
SoH: number | null;
|
||||||
SoC: number;
|
SoC: number | null;
|
||||||
|
chargingState: BmsState | null;
|
||||||
|
lat: number | null;
|
||||||
|
lon: number | null;
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: TelemetryState = {
|
const initialState: TelemetryState = {
|
||||||
SoH: 0,
|
SoH: null,
|
||||||
SoC: 0,
|
SoC: null,
|
||||||
|
chargingState: null,
|
||||||
|
lat: null,
|
||||||
|
lon: null,
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const telemetrySlice = createSlice({
|
export const telemetrySlice = createSlice({
|
||||||
|
|
@ -17,8 +28,17 @@ export const telemetrySlice = createSlice({
|
||||||
updateTelemetry: (state, action: PayloadAction<TelemetryState>) => {
|
updateTelemetry: (state, action: PayloadAction<TelemetryState>) => {
|
||||||
return { ...state, ...action.payload };
|
return { ...state, ...action.payload };
|
||||||
},
|
},
|
||||||
|
setTelemetryLoading: (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
setTelemetryError: (state, action: PayloadAction<string>) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { updateTelemetry } = telemetrySlice.actions;
|
export const { updateTelemetry, setTelemetryLoading, setTelemetryError } =
|
||||||
|
telemetrySlice.actions;
|
||||||
export default telemetrySlice.reducer;
|
export default telemetrySlice.reducer;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { AppTheme } from "./types";
|
||||||
|
|
||||||
|
export const DarkTheme: AppTheme = {
|
||||||
|
dark: true,
|
||||||
|
colors: {
|
||||||
|
primary: "#008761",
|
||||||
|
background: "#252A34",
|
||||||
|
card: "#1C1E21",
|
||||||
|
text: "#F3F5F8",
|
||||||
|
border: "#4A4F57",
|
||||||
|
notification: "#F4C5C3",
|
||||||
|
|
||||||
|
secondaryBackground: "#1C1E21",
|
||||||
|
accent: "#F3F5F8",
|
||||||
|
success: "#B8F1E3",
|
||||||
|
warning: "#FDE9E7",
|
||||||
|
info: "#D7E6FD",
|
||||||
|
muted: "#B9BDC5",
|
||||||
|
},
|
||||||
|
fonts: {
|
||||||
|
regular: "System",
|
||||||
|
bold: "System",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { LightTheme } from "./light";
|
||||||
|
export { DarkTheme } from "./dark";
|
||||||
|
export type { AppTheme } from "./types";
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { AppTheme } from "./types";
|
||||||
|
|
||||||
|
export const LightTheme: AppTheme = {
|
||||||
|
dark: false,
|
||||||
|
colors: {
|
||||||
|
primary: "#008761",
|
||||||
|
background: "#F3F5F8",
|
||||||
|
card: "#FFFFFF",
|
||||||
|
text: "#1C1E21",
|
||||||
|
border: "#B9BDC5",
|
||||||
|
notification: "#F4C5C3",
|
||||||
|
|
||||||
|
// custom additions
|
||||||
|
secondaryBackground: "#E5EBFD",
|
||||||
|
accent: "#252A34",
|
||||||
|
success: "#B8F1E3",
|
||||||
|
warning: "#FDE9E7",
|
||||||
|
info: "#D7E6FD",
|
||||||
|
muted: "#4A4F57",
|
||||||
|
},
|
||||||
|
fonts: {
|
||||||
|
regular: "System",
|
||||||
|
bold: "System",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
export interface AppTheme {
|
||||||
|
dark: boolean;
|
||||||
|
colors: {
|
||||||
|
primary: string;
|
||||||
|
background: string;
|
||||||
|
card: string;
|
||||||
|
text: string;
|
||||||
|
border: string;
|
||||||
|
notification: string;
|
||||||
|
|
||||||
|
secondaryBackground?: string;
|
||||||
|
accent?: string;
|
||||||
|
success?: string;
|
||||||
|
warning?: string;
|
||||||
|
info?: string;
|
||||||
|
muted?: string;
|
||||||
|
};
|
||||||
|
fonts: {
|
||||||
|
regular: string;
|
||||||
|
bold?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { useTheme } from "@react-navigation/native";
|
||||||
|
import { AppTheme } from "./types";
|
||||||
|
|
||||||
|
export function useThemeColors(): AppTheme["colors"] {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
export function calculateBearing(
|
||||||
|
lat1: number,
|
||||||
|
lon1: number,
|
||||||
|
lat2: number,
|
||||||
|
lon2: number
|
||||||
|
): number {
|
||||||
|
const φ1 = (lat1 * Math.PI) / 180; // Convert to radians
|
||||||
|
const φ2 = (lat2 * Math.PI) / 180;
|
||||||
|
const Δλ = ((lon2 - lon1) * Math.PI) / 180;
|
||||||
|
|
||||||
|
// Bearing formula
|
||||||
|
const y = Math.sin(Δλ) * Math.cos(φ2);
|
||||||
|
const x =
|
||||||
|
Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);
|
||||||
|
let θ = Math.atan2(y, x);
|
||||||
|
θ = (θ * 180) / Math.PI; // Convert back to degrees
|
||||||
|
return (θ + 360) % 360; // Normalize to 0-360°
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateDistance(
|
||||||
|
lat1: number,
|
||||||
|
lon1: number,
|
||||||
|
lat2: number,
|
||||||
|
lon2: number
|
||||||
|
): number {
|
||||||
|
const R = 6371e3; // Earth radius in meters
|
||||||
|
const φ1 = (lat1 * Math.PI) / 180;
|
||||||
|
const φ2 = (lat2 * Math.PI) / 180;
|
||||||
|
const Δφ = ((lat2 - lat1) * Math.PI) / 180;
|
||||||
|
const Δλ = ((lon2 - lon1) * Math.PI) / 180;
|
||||||
|
|
||||||
|
const a =
|
||||||
|
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
|
||||||
|
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
|
||||||
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
return R * c; // Distance in meters
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue