Переглянути джерело

new landmark tagging system + expiry dates

aidan 1 рік тому
батько
коміт
2388b21a23

+ 1 - 0
android/app/build.gradle

@@ -195,6 +195,7 @@ android {
 }
 
 dependencies {
+    compile project(':react-native-config')
     implementation fileTree(dir: "libs", include: ["*.jar"])
     //noinspection GradleDynamicVersion
     implementation "com.facebook.react:react-native:+"  // From node_modules

+ 1 - 1
android/app/src/main/java/com/clicknpush/mobile/MainApplication.java

@@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
 import com.facebook.react.PackageList;
 import com.facebook.react.ReactApplication;
 import fr.greweb.reactnativeviewshot.RNViewShotPackage;
-import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
+// import com.lugg.ReactNativeConfig.ReactNativeConfigPackage;
 import com.facebook.react.ReactInstanceManager;
 import com.facebook.react.ReactNativeHost;
 import com.facebook.react.ReactPackage;

+ 8 - 0
package-lock.json

@@ -14764,6 +14764,14 @@
       "resolved": "https://registry.npmjs.org/react-native-config/-/react-native-config-1.5.0.tgz",
       "integrity": "sha512-slecooA/0tCwhb+RuWEbwLqtKirGh9vWPRpgDfH7uPAraCciqHNH2XjS9ylW+Spn4FUrHg5KWTqUGs9BdBADHg=="
     },
+    "react-native-date-picker": {
+      "version": "4.2.9",
+      "resolved": "https://registry.npmjs.org/react-native-date-picker/-/react-native-date-picker-4.2.9.tgz",
+      "integrity": "sha512-msx877/ULxaoceqOV3GKx/A93ninYmOp46w5DOCh3pQGPbgL4ZmbglvXJ3mOp2h/j5scuYf9+NetCRLqj1pODA==",
+      "requires": {
+        "prop-types": "^15.8.1"
+      }
+    },
     "react-native-device-info": {
       "version": "8.7.1",
       "resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.7.1.tgz",

+ 1 - 0
package.json

@@ -76,6 +76,7 @@
     "react-native-animated-spinkit": "^1.5.2",
     "react-native-collapsible": "^1.6.0",
     "react-native-config": "^1.4.5",
+    "react-native-date-picker": "^4.2.9",
     "react-native-device-info": "^8.6.0",
     "react-native-dialog": "^9.2.0",
     "react-native-dotenv": "^3.3.0",

+ 53 - 8
src/components/Map/Panels/AddLandmarkPanel.tsx

@@ -8,7 +8,7 @@
 import { FontAwesome } from "@expo/vector-icons";
 import { ImageInfo } from "expo-image-picker/build/ImagePicker.types";
 import React, { memo, useEffect, useState, useRef } from "react";
-import { ActivityIndicator, Dimensions, Image, Platform, SafeAreaView, Text, TextInput, TouchableOpacity, View, ImageSourcePropType, Share, KeyboardEventName, Keyboard, StyleSheet, KeyboardAvoidingView, Alert } from 'react-native';
+import { ActivityIndicator, Dimensions, Image, Platform, SafeAreaView, Text, TextInput, TouchableOpacity, View, ImageSourcePropType, Share, KeyboardEventName, Keyboard, StyleSheet, KeyboardAvoidingView, Alert, Button } from 'react-native';
 import { ScrollView } from "react-native-gesture-handler";
 import Modal from 'react-native-modal';
 import Picker from 'react-native-picker-select';
@@ -29,6 +29,10 @@ import LandmarkTypePicker from "../../LandmarkTypePicker";
 import { values } from "mobx";
 import { Icon } from "react-native-paper/lib/typescript/components/Avatar/Avatar";
 
+import DatePicker from 'react-native-date-picker'
+import CheckBox from "@react-native-community/checkbox";
+import { propertiesContainsFilter } from "@turf/turf";
+
 
 /**
  * Props for the {@link AddLandmarkPanel} component.
@@ -62,6 +66,9 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
     const [photos, setPhotos] = useState<LMPhoto[]>([])
     const [photoSourceMenuOpened, togglePhotoSourceMenu] = useState<boolean>(false)
     const [keyboardOpened, setKeyboardOpened] = useState<boolean>(false);
+    const [date, setDate] = useState(new Date())
+    const [showExpire, toggleShowExpire] = useState<boolean>(false)
+    var minDate = new Date();
 
     const addLandmarkMutation = useAddLandmark()
 
@@ -80,13 +87,13 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
             const validEventString: KeyboardEventName = eventString as KeyboardEventName;
 
             const keyboardDidShowListener = Keyboard.addListener(
-                validEventString,
+                "keyboardDidShow",
                 () => {
                     setKeyboardOpened(true); // or some other action
                 }
             );
             const keyboardDidHideListener = Keyboard.addListener(
-                validEventString,
+                "keyboardDidHide",
                 () => {
                     setKeyboardOpened(false); // or some other action
                 }
@@ -103,10 +110,9 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
      * Returns a height for the modal depending on if an image is maximzed, if the keyboard is opened, and if the current landmark has photos associated with it
      */
     const determineModalHeight = () => {
-        if (keyboardOpened) {
+        if (keyboardOpened)
             return Dimensions.get("window").height * .45
-        }
-        else if (photos?.length > 0)
+        else if (photos?.length > 0 || showExpire)
             return Dimensions.get("window").height * .9
         else
             return Dimensions.get("window").height * .6
@@ -140,6 +146,22 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
         addLandmarkMutation.reset()
     }, [visible]);
 
