current location map ui implemented
This commit is contained in:
committed by
GitHub
parent
85fbb0a364
commit
dcb9ff3bee
@@ -1,4 +1,6 @@
|
|||||||
import { useUser } from "@clerk/clerk-expo";
|
import { useUser } from "@clerk/clerk-expo";
|
||||||
|
import * as Location from "expo-location";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
FlatList,
|
FlatList,
|
||||||
@@ -13,6 +15,7 @@ import { GoogleTextInput } from "@/components/google-text-input";
|
|||||||
import { Map } from "@/components/map";
|
import { Map } from "@/components/map";
|
||||||
import { RideCard } from "@/components/ride-card";
|
import { RideCard } from "@/components/ride-card";
|
||||||
import { icons, images } from "@/constants";
|
import { icons, images } from "@/constants";
|
||||||
|
import { useLocationStore } from "@/store";
|
||||||
|
|
||||||
const recentRides = [
|
const recentRides = [
|
||||||
{
|
{
|
||||||
@@ -126,12 +129,38 @@ const recentRides = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
|
const { setUserLocation, setDestinationLocation } = useLocationStore();
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const isLoading = false;
|
const isLoading = false;
|
||||||
|
|
||||||
|
const [hasPermissions, setHasPermissions] = useState(false);
|
||||||
|
|
||||||
const handleSignOut = () => {};
|
const handleSignOut = () => {};
|
||||||
const handleDestinationPress = () => {};
|
const handleDestinationPress = () => {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const requestLocation = async () => {
|
||||||
|
let { status } = await Location.requestForegroundPermissionsAsync();
|
||||||
|
|
||||||
|
if (status !== "granted") return setHasPermissions(false);
|
||||||
|
|
||||||
|
let location = await Location.getCurrentPositionAsync();
|
||||||
|
|
||||||
|
const address = await Location.reverseGeocodeAsync({
|
||||||
|
longitude: location.coords?.longitude,
|
||||||
|
latitude: location.coords?.latitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
setUserLocation({
|
||||||
|
latitude: location.coords.latitude,
|
||||||
|
longitude: location.coords.longitude,
|
||||||
|
address: `${address[0].name}, ${address[0].region}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
requestLocation();
|
||||||
|
}, [setUserLocation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView className="bg-general-500">
|
<SafeAreaView className="bg-general-500">
|
||||||
<FlatList
|
<FlatList
|
||||||
@@ -185,7 +214,7 @@ const Home = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Text className="text-xl font-JakartaBold mt-5 mb-3">
|
<Text className="text-xl font-JakartaBold mt-5 mb-3">
|
||||||
Your current location
|
Your Current Location
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View className="flex flex-row items-center bg-transparent h-[300px]">
|
<View className="flex flex-row items-center bg-transparent h-[300px]">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { GoogleInputProps } from "@/types/type";
|
|
||||||
import { Text, View } from "react-native";
|
import { Text, View } from "react-native";
|
||||||
|
|
||||||
|
import type { GoogleInputProps } from "@/types/type";
|
||||||
|
|
||||||
export const GoogleTextInput = ({
|
export const GoogleTextInput = ({
|
||||||
icon,
|
icon,
|
||||||
initialLocation,
|
initialLocation,
|
||||||
|
|||||||
+113
-4
@@ -1,9 +1,118 @@
|
|||||||
import { Text, View } from "react-native";
|
import { useEffect, useState } from "react";
|
||||||
|
import MapView, { Marker, PROVIDER_DEFAULT } from "react-native-maps";
|
||||||
|
|
||||||
|
import { calculateRegion, generateMarkersFromData } from "@/lib/map";
|
||||||
|
import { useDriverStore, useLocationStore } from "@/store";
|
||||||
|
import type { MarkerData } from "@/types/type";
|
||||||
|
import { icons } from "@/constants";
|
||||||
|
|
||||||
|
const drivers = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
driver_id: 1,
|
||||||
|
first_name: "James",
|
||||||
|
last_name: "Wilson",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/dae59f69-2c1f-48c3-a883-017bcf0f9950/-/preview/1000x666/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/a2dc52b2-8bf7-4e49-9a36-3ffb5229ed02/-/preview/465x466/",
|
||||||
|
car_seats: 4,
|
||||||
|
rating: 4.8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
driver_id: 2,
|
||||||
|
first_name: "David",
|
||||||
|
last_name: "Brown",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/6ea6d83d-ef1a-483f-9106-837a3a5b3f67/-/preview/1000x666/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/a3872f80-c094-409c-82f8-c9ff38429327/-/preview/930x932/",
|
||||||
|
car_seats: 5,
|
||||||
|
rating: 4.6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
driver_id: 3,
|
||||||
|
first_name: "Michael",
|
||||||
|
last_name: "Johnson",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/0330d85c-232e-4c30-bd04-e5e4d0e3d688/-/preview/826x822/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/289764fb-55b6-4427-b1d1-f655987b4a14/-/preview/930x932/",
|
||||||
|
car_seats: 4,
|
||||||
|
rating: 4.7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
driver_id: 4,
|
||||||
|
first_name: "Robert",
|
||||||
|
last_name: "Green",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/fdfc54df-9d24-40f7-b7d3-6f391561c0db/-/preview/626x417/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/b6fb3b55-7676-4ff3-8484-fb115e268d32/-/preview/930x932/",
|
||||||
|
car_seats: 4,
|
||||||
|
rating: 4.9,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const Map = () => {
|
export const Map = () => {
|
||||||
|
const {
|
||||||
|
userLatitude,
|
||||||
|
userLongitude,
|
||||||
|
destinationLatitude,
|
||||||
|
destinationLongitude,
|
||||||
|
} = useLocationStore();
|
||||||
|
const { selectedDriver, setDrivers } = useDriverStore();
|
||||||
|
const [markers, setMarkers] = useState<MarkerData[]>([]);
|
||||||
|
|
||||||
|
const region = calculateRegion({
|
||||||
|
userLatitude,
|
||||||
|
userLongitude,
|
||||||
|
destinationLatitude,
|
||||||
|
destinationLongitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Array.isArray(drivers)) {
|
||||||
|
if (!userLatitude || !userLongitude) return;
|
||||||
|
|
||||||
|
const newMarkers = generateMarkersFromData({
|
||||||
|
data: drivers,
|
||||||
|
userLatitude,
|
||||||
|
userLongitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
setMarkers(newMarkers);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [drivers]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<MapView
|
||||||
<Text>Map</Text>
|
provider={PROVIDER_DEFAULT}
|
||||||
</View>
|
className="w-full h-full rounded-2xl"
|
||||||
|
tintColor="black"
|
||||||
|
mapType="mutedStandard"
|
||||||
|
showsPointsOfInterest={false}
|
||||||
|
initialRegion={region}
|
||||||
|
showsUserLocation
|
||||||
|
userInterfaceStyle="light"
|
||||||
|
>
|
||||||
|
{markers.map((marker) => (
|
||||||
|
<Marker
|
||||||
|
key={marker.id}
|
||||||
|
coordinate={{
|
||||||
|
latitude: marker.latitude,
|
||||||
|
longitude: marker.longitude,
|
||||||
|
}}
|
||||||
|
title={marker.title}
|
||||||
|
image={
|
||||||
|
selectedDriver === marker.id ? icons.selectedMarker : icons.marker
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</MapView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
import type { Driver, MarkerData } from "@/types/type";
|
||||||
|
|
||||||
|
const directionsAPI = process.env.EXPO_PUBLIC_GOOGLE_API_KEY;
|
||||||
|
|
||||||
|
export const generateMarkersFromData = ({
|
||||||
|
data,
|
||||||
|
userLatitude,
|
||||||
|
userLongitude,
|
||||||
|
}: {
|
||||||
|
data: Driver[];
|
||||||
|
userLatitude: number;
|
||||||
|
userLongitude: number;
|
||||||
|
}): MarkerData[] => {
|
||||||
|
return data.map((driver, i) => {
|
||||||
|
const latOffset = (Math.random() - 0.5) * 0.01; // Random offset between -0.005 and 0.005
|
||||||
|
const lngOffset = (Math.random() - 0.5) * 0.01; // Random offset between -0.005 and 0.005
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: i,
|
||||||
|
latitude: userLatitude + latOffset,
|
||||||
|
longitude: userLongitude + lngOffset,
|
||||||
|
title: `${driver.first_name} ${driver.last_name}`,
|
||||||
|
...driver,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const calculateRegion = ({
|
||||||
|
userLatitude,
|
||||||
|
userLongitude,
|
||||||
|
destinationLatitude,
|
||||||
|
destinationLongitude,
|
||||||
|
}: {
|
||||||
|
userLatitude: number | null;
|
||||||
|
userLongitude: number | null;
|
||||||
|
destinationLatitude?: number | null;
|
||||||
|
destinationLongitude?: number | null;
|
||||||
|
}) => {
|
||||||
|
if (!userLatitude || !userLongitude) {
|
||||||
|
return {
|
||||||
|
latitude: 37.78825,
|
||||||
|
longitude: -122.4324,
|
||||||
|
latitudeDelta: 0.01,
|
||||||
|
longitudeDelta: 0.01,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!destinationLatitude || !destinationLongitude) {
|
||||||
|
return {
|
||||||
|
latitude: userLatitude,
|
||||||
|
longitude: userLongitude,
|
||||||
|
latitudeDelta: 0.01,
|
||||||
|
longitudeDelta: 0.01,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const minLat = Math.min(userLatitude, destinationLatitude);
|
||||||
|
const maxLat = Math.max(userLatitude, destinationLatitude);
|
||||||
|
const minLng = Math.min(userLongitude, destinationLongitude);
|
||||||
|
const maxLng = Math.max(userLongitude, destinationLongitude);
|
||||||
|
|
||||||
|
const latitudeDelta = (maxLat - minLat) * 1.3; // Adding some padding
|
||||||
|
const longitudeDelta = (maxLng - minLng) * 1.3; // Adding some padding
|
||||||
|
|
||||||
|
const latitude = (userLatitude + destinationLatitude) / 2;
|
||||||
|
const longitude = (userLongitude + destinationLongitude) / 2;
|
||||||
|
|
||||||
|
return {
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
latitudeDelta,
|
||||||
|
longitudeDelta,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const calculateDriverTimes = async ({
|
||||||
|
markers,
|
||||||
|
userLatitude,
|
||||||
|
userLongitude,
|
||||||
|
destinationLatitude,
|
||||||
|
destinationLongitude,
|
||||||
|
}: {
|
||||||
|
markers: MarkerData[];
|
||||||
|
userLatitude: number | null;
|
||||||
|
userLongitude: number | null;
|
||||||
|
destinationLatitude: number | null;
|
||||||
|
destinationLongitude: number | null;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
!userLatitude ||
|
||||||
|
!userLongitude ||
|
||||||
|
!destinationLatitude ||
|
||||||
|
!destinationLongitude
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const timesPromises = markers.map(async (marker) => {
|
||||||
|
const responseToUser = await fetch(
|
||||||
|
`https://maps.googleapis.com/maps/api/directions/json?origin=${marker.latitude},${marker.longitude}&destination=${userLatitude},${userLongitude}&key=${directionsAPI}`,
|
||||||
|
);
|
||||||
|
const dataToUser = await responseToUser.json();
|
||||||
|
const timeToUser = dataToUser.routes[0].legs[0].duration.value; // Time in seconds
|
||||||
|
|
||||||
|
const responseToDestination = await fetch(
|
||||||
|
`https://maps.googleapis.com/maps/api/directions/json?origin=${userLatitude},${userLongitude}&destination=${destinationLatitude},${destinationLongitude}&key=${directionsAPI}`,
|
||||||
|
);
|
||||||
|
const dataToDestination = await responseToDestination.json();
|
||||||
|
const timeToDestination =
|
||||||
|
dataToDestination.routes[0].legs[0].duration.value; // Time in seconds
|
||||||
|
|
||||||
|
const totalTime = (timeToUser + timeToDestination) / 60; // Total time in minutes
|
||||||
|
const price = (totalTime * 0.5).toFixed(2); // Calculate price based on time
|
||||||
|
|
||||||
|
return { ...marker, time: totalTime, price };
|
||||||
|
});
|
||||||
|
|
||||||
|
return await Promise.all(timesPromises);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error calculating driver times:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { Ride } from "@/types/type";
|
import type { Ride } from "@/types/type";
|
||||||
|
|
||||||
export const sortRides = (rides: Ride[]): Ride[] => {
|
export const sortRides = (rides: Ride[]): Ride[] => {
|
||||||
const result = rides.sort((a, b) => {
|
const result = rides.sort((a, b) => {
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import type { DriverStore, LocationStore, MarkerData } from "@/types/type";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
export const useLocationStore = create<LocationStore>((set) => ({
|
||||||
|
userAddress: null,
|
||||||
|
userLongitude: null,
|
||||||
|
userLatitude: null,
|
||||||
|
destinationLongitude: null,
|
||||||
|
destinationLatitude: null,
|
||||||
|
destinationAddress: null,
|
||||||
|
|
||||||
|
setUserLocation: ({
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
address,
|
||||||
|
}: {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
address: string;
|
||||||
|
}) => {
|
||||||
|
set(() => ({
|
||||||
|
userLatitude: latitude,
|
||||||
|
userLongitude: longitude,
|
||||||
|
userAddress: address,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
setDestinationLocation: ({
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
address,
|
||||||
|
}: {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
address: string;
|
||||||
|
}) => {
|
||||||
|
set(() => ({
|
||||||
|
destinationLatitude: latitude,
|
||||||
|
destinationLongitude: longitude,
|
||||||
|
destinationAddress: address,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const useDriverStore = create<DriverStore>((set) => ({
|
||||||
|
drivers: [] as MarkerData[],
|
||||||
|
selectedDriver: null,
|
||||||
|
|
||||||
|
setSelectedDriver: (driverId: number) =>
|
||||||
|
set(() => ({ selectedDriver: driverId })),
|
||||||
|
setDrivers: (drivers: MarkerData[]) => set(() => ({ drivers })),
|
||||||
|
clearSelectedDriver: () => set(() => ({ selectedDriver: null })),
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user