Sherlock / components / markerModalView.tsx
markerModalView.tsx
Raw
import React, { useEffect, useState } from "react";
import { View, StyleSheet, Dimensions, Image, ScrollView, Touchable, TouchableOpacity } from "react-native";
import Colors from "../constants/Colors";
import {Text} from "./Themed";
import { getMarkerByID } from "./backend";
import { getIncidentEmoji } from "../constants/emojis";
import {globalStyles} from "../constants/styles";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { getDist } from "./helpers";
import global from "../constants/global";
import { formatWithCommas } from "../app/(tabs)/(integrations)/integrations";

// Function to get marker data
// Taken from main map component
export function populateView(key:any, markerCache:any, sor:any){

    let formattedKey;
    let popMarker;

    if (!(typeof key === "number") && key.includes("$")) {
        formattedKey = key.split("$")[1];
    } else {
        formattedKey = key;
    }
    let source = formattedKey.charAt(0);
    formattedKey = formattedKey.substring(1)

    if(source){
        let t = ["C", "K", "U", "D", "S"];
        if(source=="S"){
            popMarker = sor.find((marker: any) => marker.attributes.Tid == formattedKey);
        }else{
            popMarker = markerCache.find((marker: any) => marker.id == formattedKey && marker.source == t.indexOf(source)+1);
        }
    }

    if (popMarker == undefined || markerCache.length == 0 && source!="S") {
        console.log("Marker ID: " +formattedKey +" not found in cache. Fetching from backend.");
        getMarkerByID(formattedKey, parseInt(source))
        .then((res) => {
            return res;
        })
        .catch((e) => {
            console.log(e);
        });
    } else {
        console.log("Marker ID: " + source+formattedKey + " found in cache.");
        return popMarker;
    }
}

// Defines props
type MarkerModalViewProps = {
    maxIndex: any;
    keyId:any;
    markerCache:any;
    sor:any;
}

