some iOS bug fixes and UI edits
This commit is contained in:
parent
cd7934b59a
commit
721603cff3
14 changed files with 2391 additions and 1111 deletions
|
|
@ -57,9 +57,11 @@ def fetchItem(store):
|
||||||
'lowername': request.args['itemName'].lower(),
|
'lowername': request.args['itemName'].lower(),
|
||||||
'store': store['key'],
|
'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):
|
while (fetch.last is not None):
|
||||||
fetch = itemsDB.fetch({
|
fetch = itemsDB.fetch({
|
||||||
'lowername': request.args['itemName'].lower(),
|
'lowername': request.args['itemName'].lower(),
|
||||||
|
|
@ -69,8 +71,6 @@ def fetchItem(store):
|
||||||
|
|
||||||
if fetch.items:
|
if fetch.items:
|
||||||
item = fetch.items[0]
|
item = fetch.items[0]
|
||||||
break
|
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def fetchItems(store):
|
def fetchItems(store):
|
||||||
|
|
|
||||||
|
|
@ -1,92 +1,46 @@
|
||||||
import { useState, useRef, useEffect } from "react";
|
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 { 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 { useAssets } from "expo-asset";
|
||||||
import * as Location from "expo-location";
|
import * as Location from "expo-location";
|
||||||
import { useNavigation, router } from "expo-router";
|
import { useNavigation } from "expo-router";
|
||||||
|
|
||||||
|
import messageHandler from "./messageHandler";
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
const [webViewKey, setWebViewKey] = useState(Math.random() * 10);
|
||||||
const [assets] = useAssets([require("../assets/index.html")]);
|
const [assets] = useAssets([require("../assets/index.html")]);
|
||||||
const [htmlString, setHtmlString] = useState<string>();
|
const [htmlString, setHtmlString] = useState<string>();
|
||||||
const dimensions = useWindowDimensions();
|
const dimensions = useWindowDimensions();
|
||||||
const webViewRef = useRef<WebView | null>();
|
const webViewRef = useRef<WebView | null>();
|
||||||
const navigation = useNavigation();
|
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 getLocation = async () => {
|
||||||
const { status } = await Location.requestForegroundPermissionsAsync();
|
const { status } = await Location.requestForegroundPermissionsAsync();
|
||||||
if (status !== "granted") return;
|
if (status !== "granted") return;
|
||||||
|
|
||||||
const location = await Location.getCurrentPositionAsync({
|
const location = await Location.getCurrentPositionAsync({
|
||||||
distanceInterval: 0, // for IOS
|
distanceInterval: 0,
|
||||||
accuracy: Location.Accuracy.High,
|
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()`;
|
webViewRef.current?.injectJavaScript(
|
||||||
// passes string to webview - there it is handled by OpenLayers to change the current location
|
`setTimeout(() => {window.passLocation(${location.coords.longitude}, ${location.coords.latitude})}, 50); true`
|
||||||
webViewRef.current?.injectJavaScript(str);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// refresh on navigating back to this page
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// remove header
|
|
||||||
navigation.setOptions({ headerShown: false });
|
navigation.setOptions({ headerShown: false });
|
||||||
const focusHandler = navigation.addListener("focus", () => {
|
|
||||||
webViewRef.current?.reload();
|
return navigation.addListener("focus", () => {
|
||||||
|
setWebViewKey(Math.random() * 10);
|
||||||
getLocation();
|
getLocation();
|
||||||
});
|
});
|
||||||
return focusHandler;
|
|
||||||
}, [navigation]);
|
}, [navigation]);
|
||||||
|
|
||||||
// loads assets
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (assets) {
|
if (assets) {
|
||||||
fetch(assets[0].localUri || "")
|
fetch(assets[0].localUri || "")
|
||||||
|
|
@ -95,29 +49,23 @@ export default function () {
|
||||||
}
|
}
|
||||||
}, [assets]);
|
}, [assets]);
|
||||||
|
|
||||||
// exits if no map passed in
|
if (!htmlString) return <></>;
|
||||||
if (!htmlString) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<WebView
|
<WebView
|
||||||
ref={(currentRef) => (webViewRef.current = currentRef)}
|
ref={webViewRef}
|
||||||
injectedJavascript=""
|
injectedJavascript=""
|
||||||
source={{
|
source={{
|
||||||
html: htmlString,
|
html: htmlString,
|
||||||
}}
|
}}
|
||||||
javaScriptEnabled
|
|
||||||
style={{
|
style={{
|
||||||
width: dimensions.width,
|
width: dimensions.width,
|
||||||
height: dimensions.height,
|
height: dimensions.height,
|
||||||
|
|
@ -130,8 +78,13 @@ export default function () {
|
||||||
containerStyle={{
|
containerStyle={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
onMessage={messageHandler}
|
onMessage={(event) => {
|
||||||
|
messageHandler(event, webViewRef.current);
|
||||||
|
}}
|
||||||
webviewDebuggingEnabled={true}
|
webviewDebuggingEnabled={true}
|
||||||
|
javaScriptEnabled={true}
|
||||||
|
domStorageEnabled={true}
|
||||||
|
key={webViewKey}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ export default function () {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 500:
|
case 500:
|
||||||
|
console.log(res);
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Error!",
|
"Error!",
|
||||||
"Backend server error. Please report to ak95@riseup.net"
|
"Backend server error. Please report to ak95@riseup.net"
|
||||||
|
|
@ -108,6 +109,7 @@ export default function () {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
paddingBottom: "5%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|
|
||||||
58
react-native/app/messageHandler.ts
Normal file
58
react-native/app/messageHandler.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Alert } from "react-native";
|
||||||
|
import { WebViewMessageEvent } from "react-native-webview";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
import WebView from "react-native-webview";
|
||||||
|
|
||||||
|
const messageHandler = (event: WebViewMessageEvent, element: WebView) => {
|
||||||
|
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;
|
||||||
|
|
@ -8,22 +8,18 @@ import {
|
||||||
Alert,
|
Alert,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
|
||||||
import {
|
import { useNavigation, Stack, useLocalSearchParams } from "expo-router";
|
||||||
useNavigation,
|
|
||||||
Stack,
|
|
||||||
useLocalSearchParams,
|
|
||||||
router,
|
|
||||||
} from "expo-router";
|
|
||||||
import * as ImagePicker from "expo-image-picker";
|
|
||||||
import * as FileSystem from "expo-file-system";
|
import * as FileSystem from "expo-file-system";
|
||||||
|
|
||||||
import { styled } from "nativewind";
|
import { styled } from "nativewind";
|
||||||
|
|
||||||
import utmObj from "utm-latlng";
|
|
||||||
import mime from "mime";
|
|
||||||
|
|
||||||
import StoreItem from "../../components/StoreItem";
|
import StoreItem from "../../components/StoreItem";
|
||||||
|
|
||||||
|
import handleAddItem from "./handleAddItem";
|
||||||
|
import toUTM from "./toUTM";
|
||||||
|
import uploadImage from "./uploadImage";
|
||||||
|
|
||||||
declare function isNaN(x: string | number): boolean;
|
declare function isNaN(x: string | number): boolean;
|
||||||
|
|
||||||
interface storeItemInterface {
|
interface storeItemInterface {
|
||||||
|
|
@ -43,16 +39,14 @@ export default function () {
|
||||||
const [image, setImage] = useState(null);
|
const [image, setImage] = useState(null);
|
||||||
|
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const utm = new utmObj();
|
|
||||||
|
|
||||||
const StyledText = styled(Text);
|
const StyledText = styled(Text);
|
||||||
|
|
||||||
const coords = useLocalSearchParams().coords.toString().split("%2B");
|
const coords = useLocalSearchParams().coords.toString().split("%2B");
|
||||||
const lon = coords[0];
|
const lon = coords[0];
|
||||||
const lat = coords[1];
|
const lat = coords[1];
|
||||||
const utmCoords = utm.convertLatLngToUtm(lat, lon, 5);
|
const utmCoords = toUTM(lat, lon);
|
||||||
|
|
||||||
// get store information
|
|
||||||
const getStore = async () => {
|
const getStore = async () => {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${process.env.EXPO_PUBLIC_BACKEND_URL}/?` +
|
`${process.env.EXPO_PUBLIC_BACKEND_URL}/?` +
|
||||||
|
|
@ -71,6 +65,7 @@ export default function () {
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
setStoreName(json.name);
|
setStoreName(json.name);
|
||||||
setStoreKey(json.key.toString());
|
setStoreKey(json.key.toString());
|
||||||
|
const imageKey = json.key.toString();
|
||||||
|
|
||||||
const items = json.items.map((item: storeItemInterface, index: number) => {
|
const items = json.items.map((item: storeItemInterface, index: number) => {
|
||||||
const date = new Date(item.lastUpdated.replace(" ", "T") + "Z");
|
const date = new Date(item.lastUpdated.replace(" ", "T") + "Z");
|
||||||
|
|
@ -88,71 +83,20 @@ export default function () {
|
||||||
});
|
});
|
||||||
setStoreItems(items);
|
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(
|
await FileSystem.downloadAsync(
|
||||||
imageURL,
|
imageURL,
|
||||||
FileSystem.documentDirectory + storeKey
|
FileSystem.documentDirectory + imageKey
|
||||||
)
|
)
|
||||||
.then(({ uri }) => {
|
.then(({ uri }) => {
|
||||||
setImage(uri);
|
setImage(uri);
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
console.log(image);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getStore();
|
return navigation.addListener("focus", () => 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;
|
|
||||||
}, [navigation]);
|
}, [navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -162,6 +106,7 @@ export default function () {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
paddingBottom: "5%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|
@ -169,7 +114,13 @@ export default function () {
|
||||||
title: storeName,
|
title: storeName,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity onPress={uploadImage}>
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
uploadImage(storeKey).then((uri) => {
|
||||||
|
setImage(uri);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
{image ? (
|
{image ? (
|
||||||
<Image source={{ uri: image }} className="h-[33vh] w-[100vw]" />
|
<Image source={{ uri: image }} className="h-[33vh] w-[100vw]" />
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -187,7 +138,7 @@ export default function () {
|
||||||
<ScrollView style={{ flex: 1 }}>{storeItems}</ScrollView>
|
<ScrollView style={{ flex: 1 }}>{storeItems}</ScrollView>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
className="px-8 py-4 my-4 bg-green-700 rounded"
|
className="px-8 py-4 my-4 bg-green-700 rounded"
|
||||||
onPress={handleAddItem}
|
onPress={() => handleAddItem(storeKey)}
|
||||||
>
|
>
|
||||||
<StyledText className="text-white text-center text-2xl">
|
<StyledText className="text-white text-center text-2xl">
|
||||||
Add
|
Add
|
||||||
|
|
|
||||||
12
react-native/app/store/handleAddItem.ts
Normal file
12
react-native/app/store/handleAddItem.ts
Normal file
|
|
@ -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;
|
||||||
|
|
@ -10,11 +10,12 @@ import {
|
||||||
Alert,
|
Alert,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Stack, useLocalSearchParams, router } from "expo-router";
|
import { Stack, useLocalSearchParams, router } from "expo-router";
|
||||||
import * as ImagePicker from "expo-image-picker";
|
|
||||||
import utmObj from "utm-latlng";
|
import utmObj from "utm-latlng";
|
||||||
import { styled } from "nativewind";
|
import { styled } from "nativewind";
|
||||||
import mime from "mime";
|
import mime from "mime";
|
||||||
|
|
||||||
|
import uploadImage from "./uploadImage";
|
||||||
|
|
||||||
declare function isNaN(x: string | number): boolean;
|
declare function isNaN(x: string | number): boolean;
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
|
@ -27,20 +28,6 @@ export default function () {
|
||||||
|
|
||||||
let { coords } = useLocalSearchParams();
|
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 () => {
|
const handleSubmit = async () => {
|
||||||
coords = coords.split("%2B");
|
coords = coords.split("%2B");
|
||||||
const lon = coords[0];
|
const lon = coords[0];
|
||||||
|
|
@ -170,6 +157,7 @@ export default function () {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
paddingBottom: "5%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|
@ -177,7 +165,7 @@ export default function () {
|
||||||
title: "New Store",
|
title: "New Store",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity onPress={uploadImage}>
|
<TouchableOpacity onPress={() => setImage(uploadImage())}>
|
||||||
{image ? (
|
{image ? (
|
||||||
<Image source={{ uri: image.uri }} className="h-[33vh] w-[100vw]" />
|
<Image source={{ uri: image.uri }} className="h-[33vh] w-[100vw]" />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
17
react-native/app/store/new/uploadImage.ts
Normal file
17
react-native/app/store/new/uploadImage.ts
Normal file
|
|
@ -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;
|
||||||
8
react-native/app/store/toUTM.js
vendored
Normal file
8
react-native/app/store/toUTM.js
vendored
Normal file
|
|
@ -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;
|
||||||
41
react-native/app/store/uploadImage.ts
Normal file
41
react-native/app/store/uploadImage.ts
Normal file
|
|
@ -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;
|
||||||
File diff suppressed because one or more lines are too long
3097
react-native/package-lock.json
generated
3097
react-native/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -11,6 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@andordavoti/react-native-timeago": "^0.0.14",
|
"@andordavoti/react-native-timeago": "^0.0.14",
|
||||||
"@expo/vector-icons": "^13.0.0",
|
"@expo/vector-icons": "^13.0.0",
|
||||||
|
"@react-native-picker/picker": "2.4.10",
|
||||||
"@react-navigation/native": "^6.0.2",
|
"@react-navigation/native": "^6.0.2",
|
||||||
"expo": "~49.0.18",
|
"expo": "~49.0.18",
|
||||||
"expo-asset": "^8.10.1",
|
"expo-asset": "^8.10.1",
|
||||||
|
|
@ -30,20 +31,19 @@
|
||||||
"nativewind": "^2.0.11",
|
"nativewind": "^2.0.11",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-native": "0.72.6",
|
"react-native": "0.73.1",
|
||||||
"react-native-gesture-handler": "~2.12.0",
|
"react-native-gesture-handler": "~2.12.0",
|
||||||
"react-native-safe-area-context": "4.6.3",
|
"react-native-safe-area-context": "4.6.3",
|
||||||
"react-native-screens": "~3.22.0",
|
"react-native-screens": "~3.22.0",
|
||||||
"react-native-web": "~0.19.6",
|
"react-native-web": "~0.19.6",
|
||||||
"react-native-webview": "13.2.2",
|
"react-native-webview": "13.2.2",
|
||||||
"utm-latlng": "^1.0.7",
|
"utm-latlng": "^1.0.7"
|
||||||
"@react-native-picker/picker": "2.4.10"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
"@types/react": "~18.2.14",
|
"@types/react": "~18.2.14",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "3.3.2",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|
|
||||||
7
react-native/src/index.d.ts
vendored
Normal file
7
react-native/src/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
passLocation: Function;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue