180 lines
5.3 KiB
TypeScript
180 lines
5.3 KiB
TypeScript
import { useEffect, useState, useRef } from "react";
|
|
import { Text, View, ScrollView, Alert } from "react-native";
|
|
|
|
import { Picker } from "@react-native-picker/picker";
|
|
|
|
import { Stack, useLocalSearchParams } from "expo-router";
|
|
|
|
import { AntDesign } from "@expo/vector-icons";
|
|
|
|
import { styled } from "nativewind";
|
|
|
|
import utmObj from "utm-latlng";
|
|
|
|
import QueryItem from "../../components/QueryItem";
|
|
|
|
declare function isNaN(x: string | number): boolean;
|
|
|
|
interface itemInterface {
|
|
key: String;
|
|
lastUpdated: String;
|
|
name: String;
|
|
perFloz: Number;
|
|
price: Number;
|
|
store: String;
|
|
volumeFloz: Number;
|
|
distance: Number;
|
|
}
|
|
|
|
export default function () {
|
|
const [items, setItems] = useState([]);
|
|
const [queryType, setQueryType] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const utm = new utmObj();
|
|
|
|
const StyledPicker = styled(Picker);
|
|
const StyledText = styled(Text);
|
|
const StyledIcon = styled(AntDesign);
|
|
|
|
const slug = useLocalSearchParams().slug.toString().split("%2B");
|
|
const lon = slug[0];
|
|
const lat = slug[1];
|
|
const utmCoords = utm.convertLatLngToUtm(lat, lon, 5);
|
|
const query = useRef(slug[2]);
|
|
|
|
// get store information
|
|
const getItems = async (sortBy: string) => {
|
|
setLoading(true);
|
|
const res = await fetch(
|
|
`${process.env.EXPO_PUBLIC_BACKEND_URL}/search?` +
|
|
new URLSearchParams({
|
|
easting: utmCoords.Easting,
|
|
northing: utmCoords.Northing,
|
|
query: query.current,
|
|
})
|
|
);
|
|
if (!res.ok) {
|
|
if (res.status === 404) {
|
|
const notFound = (
|
|
<View
|
|
className="flex flex-row w-[100vw] py-4 justify-center items-center dark:text-white"
|
|
style={{ flex: 1 }}
|
|
>
|
|
<StyledText className="text-xl dark:text-white">
|
|
No results for "{slug[2]}"
|
|
</StyledText>
|
|
</View>
|
|
);
|
|
setItems(notFound);
|
|
return setLoading(false);
|
|
} else
|
|
return Alert.alert(
|
|
"Error!",
|
|
"Server error. Please report to ak95@riseup.net"
|
|
);
|
|
}
|
|
const json = await res.json();
|
|
switch (sortBy) {
|
|
case "Distance":
|
|
json.sort((a: itemInterface, b: itemInterface) => {
|
|
return Number(a.distance) - Number(b.distance);
|
|
});
|
|
break;
|
|
case "Price":
|
|
json.sort((a: itemInterface, b: itemInterface) => {
|
|
return Number(a.price) - Number(b.price);
|
|
});
|
|
break;
|
|
case "Price per Fluid Ounce":
|
|
json.sort((a: itemInterface, b: itemInterface) => {
|
|
return Number(a.perFloz) - Number(b.perFloz);
|
|
});
|
|
break;
|
|
case "Last Updated":
|
|
json.sort((a: itemInterface, b: itemInterface) => {
|
|
const aDate = new Date(a.lastUpdated.replace(" ", "T") + "Z");
|
|
const bDate = new Date(b.lastUpdated.replace(" ", "T") + "Z");
|
|
if (aDate < bDate) return -1;
|
|
else if (aDate > bDate) return 1;
|
|
return 0;
|
|
});
|
|
break;
|
|
}
|
|
|
|
const jsonItems = json.map((item: itemInterface, index: number) => {
|
|
const date = new Date(item.lastUpdated.replace(" ", "T") + "Z");
|
|
return (
|
|
<View className="flex flex-row w-[100vw]" key={index}>
|
|
<QueryItem
|
|
name={item.name.toString()}
|
|
volume={item.volumeFloz.toString()}
|
|
price={item.price.toString()}
|
|
date={date}
|
|
storeKey={item.store.toString()}
|
|
distance={item.distance}
|
|
perFloz={item.perFloz}
|
|
/>
|
|
</View>
|
|
);
|
|
});
|
|
setItems(jsonItems);
|
|
setLoading(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
getItems(queryType);
|
|
}, [queryType]);
|
|
|
|
return (
|
|
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
alignItems: "center",
|
|
height: "100%",
|
|
}}
|
|
>
|
|
<Stack.Screen
|
|
options={{
|
|
title: `Results for "${query.current}"`,
|
|
}}
|
|
/>
|
|
<ScrollView style={{ flex: 1 }}>
|
|
<View
|
|
className={
|
|
loading
|
|
? "border-b dark:border-white flex-1 opacity-0"
|
|
: "border-b dark:border-white flex-1"
|
|
}
|
|
>
|
|
<View className="flex absolute h-full w-full">
|
|
<View className="flex flex-row flex-1 justify-end items-center gap-1 px-2">
|
|
<StyledText className="text-lg dark:text-white">
|
|
Sort by {queryType}
|
|
</StyledText>
|
|
<StyledIcon name="down" className="text-lg dark:text-white" />
|
|
</View>
|
|
</View>
|
|
<StyledPicker
|
|
selectedValue={queryType}
|
|
onValueChange={(value, index: number) => {
|
|
setQueryType(value);
|
|
}}
|
|
className="opacity-0 bg-red-700"
|
|
>
|
|
<Picker.Item label="Distance" value="Distance" />
|
|
<Picker.Item label="Price" value="Price" />
|
|
<Picker.Item
|
|
label="Price per Fluid Ounce"
|
|
value="Price per Fluid Ounce"
|
|
/>
|
|
<Picker.Item label="Last Updated" value="Last Updated" />
|
|
</StyledPicker>
|
|
</View>
|
|
<View className={loading && "opacity-0"}>{items}</View>
|
|
</ScrollView>
|
|
</View>
|
|
</ScrollView>
|
|
);
|
|
}
|