// Main functional component
// Used as memo to avoid rerendering
const MarkerModalView = React.memo((props: MarkerModalViewProps) => {

    let marker=populateView(props.keyId, props.markerCache, props.sor);

    if(props.keyId.charAt(2)!="S" && props.keyId.charAt(0)!="S"){
        // console.log(props.keyId.charAt(2), props.keyId.charAt(0));
        let markerDate = new Date(parseInt(marker.date + "000"));
        let addy = marker.formatted_address.split(",");
        addy = addy[0] + ", " + addy[1] + "," + addy[2].substring(0, 3);
        let source = marker.source=="1"?"KPD CIP":marker.source=="2"?"Knox county sheriff":marker.source=="3"?"UTPD Clery act":marker.source=="4"?"KCSO Dangerous dog map":"TBI Sex offender registry";
        const userloc = global.userLocation as any;
        let distance = getDist({latitude: marker.latitude, longitude:marker.longitude}, userloc.coords);

        // Returns specialized layout for each source type
        if(marker.source=="1"){
            return(
                <View 
                    style={styles.container}
                >   
                    {/* Header containing incident and title */}
                    <View style={styles.header}>
                        <Text adjustsFontSizeToFit numberOfLines={2} minimumFontScale={0.7} style={[styles.incidentTitle, {color: marker.class==4?Colors.dark.redPastel:marker.class==5?Colors.dark.redPastel:Colors.dark.cyan}]}>{marker.incident}</Text>
                        <Text style={styles.code}><Text style={{fontWeight:'normal'}}>Radio code: </Text>{marker.code}</Text>
                    </View>

                    {/* Emoji display */}
                    <View style={[styles.emojiContainer, {backgroundColor:Colors.map['class'+marker.class as keyof typeof Colors.map]}]}>
                        <Text adjustsFontSizeToFit numberOfLines={1} style={styles.emoji}>{getIncidentEmoji(marker.code, marker.source.toString())}</Text>
                    </View>

                    <View style={[globalStyles.separator, {width:"50%", marginBottom:10,marginTop:0, alignSelf:"flex-start"}]}></View>

                    {/* Incident description */}
                    <View style={styles.descContainer}>
                        <Text style={styles.description}>๐Ÿ“: <Text style={{color: parseInt(distance)<2||distance.substring(distance.length-4)=="feet"?Colors.dark.redBright:Colors.dark.text}}>{formatWithCommas(distance)}</Text> away from you.</Text>
                        <Text style={styles.description}>๐Ÿ“: {addy}</Text>  
                        <Text style={styles.description}>๐Ÿ•“: {markerDate.toLocaleString("en-US", {month:"long", day:"numeric",  year:"numeric", hour:"numeric", minute:"numeric"})}</Text>
                        <Text style={[styles.description, {width:"100%"}]}>โž• ๐Ÿ›ฃ: {marker.cross_street}</Text>
                    </View>

                    <View style={[globalStyles.separator, {width:"50%", marginBottom:10,marginTop:10}]}></View>
                    {/* Likes and comments */}
                    <TouchableOpacity>
                        
                    </TouchableOpacity>


                    <View style={styles.footer}>
                        

                        <Text style={styles.footerText}>๐Ÿ‘ฎโ€โ™‚๏ธ: <Text style={{color:Colors.dark.cyan, fontWeight:'bold'}}>{source}</Text></Text>
                        <Text style={styles.footerText}>๐Ÿ†”: {(marker.source=="1"?"C":marker.source=="2"?"K":marker.source=="3"?"U":marker.source=="4"?"D":"S") +marker.id}</Text>
                        <Text style={[styles.footerText, {fontWeight:"bold", color:Colors.map["class"+marker.class  as keyof typeof Colors.map]}]}>โ—<Text>: </Text>{marker.class}</Text>
                    </View>

                </View>
            )
        }else if(marker.source =="2"){
            return(
                <View
                    style={styles.container}
                >

                    <View style={styles.header}>
                        <Text adjustsFontSizeToFit numberOfLines={2} minimumFontScale={0.7} style={[styles.incidentTitle, {color: marker.class==4?Colors.dark.redPastel:marker.class==5?Colors.dark.redPastel:Colors.dark.cyan}]}>{marker.incident}</Text>
                        <Text style={styles.code}><Text style={{fontWeight:'normal'}}>Zone: </Text>{marker.zone}</Text>
                    </View>

                    <View style={[globalStyles.separator, {width:"50%", marginBottom:10,marginTop:0}]}></View>


                    {/* Emoji display */}
                    <View style={[styles.emojiContainer, {backgroundColor:Colors.map['class'+marker.class as keyof typeof Colors.map]}]}>
                        <Text adjustsFontSizeToFit numberOfLines={1} style={styles.emoji}>{getIncidentEmoji(marker.incident, marker.source.toString())}</Text>
                    </View>

                    {/* Incident description */}
                    <View style={styles.descContainer}>
                        <Text style={styles.description}>๐Ÿ“: <Text style={{color: parseInt(distance)<2||distance.substring(distance.length-5)=="feet"?Colors.dark.redBright:Colors.dark.text}}>{formatWithCommas(distance)}</Text> away from you.</Text>
                        <Text style={styles.description}>๐Ÿ“: {addy}</Text>  
                        <Text style={styles.description}>๐Ÿ•“: {markerDate.toLocaleString("en-US", {month:"long", day:"numeric",  year:"numeric", hour:"numeric", minute:"numeric"})}</Text>
                        <Text style={[styles.description, {width:"100%"}]}>โž• ๐Ÿ›ฃ: {marker.cross_street1}, {marker.cross_street2}</Text>

                    </View>

                    <View style={[globalStyles.separator, {width:"50%", marginBottom:10,marginTop:10}]}></View>


                    <View style={styles.footer}>
                        <Text style={styles.footerText}>๐Ÿ‘ฎโ€โ™‚๏ธ: <Text style={{color:Colors.dark.cyan, fontWeight:'bold'}}>{source}</Text></Text>
                        <Text style={styles.footerText}>๐Ÿ†”: {(marker.source=="1"?"C":marker.source=="2"?"K":marker.source=="3"?"U":marker.source=="4"?"D":"S") +marker.id}</Text>
                        <Text style={[styles.footerText, {fontWeight:"bold", color:Colors.map["class"+marker.class  as keyof typeof Colors.map]}]}>โ—<Text>: </Text>{marker.class}</Text>
                    </View>


                </View>
            )
        }
    }else{
        let data = marker.attributes;
        let imgSource = "https://sor.tbi.tn.gov/api/sorimage/"+data.Tid;
        let age = new Date().getFullYear() - parseInt(data.Dob.substring(6));
        
        const userloc = global.userLocation as any;
        let distance = getDist(userloc.coords, {latitude: marker.geometry.y, longitude: marker.geometry.x});
        return(
                <View
                    style={styles.container}
                >

                    <View style={styles.header}>
                        <Text adjustsFontSizeToFit numberOfLines={2} minimumFontScale={0.7} style={[styles.incidentTitle, {color: marker.class==4?Colors.dark.redPastel:marker.class==5?Colors.dark.redPastel:Colors.dark.cyan}]}>{data.FirstName + " " + data.MiddleName + " " + data.LastName}</Text>
                        <Text style={[styles.code, {color:Colors.dark.redText}]}><Text style={{fontWeight:'normal'}}>Status: </Text>{data.StatusCode=="A"?"Active":"Active Incapacitated"}</Text>
                        <Text style={styles.description}>Classification: <Text style={[styles.description, {color:data.Classification=="OFFENDER AGAINST CHILDREN"?Colors.dark.redText:'orange', fontWeight:'bold'}]}>{data.Classification}</Text></Text>
                    </View>

                    <View style={[globalStyles.separator, {width:"50%", marginBottom:10,marginTop:0, alignSelf:'flex-start'}]}></View>


                    {/* Emoji display */}
                    <View style={[styles.emojiContainer, {backgroundColor:Colors.dark.alertBackground}]}>
                        <Image style={styles.sorImage} src={imgSource} />
                    </View>

                    {/* Incident description */}
                    <ScrollView style={[styles.descContainer, {height:height*0.15}]}>
                        <Text style={styles.description}>๐Ÿ“: <Text style={{color: parseInt(distance)<2||distance.substring(distance.length-4)=="feet"?Colors.dark.redBright:Colors.dark.text}}>{formatWithCommas(distance)}</Text> away from you.</Text>
                        <Text style={[styles.description, {fontWeight:'bold', color:Colors.dark.cyan}]}>DOB: <Text style={{fontWeight:'normal', color:Colors.dark.text}}>{data.Dob}, {age} years old</Text></Text>
                        <Text style={styles.description}>๐Ÿ•“: {data.OffenseDate}</Text>
                        <Text style={styles.description}>๐Ÿ : {data.ResAddr1+" "+data.ResAddr2+", "+data.ResCity +", "+data.ResState}</Text>  
                        <Text style={styles.description}>๐Ÿ โŒš: {data.ResStartDate}</Text>
                        {data.Tca1!=""&&<Text style={styles.description}>๐Ÿ“: {data.Tca1}</Text>}
                        {data.Tca2!=""&&<Text style={styles.description}>๐Ÿ“: {data.Tca2}</Text>}
                        {data.Tca3!=""&&<Text style={styles.description}>๐Ÿ“: {data.Tca3}</Text>}
                        {data.Tca4!=""&&<Text style={styles.description}>๐Ÿ“: {data.Tca4}</Text>}
                        {data.Tca5!=""&&<Text style={styles.description}>๐Ÿ“: {data.Tca5}</Text>}
                        {data.Univ_Name!=""&&<Text style={styles.description}>๐Ÿซ: {data.Univ_Name}</Text>}
                        {data.Univ_Status!=""&&<Text style={styles.description}>๐ŸŽ“: {data.Univ_Status}</Text>}
                        {data.UnivStartDate!=""&&<Text style={styles.description}>๐ŸŽ“ Start: {data.UnivStartDate}</Text>}
                        {data.UNIV_CITY!="                              "&&<Text style={styles.description}>๐Ÿซ: {data.UNIV_CITY}</Text>}

                    </ScrollView>

                    <View style={[globalStyles.separator, {width:"50%", marginBottom:10,marginTop:10}]}></View>


                    <View style={[styles.footer]}>
                        <Text style={styles.footerText}>๐Ÿ‘ฎโ€โ™‚๏ธ: <Text style={{color:Colors.dark.cyan, fontWeight:'bold'}}>{data.Site}</Text></Text>
                        <Text style={styles.footerText}>๐Ÿ†”: {props.keyId.substring(2)}</Text>
                    </View>


                </View>
            )
    }
    
})