+    useEffect(() => {
+        if (!showExpire && newLandmark) {
+            newLandmark.expiry_date = null
+        }
+        else if (showExpire && newLandmark) {
+            newLandmark.expiry_date = date
+        }
+    }, [showExpire])
+    
+    useEffect(() => {
+        if (newLandmark) {
+            console.log(date)
+            newLandmark.expiry_date = date
+        }
+    }, [date])
+
     /**
      * Calls {@link addLandmarkAsync} from {@link useLandmarks} to initate the process of adding a landmark, then closes the modal.
      */
@@ -184,6 +206,8 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
         setVisible(false)
         togglePhotoSourceMenu(false)
         setNewLandmark({})
+        toggleShowExpire(false)
+        setDate(new Date())
     }
 
     const addPhoto = (result: ImageInfo) => {
@@ -267,10 +291,31 @@ const AddLandmarkPanel: React.FC<AddLandmarkProps> = ({ newLandmark, setNewLandm
                                                 value: icon, key: lmTypes[parseInt(icon)]?.cat.toUpperCase() }
                                             )
                                     })}/>
-
+                                    <View style={{ paddingHorizontal: 15, paddingVertical: 10, flex: 1, flexDirection: "row"}}>
+                                        <Text style={{color: "white", margin: 0, fontSize: 16}}>Add expiry date?</Text>
+                                        <CheckBox
+                                            value={showExpire}
+                                            boxType="square"
+                                            onValueChange={() => toggleShowExpire(!showExpire)}
+                                            tintColors={{true: "white", false: "white"}}
+                                            tintColor="white"
+                                            onCheckColor="#00000000"
+                                            onFillColor="white"
+                                            onTintColor="white"
+                                            style={{borderColor: "white", marginLeft: 10, width: 20, height: 20}}/>
+                                    </View>
+                                    <View style={{flex: 1, alignSelf: "stretch"}}>
+                                        {showExpire && !keyboardOpened && <DatePicker
+                                            mode="date"
+                                            date={date}
+                                            onDateChange={setDate}
+                                            fadeToColor="none"
+                                            timeZoneOffsetInMinutes={(-1) * (new Date()).getTimezoneOffset()}
+                                            minimumDate={minDate} />}
+                                    </View>
                                 </View>
                                 {newLandmark?.landmark_type ?
-                                    <View style={{ justifyContent: 'flex-end', flexDirection: 'row', paddingHorizontal: 20, marginTop: 5 }}>
+                                    <View style={{ justifyContent: 'flex-end', flexDirection: 'row', paddingHorizontal: 20, paddingBottom: 10, marginTop: 5 }}>
                                         {newLandmark.description && newLandmark.title ?
                                             <View style={{ flexDirection: 'row' }}>
                                                 <TouchableOpacity onPress={async () => await submit()}><Text style={{ color: 'white', marginRight: 25 }}>Add</Text></TouchableOpacity>

+ 65 - 5
src/components/Map/Panels/LandmarkDetailsPanel/DetailsBody.tsx

@@ -23,6 +23,9 @@ import { Separator } from "../../../Separator";
 import { CommentsContainer } from "./CommentsContainer";
 import { LandmarkPhotos } from "./LandmarkPhotos";
 import { useOutdoorMapState } from "../../MainMapComponent/useMapState";
+import { TagContainer } from "./TagContainer";
+import CheckBox from "@react-native-community/checkbox";
+import DatePicker from "react-native-date-picker";
 
 
 
@@ -57,6 +60,8 @@ interface DetailsBodyProps {
     setProcessingPhoto: (state: boolean) => void
     authNavigation: MainTabsNavigationProp
     uri: String
+    tagLandmark: (tagType: string) => void
+    taggedByUser?: Array<boolean>
 }
 
 /**
@@ -86,12 +91,38 @@ export const DetailsBody: React.FC<DetailsBodyProps> = (props) => {
         if (props.editingEnabled) {
             console.log("[LandmarkDetails]: Editing is enabled")
             props.setUpdatedLandmark(props.landmark)
+            console.error(props.landmark?.expiry_date)
+            if (props.landmark?.expiry_date !== null) {
+                toggleShowExpire(true)
+                var parts = (props.landmark?.expiry_date?.toString().substring(0,10)).split('-')
+                setDate(new Date(+parts[0], +parts[1] - 1, +parts[2]))
+            }
+            else {
+                toggleShowExpire(false)
+            }
         }
         else {
             props.setUpdatedLandmark(undefined)
         }
     }, [props.editingEnabled])
     
+    const [date, setDate] = useState(new Date())
+    const [showExpire, toggleShowExpire] = useState<boolean>(false)
+
+    useEffect(() => {
+        if (props.editingEnabled && !showExpire)
+            props.setUpdatedLandmark({...props.updatedLandmark, expiry_date: null})
+        else if (props.editingEnabled && showExpire)
+            props.setUpdatedLandmark({...props.updatedLandmark, expiry_date: date})
+    }, [showExpire])
+    
+    useEffect(() => {
+        if (showExpire)
+            props.setUpdatedLandmark({...props.updatedLandmark, expiry_date: date})
+    }, [date])
+
+    const [editLocation, toggleEditLocation] = useState<boolean>(false)
+    
     /**
      * Sub-component that renders picker for landmark types
      * @param 
@@ -151,6 +182,8 @@ export const DetailsBody: React.FC<DetailsBodyProps> = (props) => {
                     <Text style={{color: 'white', marginBottom: 10, fontSize: 15}}>{lmTypes[props.landmark?.landmark_type]?.label.toUpperCase()}</Text>
                     <ScrollView nestedScrollEnabled={true}>
                         <Text style={{color: 'white', fontSize: 13}}>{props.landmark?.description}</Text>
+                        {props.landmark?.expiry_date ?
+                        <Text style={{color: 'white', fontSize: 13}}>Landmark expires on {props.landmark.expiry_date.toString().substring(0,10)}</Text> : null}
                     </ScrollView>
                 </View>
                 {props.landmark?.landmark_type ? <Image source={lmTypes[props.landmark?.landmark_type]?.image} /> : null}
@@ -162,8 +195,8 @@ export const DetailsBody: React.FC<DetailsBodyProps> = (props) => {
         <ScrollView ref={mainScrollRef} nestedScrollEnabled={true} contentContainerStyle={{justifyContent: 'space-between'}} style={{flex: 1, marginHorizontal: 20}}>
             {props.editingEnabled ?
             <>
-                <Text style={{color: 'white', marginBottom: 10}}>Location</Text>
-                <MapView
+                <Text onPress={() => toggleEditLocation(!editLocation)} style={{color: 'white', marginBottom: 10}}>Tap to edit location</Text>
+                {editLocation && <MapView
                     toolbarEnabled={false}
                     onPress={(e) => 
                         props.setUpdatedLandmark({...props.updatedLandmark, longitude: e.nativeEvent.coordinate.longitude, latitude: e.nativeEvent.coordinate.latitude})
@@ -179,9 +212,9 @@ export const DetailsBody: React.FC<DetailsBodyProps> = (props) => {
                         coordinate={{ latitude: props.updatedLandmark?.latitude as number, longitude: props.updatedLandmark?.longitude as number }} >
                         {props.updatedLandmark?.landmark_type ? <Image style={{ height: 35, width: 25 }} source={lmTypes[props.updatedLandmark?.landmark_type]?.image} /> : null}
                     </Marker>
-                </MapView>
+                </MapView>}
                 <LandmarkTypePickerContainer />
-                <Separator style={{marginBottom: 20, opacity: .5}} color="lightgray" />
+                <Separator style={{marginVertical: 10, opacity: .5}} color="lightgray" />
                 <Text style={{color: 'white', marginBottom: 10}}>Description</Text>
                 <ScrollView nestedScrollEnabled={true} style={{backgroundColor: 'white', marginBottom: 20}}>
                     <TextInput 
@@ -190,8 +223,35 @@ export const DetailsBody: React.FC<DetailsBodyProps> = (props) => {
                         onChangeText={text => props.setUpdatedLandmark({...props.updatedLandmark, description: text})} 
                         value={props.updatedLandmark?.description}/>
                 </ScrollView>
-
+                <View style={{ paddingHorizontal: 15, paddingVertical: 10, flex: 1, flexDirection: "row"}}>
+                    <Text style={{color: "white", margin: 0, fontSize: 16}}>Add expiry date?</Text>
+                    <CheckBox
+                        value={showExpire}
+                        boxType="square"
+                        onValueChange={() => toggleShowExpire(!showExpire)}
+                        tintColors={{true: "white", false: "white"}}
+                        tintColor="white"
+                        onCheckColor="#00000000"
+                        onFillColor="white"
+                        onTintColor="white"
+                        style={{borderColor: "white", marginLeft: 10, width: 20, height: 20}}
+                    />
+                </View>
+                <View style={{flex: 1, alignSelf: "stretch"}}>
+                    {showExpire && <DatePicker
+                        mode="date"
+                        date={date}
+                        onDateChange={setDate}
+                        fadeToColor="none" />}
+                </View>
             </>: <EditingDisabledUpperView />}
+            {!props.editingEnabled ? 
+            <TagContainer
+                landmark={props.landmark}
+                tagLandmark={props.tagLandmark}
+                toggleLmDetails={props.toggleLmDetails}
+                authNavigation={props.authNavigation}
+                taggedByUser={props.taggedByUser} /> : null}
             {!props.editingEnabled ?
             <CommentsContainer
                 toggleLmDetails={props.toggleLmDetails}

+ 14 - 4
src/components/Map/Panels/LandmarkDetailsPanel/LandmarkDetails.tsx

@@ -10,7 +10,7 @@ import React, { memo, useEffect, useRef, useState } from "react";
 import { ScrollView, ActivityIndicator, Alert, Dimensions, FlatList, Image, Keyboard, Platform, SafeAreaView, StyleSheet, Text, TextInput, TouchableOpacity, View } from "react-native";
 import Modal from 'react-native-modal';
 import { LMComment, useAddComment, useDeleteComment, useEditComment, useLandmarkComments } from "../../../../data/comments";
-import { Landmark, useAddLandmarkPhoto, useDeleteLandmark, useDeleteLandmarkPhoto, useEditLandmark, useLandmark, useRateLandmark } from "../../../../data/landmarks";
+import { Landmark, useAddLandmarkPhoto, useDeleteLandmark, useDeleteLandmarkPhoto, useEditLandmark, useLandmark, useRateLandmark, useTagLandmark } from "../../../../data/landmarks";
 import { useAuth } from "../../../../data/Auth/AuthContext";
 import { colors, lmTypes } from "../../../../utils/GlobalUtils";
 import { IconButton, PrimaryButton } from "../../../Buttons";
@@ -99,6 +99,7 @@ const LandmarkDetails: React.FC<LandmarkDetailsProps> = ({markerWindowPosition,
     const landmarkQuery = useLandmark(landmarkId)
     const editLandmarkMutation = useEditLandmark()
     const rateLandmarkMutation = useRateLandmark()
+    const tagLandmarkMutation = useTagLandmark()
     const deleteLandmarkMutation = useDeleteLandmark()
     const addLandmarkPhotoMutation = useAddLandmarkPhoto()
     const deleteLandmarkPhotoMutation = useDeleteLandmarkPhoto()
@@ -237,6 +238,13 @@ const LandmarkDetails: React.FC<LandmarkDetailsProps> = ({markerWindowPosition,
         }
     }
 
+    /**
+     * Call the {@linkcode tagLandmarkAsync} mutation from the {@link useLandmarks} hook. Increases tag count if user has not already used this tag, else decrement
+     */
+    const tagLandmark = async (tagType: string) => {
+        await tagLandmarkMutation.mutateAsync({id: landmarkId, tagType: tagType})
+    }
+
     /**
      * Calls the {@linkcode deleteLandmark} mutation from the {@link useLandmarks} hook and closes the modal once finished.
      */
@@ -335,12 +343,12 @@ const LandmarkDetails: React.FC<LandmarkDetailsProps> = ({markerWindowPosition,
         if (selectedImage > -1) 
             return Dimensions.get("window").height 
         else if (keyboardOpened || editingEnabled || (!landmarkOwnedByUser(landmarkQuery?.data?.landmark) && landmarkQuery?.data?.landmark?.photos?.length == 0)) {
-            return Dimensions.get("window").height * .8
+            return Dimensions.get("window").height * .9
         }
         else if (landmarkQuery?.data?.landmark?.photos?.length > 0) 
             return Dimensions.get("window").height * .9 
         else
-            return Dimensions.get("window").height * .6
+            return Dimensions.get("window").height * .7
     }
 
     return (
@@ -427,7 +435,9 @@ const LandmarkDetails: React.FC<LandmarkDetailsProps> = ({markerWindowPosition,
                     toggleLmDetails={toggleLmDetails}
                     addPhoto={addLandmarkPhotoMutation.mutateAsync}
                     addPhotoStatus={addLandmarkPhotoMutation.status}
-                    uri={uri}/>
+                    uri={uri}
+                    tagLandmark={tagLandmark}
+                    taggedByUser={landmarkQuery?.data?.taggedByUser}/>
                 </> :
                 <View style={{height: '100%', justifyContent: "space-evenly", alignItems: "center", marginHorizontal: 20}}>
                     <Text style={{color: 'white', fontSize: 20}}>{

+ 133 - 0
src/components/Map/Panels/LandmarkDetailsPanel/TagContainer.tsx

@@ -0,0 +1,133 @@
+/* Copyright (C) Click & Push Accessibility, Inc - All Rights Reserved
+ * Unauthorized copying of this file, via any medium is strictly prohibited
+ * Proprietary and confidential
+ * Written and maintained by the Click & Push Development team 
+ * <dev@clicknpush.ca>, January 2022
+ */
+
+import React, { } from "react";
+import { StyleSheet, View, Text } from "react-native";
+import { Chip } from "react-native-paper";
+import { useAuth } from "../../../../data/Auth/AuthContext";
+import { Landmark } from "../../../../data/landmarks";
+import { MainTabsNavigationProp } from "../../../../navigation/MainTabsNavigator";
+
+
+interface TagContainerProps {
+    landmark?: Landmark
+    tagLandmark: (tagType: string) => void
+    toggleLmDetails: (state: boolean) => void
+    authNavigation: MainTabsNavigationProp
+    taggedByUser?: Array<boolean>
+}
+
+export const TagContainer: React.FC<TagContainerProps> = (props) => {
+    const { anonUserId, setAlert } = useAuth()
+    return (
+        <View style={styles.tagContainer}>
+            <View style={styles.tagRow}>
+                <Chip selected={props.taggedByUser?.[0]} mode="flat" icon="check" style={styles.tag} onPress={() => {
+                    if (anonUserId) {
+                        setAlert({
+                            title: "Authentication required",
+                            type: "warning",
+                            message: "Oops! In order to tag landmarks, you have to log in.",
+                            callback: () => { props.toggleLmDetails(false); props.authNavigation.navigate("Account") },
+                            callbackButtonText: 'Go to login'
+                        });
+                    }
+                    else {
+                        props.tagLandmark("positive")
+                    }
+                }}>
+                    Positive | {props.landmark?.tag_positive}
+                </Chip>
+                <Chip selected={props?.taggedByUser?.[1]} mode="flat" icon="close" style={styles.tag} onPress={() => {
+                    if (anonUserId) {
+                        setAlert({
+                            title: "Authentication required",
+                            type: "warning",
+                            message: "Oops! In order to tag landmarks, you have to log in.",
+                            callback: () => { props.toggleLmDetails(false); props.authNavigation.navigate("Account") },
+                            callbackButtonText: 'Go to login'
+                        });
+                    }
+                    else {
+                        props.tagLandmark("negative")
+                    }
+                }}>
+                    Negative | {props.landmark?.tag_negative}
+                </Chip>
+                <Chip selected={props?.taggedByUser?.[2]} mode="flat" icon="calendar-remove" style={styles.tag} onPress={() => {
+                    if (anonUserId) {
+                        setAlert({
+                            title: "Authentication required",
+                            type: "warning",
+                            message: "Oops! In order to tag landmarks, you have to log in.",
+                            callback: () => { props.toggleLmDetails(false); props.authNavigation.navigate("Account") },
+                            callbackButtonText: 'Go to login'
+                        });
+                    }
+                    else {
+                        props.tagLandmark("stale")
+                    }
+                }}>
+                    Stale | {props.landmark?.tag_stale}
+                </Chip>
+            </View>
+            <View style={styles.tagRow}>
+                <Chip selected={props?.taggedByUser?.[3]} mode="flat" icon="content-copy" style={styles.tag} onPress={() => {
+                    if (anonUserId) {
+                        setAlert({
+                            title: "Authentication required",
+                            type: "warning",
+                            message: "Oops! In order to tag landmarks, you have to log in.",
+                            callback: () => { props.toggleLmDetails(false); props.authNavigation.navigate("Account") },
+                            callbackButtonText: 'Go to login'
+                        });
+                    }
+                    else {
+                        props.tagLandmark("duplicate")
+                    }
+                }}>
+                    Duplicate | {props.landmark?.tag_duplicate}
+                </Chip>
+                <Chip selected={props?.taggedByUser?.[4]} mode="flat" icon="map-marker" style={styles.tag} onPress={() => {
+                    if (anonUserId) {
+                        setAlert({
+                            title: "Authentication required",
+                            type: "warning",
+                            message: "Oops! In order to tag landmarks, you have to log in.",
+                            callback: () => { props.toggleLmDetails(false); props.authNavigation.navigate("Account") },
+                            callbackButtonText: 'Go to login'
+                        });
+                    }
+                    else {
+                        props.tagLandmark("imprecise")
+                    }
+                }}>
+                    Imprecise Location | {props.landmark?.tag_imprecise}
+                </Chip>
+            </View>
+        </View>
+    )
+}
+
+const styles = StyleSheet.create({
+    tagContainer: {
+        marginBottom: 20,
+    },
+    tagRow: {
+        flex: 1,
+        flexDirection: "row",
+        marginBottom: 5,
+        justifyContent: "flex-start",
+    },
+    tag: {
+        margin: 2,
+        // backgroundColor: "#ADADAD",
+    },
+    tagText: {
+        color: "#FF0000",
+    }
+})

+ 42 - 2
src/data/landmarks.ts

@@ -42,7 +42,19 @@ export interface Landmark {
     // DONE! 
     floor?: number | null,
     anonymous?: string,
-    voice?: boolean
+    voice?: boolean,
+    /*** The "Positive" tag rating of the landmark. */
+    tag_positive?: number | null,
+    /*** The "Negative" tag rating of the landmark. */
+    tag_negative?: number | null,
+    /*** The "Stale" tag rating of the landmark. */
+    tag_stale?: number | null,
+    /*** The "Duplicate" tag rating of the landmark. */
+    tag_duplicate?: number | null,
+    /*** The "Imprecise Location" tag rating of the landmark. */
+    tag_imprecise?: number | null,
+    /*** A Date object representing an optional expiry date that will automatically cull the landmark */
+    expiry_date?: Date | null,
 }
 
 export interface LMPhoto {
@@ -111,7 +123,7 @@ export const useLandmark = (landmarkId: string) => {
         }
     }
 
-    return useQuery<{landmark: Landmark, ratedByUser: boolean}, Error>([queryKeys.getLandmark, landmarkId], () => getLandmark(landmarkId), {
+    return useQuery<{landmark: Landmark, ratedByUser: boolean, taggedByUser: Array<boolean>}, Error>([queryKeys.getLandmark, landmarkId], () => getLandmark(landmarkId), {
         placeholderData: () => queryClient.getQueryData(queryKeys.getLandmark),
         refetchOnReconnect: true,
         refetchOnMount: false
@@ -227,6 +239,34 @@ export const useRateLandmark = () => {
     })    
 }
 
+export const useTagLandmark = () => {
+    const {sendApiRequestAsync} = useAuth()
+    const queryClient = useQueryClient();
+
+    const tagLandmark =  async (data: {id: string, tagType: string}) => {
+        if (data) {
+            const response = await sendApiRequestAsync({
+                axiosConfig:{
+                    method: 'POST',
+                    data: data,
+                    url: `/api/landmark/tag/`,
+                },
+                authorized: true,
+                errorMessage: 'Something went wrong when tagging a landmark',
+                loggingCategory: 'LANDMARKS'
+            });   
+            return response?.data;
+        }
+    }
+
+    return useMutation(tagLandmark, {
+        onSuccess: () => { 
+            queryClient.invalidateQueries(queryKeys.getLandmark)
+        },  
+        onError: () => queryClient.invalidateQueries(queryKeys.getLandmark),  
+    })    
+}
+
 export const useReportLandmark = () => {
     const {sendApiRequestAsync} = useAuth()
     const queryClient = useQueryClient();

+ 2 - 2
src/utils/RequestUtils.ts

@@ -11,11 +11,11 @@
 //export const API_URL = 'http://192.168.3.81:8000'
 // export const API_URL = 'https://staging.clicknpush.ca'
 
-export const API_URL = 'https://app.clicknpush.ca'
+// export const API_URL = 'https://app.clicknpush.ca'
 // export const API_URL = 'http://192.168.1.106:8000' // Nathan
 //export const API_URL = 'http://192.168.1.64:8000'   // Chase
 //export const API_URL = 'http://192.168.0.22:8000'       // Eric
 // export const API_URL = 'http://192.168.1.131:8000'  // Aidan surface
-// export const API_URL = 'http://192.168.1.99:8000'  // Aidan home
+export const API_URL = 'http://192.168.1.99:8000'  // Aidan home
 
 // export const API_URL = Config.API_URL