diff --git a/flask/backend.py b/flask/backend.py index 9500694..40f94bf 100644 --- a/flask/backend.py +++ b/flask/backend.py @@ -57,9 +57,11 @@ def fetchItem(store): 'lowername': request.args['itemName'].lower(), 'store': store['key'], }) - item = fetch.items[0] - if not item: + if fetch.items: + item = fetch.items[0] + return item + else: while (fetch.last is not None): fetch = itemsDB.fetch({ 'lowername': request.args['itemName'].lower(), @@ -69,9 +71,7 @@ def fetchItem(store): if fetch.items: item = fetch.items[0] - break - - return item + return item def fetchItems(store): """ Fetch all items from a given store """ diff --git a/react-native/app/index.tsx b/react-native/app/index.tsx index 26c6cc2..00cffa5 100644 --- a/react-native/app/index.tsx +++ b/react-native/app/index.tsx @@ -1,92 +1,46 @@ import { useState, useRef, useEffect } from "react"; -import { useWindowDimensions, Alert, StyleSheet } from "react-native"; +import { useWindowDimensions, Platform } from "react-native"; import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context"; -import WebView, { WebViewMessageEvent } from "react-native-webview"; +import WebView from "react-native-webview"; + import { useAssets } from "expo-asset"; import * as Location from "expo-location"; -import { useNavigation, router } from "expo-router"; +import { useNavigation } from "expo-router"; + +import messageHandler from "./messageHandler"; export default function () { + const [webViewKey, setWebViewKey] = useState(Math.random() * 10); const [assets] = useAssets([require("../assets/index.html")]); const [htmlString, setHtmlString] = useState(); const dimensions = useWindowDimensions(); const webViewRef = useRef(); const navigation = useNavigation(); - const messageHandler = (event: WebViewMessageEvent) => { - if (typeof event.nativeEvent.data == "string") { - const message = event.nativeEvent.data; - if (message === "new pin start") { - Alert.alert( - "New Pin", - `Please select location for new pin then press "OK"`, - [ - { - text: "OK", - onPress: () => { - // makes injectable javascript string - const str = `window.placePin(); true()`; - // passes string to webview - there it is handled by OpenLayers to change the current location - webViewRef.current?.injectJavaScript(str); - }, - }, - ] - ); - } else if (message.startsWith(`create@`)) { - const coords = message.slice(7).split(","); - router.push({ - pathname: "./store/new/[coords]", - params: { - coords: `${coords[0]}+${coords[1]}`, - }, - }); - } else if (message.startsWith(`open@`)) { - const coords = message.slice(5).split(","); - router.push({ - pathname: "./store/[coords]", - params: { - coords: `${coords[0]}+${coords[1]}`, - }, - }); - } else if (message.startsWith(`search@`)) { - const chunks = message.slice(7).split(":"); - const coords = chunks[0].split(","); - router.push({ - pathname: "./search/[slug]", - params: { - slug: `${coords[0]}+${coords[1]}+${chunks[1]}`, - }, - }); - } - } - }; - const getLocation = async () => { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== "granted") return; + const location = await Location.getCurrentPositionAsync({ - distanceInterval: 0, // for IOS + distanceInterval: 0, accuracy: Location.Accuracy.High, - timeInterval: 3000, // for Android + timeInterval: 3000, }); - // makes injectable javascript string - const str = `window.passLocation(${location.coords.longitude}, ${location.coords.latitude}); true()`; - // passes string to webview - there it is handled by OpenLayers to change the current location - webViewRef.current?.injectJavaScript(str); + + webViewRef.current?.injectJavaScript( + `setTimeout(() => {window.passLocation(${location.coords.longitude}, ${location.coords.latitude})}, 50); true` + ); }; - // refresh on navigating back to this page useEffect(() => { - // remove header navigation.setOptions({ headerShown: false }); - const focusHandler = navigation.addListener("focus", () => { - webViewRef.current?.reload(); + + return navigation.addListener("focus", () => { + setWebViewKey(Math.random() * 10); getLocation(); }); - return focusHandler; }, [navigation]); - // loads assets useEffect(() => { if (assets) { fetch(assets[0].localUri || "") @@ -95,29 +49,23 @@ export default function () { } }, [assets]); - // exits if no map passed in - if (!htmlString) { - return <>; - } - - const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, - }); + if (!htmlString) return <>; return ( - + (webViewRef.current = currentRef)} + ref={webViewRef} injectedJavascript="" source={{ html: htmlString, }} - javaScriptEnabled style={{ width: dimensions.width, height: dimensions.height, @@ -130,8 +78,13 @@ export default function () { containerStyle={{ flex: 1, }} - onMessage={messageHandler} + onMessage={(event) => { + messageHandler(event, webViewRef.current); + }} webviewDebuggingEnabled={true} + javaScriptEnabled={true} + domStorageEnabled={true} + key={webViewKey} /> diff --git a/react-native/app/item/new/[storeKey].tsx b/react-native/app/item/new/[storeKey].tsx index 69d7679..dfb2205 100644 --- a/react-native/app/item/new/[storeKey].tsx +++ b/react-native/app/item/new/[storeKey].tsx @@ -83,6 +83,7 @@ export default function () { break; case 500: + console.log(res); Alert.alert( "Error!", "Backend server error. Please report to ak95@riseup.net" @@ -108,6 +109,7 @@ export default function () { flex: 1, alignItems: "center", height: "100%", + paddingBottom: "5%", }} > { + if (typeof event.nativeEvent.data == "string") { + const message = event.nativeEvent.data; + + if (message === "new pin start") { + return Alert.alert( + "New Pin", + `Please select location for new pin then press "OK"`, + [ + { + text: "OK", + onPress: () => { + element.injectJavaScript(`window.placePin(); true()`); + }, + }, + ] + ); + } + + if (message.startsWith(`create@`)) { + const coords = message.slice(7).split(","); + return router.push({ + pathname: "./store/new/[coords]", + params: { + coords: `${coords[0]}+${coords[1]}`, + }, + }); + } + + if (message.startsWith(`open@`)) { + const coords = message.slice(5).split(","); + return router.push({ + pathname: "./store/[coords]", + params: { + coords: `${coords[0]}+${coords[1]}`, + }, + }); + } + + if (message.startsWith(`search@`)) { + const chunks = message.slice(7).split(":"); + const coords = chunks[0].split(","); + return router.push({ + pathname: "./search/[slug]", + params: { + slug: `${coords[0]}+${coords[1]}+${chunks[1]}`, + }, + }); + } + } +}; + +export default messageHandler; diff --git a/react-native/app/store/[coords].tsx b/react-native/app/store/[coords].tsx index dc3addd..760b02f 100644 --- a/react-native/app/store/[coords].tsx +++ b/react-native/app/store/[coords].tsx @@ -8,22 +8,18 @@ import { Alert, } from "react-native"; -import { - useNavigation, - Stack, - useLocalSearchParams, - router, -} from "expo-router"; -import * as ImagePicker from "expo-image-picker"; +import { useNavigation, Stack, useLocalSearchParams } from "expo-router"; + import * as FileSystem from "expo-file-system"; import { styled } from "nativewind"; -import utmObj from "utm-latlng"; -import mime from "mime"; - import StoreItem from "../../components/StoreItem"; +import handleAddItem from "./handleAddItem"; +import toUTM from "./toUTM"; +import uploadImage from "./uploadImage"; + declare function isNaN(x: string | number): boolean; interface storeItemInterface { @@ -43,16 +39,14 @@ export default function () { const [image, setImage] = useState(null); const navigation = useNavigation(); - const utm = new utmObj(); const StyledText = styled(Text); const coords = useLocalSearchParams().coords.toString().split("%2B"); const lon = coords[0]; const lat = coords[1]; - const utmCoords = utm.convertLatLngToUtm(lat, lon, 5); + const utmCoords = toUTM(lat, lon); - // get store information const getStore = async () => { const res = await fetch( `${process.env.EXPO_PUBLIC_BACKEND_URL}/?` + @@ -71,6 +65,7 @@ export default function () { const json = await res.json(); setStoreName(json.name); setStoreKey(json.key.toString()); + const imageKey = json.key.toString(); const items = json.items.map((item: storeItemInterface, index: number) => { const date = new Date(item.lastUpdated.replace(" ", "T") + "Z"); @@ -88,71 +83,20 @@ export default function () { }); setStoreItems(items); - const imageURL = `${process.env.EXPO_PUBLIC_BACKEND_URL}/img?imageKey=${storeKey}`; + const imageURL = `${process.env.EXPO_PUBLIC_BACKEND_URL}/img?imageKey=${imageKey}`; await FileSystem.downloadAsync( imageURL, - FileSystem.documentDirectory + storeKey + FileSystem.documentDirectory + imageKey ) .then(({ uri }) => { setImage(uri); }) .catch(() => {}); + console.log(image); }; useEffect(() => { - getStore(); - }, []); - - const uploadImage = async () => { - // No permissions request is necessary for launching the image library - let result = await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.All, - allowsEditing: true, - aspect: [4, 3], - quality: 1, - }); - - if (!result.canceled) { - setImage(result.assets[0]); - const formData = new FormData(); - formData.append("file", { - uri: image.uri, - type: mime.getType(image.uri), - name: "file", - } as unknown as File); - const res = await fetch( - `${process.env.EXPO_PUBLIC_BACKEND_URL}/?` + - new URLSearchParams({ - storeKey: storeKey, - }), - { - method: "PUT", - body: formData, - mode: "cors", - redirect: "follow", - } - ); - if (!res.ok) { - Alert.alert("Error!", "Server error! Please report to ak95@riseup.net"); - } - } - }; - - const handleAddItem = () => { - router.push({ - pathname: "/item/new/[storeKey]", - params: { - storeKey: storeKey, - }, - }); - }; - - useEffect(() => { - // remove header - const focusHandler = navigation.addListener("focus", () => { - getStore(); - }); - return focusHandler; + return navigation.addListener("focus", () => getStore()); }, [navigation]); return ( @@ -162,6 +106,7 @@ export default function () { flex: 1, alignItems: "center", height: "100%", + paddingBottom: "5%", }} > - + { + uploadImage(storeKey).then((uri) => { + setImage(uri); + }); + }} + > {image ? ( ) : ( @@ -187,7 +138,7 @@ export default function () { {storeItems} handleAddItem(storeKey)} > Add diff --git a/react-native/app/store/handleAddItem.ts b/react-native/app/store/handleAddItem.ts new file mode 100644 index 0000000..950081f --- /dev/null +++ b/react-native/app/store/handleAddItem.ts @@ -0,0 +1,12 @@ +import { router } from "expo-router"; + +const handleAddItem = (storeKey: string) => { + router.push({ + pathname: "/item/new/[storeKey]", + params: { + storeKey: storeKey, + }, + }); +}; + +export default handleAddItem; diff --git a/react-native/app/store/new/[coords].tsx b/react-native/app/store/new/[coords].tsx index a3e3d7f..7705d08 100644 --- a/react-native/app/store/new/[coords].tsx +++ b/react-native/app/store/new/[coords].tsx @@ -10,11 +10,12 @@ import { Alert, } from "react-native"; import { Stack, useLocalSearchParams, router } from "expo-router"; -import * as ImagePicker from "expo-image-picker"; import utmObj from "utm-latlng"; import { styled } from "nativewind"; import mime from "mime"; +import uploadImage from "./uploadImage"; + declare function isNaN(x: string | number): boolean; export default function () { @@ -27,20 +28,6 @@ export default function () { let { coords } = useLocalSearchParams(); - const uploadImage = async () => { - // No permissions request is necessary for launching the image library - let result = await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.All, - allowsEditing: true, - aspect: [4, 3], - quality: 1, - }); - - if (!result.canceled) { - setImage(result.assets[0]); - } - }; - const handleSubmit = async () => { coords = coords.split("%2B"); const lon = coords[0]; @@ -170,6 +157,7 @@ export default function () { flex: 1, alignItems: "center", height: "100%", + paddingBottom: "5%", }} > - + setImage(uploadImage())}> {image ? ( ) : ( diff --git a/react-native/app/store/new/uploadImage.ts b/react-native/app/store/new/uploadImage.ts new file mode 100644 index 0000000..6ca916a --- /dev/null +++ b/react-native/app/store/new/uploadImage.ts @@ -0,0 +1,17 @@ +import * as ImagePicker from "expo-image-picker"; + +const uploadImage = async () => { + // No permissions request is necessary for launching the image library + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.All, + allowsEditing: true, + aspect: [4, 3], + quality: 1, + }); + + if (!result.canceled) { + return result.assets[0]; + } +}; + +export default uploadImage; diff --git a/react-native/app/store/toUTM.js b/react-native/app/store/toUTM.js new file mode 100644 index 0000000..a27d04e --- /dev/null +++ b/react-native/app/store/toUTM.js @@ -0,0 +1,8 @@ +import utmObj from "utm-latlng"; + +const toUTM = (lat, lon) => { + const utm = new utmObj(); + return utm.convertLatLngToUtm(lat, lon, 5); +}; + +export default toUTM; diff --git a/react-native/app/store/uploadImage.ts b/react-native/app/store/uploadImage.ts new file mode 100644 index 0000000..de98e65 --- /dev/null +++ b/react-native/app/store/uploadImage.ts @@ -0,0 +1,41 @@ +import { Alert } from "react-native"; +import * as ImagePicker from "expo-image-picker"; +import mime from "mime"; + +const uploadImage = async (storeKey: string) => { + // No permissions request is necessary for launching the image library + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.All, + allowsEditing: true, + aspect: [4, 3], + quality: 1, + }); + + if (!result.canceled) { + const image = result.assets[0]; + const formData = new FormData(); + formData.append("file", { + uri: image.uri, + type: mime.getType(image.uri), + name: "file", + } as unknown as File); + const res = await fetch( + `${process.env.EXPO_PUBLIC_BACKEND_URL}/?` + + new URLSearchParams({ + storeKey: storeKey, + }), + { + method: "PUT", + body: formData, + mode: "cors", + redirect: "follow", + } + ); + if (!res.ok) { + Alert.alert("Error!", "Server error! Please report to ak95@riseup.net"); + } + return image.uri; + } +}; + +export default uploadImage; diff --git a/react-native/assets/index.html b/react-native/assets/index.html index 234c065..77ce9bb 100644 --- a/react-native/assets/index.html +++ b/react-native/assets/index.html @@ -8,20 +8,20 @@ /> Using OpenLayers with Vite