let width = Dimensions.get('window').width;
let height = Dimensions.get('window').height;

const styles = StyleSheet.create({
    container: {
        flex: 1,
        width: width*.9,
        backgroundColor: Colors.dark.background,
        borderRadius: 10,
        marginHorizontal:8,
        padding:10,
        overflow:'hidden',
    },
    header:{
        justifyContent: 'space-between',
        flexWrap: 'wrap',
    },
    incidentTitle:{
        fontSize: 17,
        fontWeight: 'bold',
        width: width*.9*0.8,
    },
    code:{
        fontSize: 15,
        color: Colors.dark.cyan,
        marginRight:5,
        fontWeight: 'bold',
    },
    emojiContainer:{
        justifyContent: 'center',
        alignItems: 'center',
        height: height*.3*.4,
        width: height*0.3*0.4,
        borderWidth:5,
        borderColor:Colors.dark.backgroundProfile,
        borderRadius: 10,
        alignSelf:"flex-end",
        position:'absolute',
        right:20,
        top:40,
    },
    emoji:{
        fontSize: 40,
    },
    description:{
        flexWrap: 'wrap',
        fontSize: 15,
        width: width*.9-height*0.3*0.3-60,
        marginBottom: 10,
    },
    footer:{
        flex:1,
        flexDirection: 'row',
        justifyContent:'center',
        alignSelf: 'center',
        columnGap: 10,
        width: width*.9-height*0.3*0.3-60,
    },
    footerText:{
        fontSize: 15,
    },
    descContainer:{
        flex:1
    },
    sorImage:{
        resizeMode: 'cover',
        width: height*0.3*0.39,
        height: height*0.3*0.39,
        borderRadius: 10,
    },
})

export default MarkerModalView;