Sherlock / app / (tabs) / (integrations) / integrations.tsx
integrations.tsx
Raw
import {Dimensions, Pressable, StyleSheet, ScrollView, BackHandler } from "react-native";

import { Text, View } from "../../../components/Themed";
import React, { useEffect, useRef } from "react";
import Constants from 'expo-constants';
import Webview from "../../../components/Webview";
import { FontAwesome, Foundation, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
import Colors from "../../../constants/Colors";
import { getStats } from "../../../components/backend";
import { useFocusEffect } from "expo-router";


export function formatWithCommas(n:any) { 
    if(n===undefined){return 0}
    return n.toString().replace(/\B(?=(\d{3})+\b)/g, ","); 
}

export default function Integrations() {
    const [showWeb, setShowWeb] = React.useState(false);
    const [webUrl, setWebUrl] = React.useState('');
    const [stats, setStats] = React.useState<any>({});
    const [fetchedDate, setFetchedDate] = React.useState("");

    useEffect(()=>{
        // fetches data on page load
        getStats().then((data)=>{
            let res:any={};

            for(let i=0;i<data.length;i++){
                res[data[i].for] = data[i].count;
            }
            setStats(res);
            setFetchedDate(new Date().toISOString());
        }).catch((error)=>{
            console.log("INTEGRATIONS: Error fetching stats: ", error);
        })
    },[])

    // When screen is focused
    useFocusEffect(
        React.useCallback(() => {
            let pastFetch = new Date(fetchedDate);
            // fetches data every 30 minutes
            // Returns if below the 30 minute threshold
            if(pastFetch.getTime()+(30*60000) >= Date.now() && !Number.isNaN(pastFetch.getTime())){
                // console.log("INTEGRATIONS: Data fetched less than 30 minutes ago, skipping fetch");
                return;
            }else if(pastFetch.getTime()+(30*60000)<Date.now()){
                // Fetches from backend
                getStats().then((data)=>{
                    let res:any={};

                    for(let i=0;i<data.length;i++){
                        res[data[i].for] = data[i].count;
                    }
                    setStats(res);
                    setFetchedDate(new Date().toISOString());
                }).catch((error)=>{
                    console.log("INTEGRATIONS: Error fetching stats: ", error);
                })
            }
        }, [fetchedDate])
    )

    // Handles back button press
    useEffect(()=>{
        const backHandler = BackHandler.addEventListener('hardwareBackPress', ()=>{
            if(showWeb){
                setShowWeb(false);
                return true;
            }
            return false;
            })

        return ()=>backHandler.remove();
    }, [showWeb])

    return (
        <View style={styles.container}>

            {/* Webview and back button */}
            {showWeb?<View style={styles.web}>
                <Pressable style={styles.backIcon}
                    onPress={()=>setShowWeb(false)}
                >
                    <Ionicons name="close" size={40} color={Colors.dark.redPastel} />
                </Pressable>

                <Webview url={webUrl} style={styles.web} />
            </View>:null}
            
            {/* Link buttons and statistics */}
            <ScrollView style={styles.content}>
                <View style={styles.iconContainer}>
                    <Pressable
                        onPress={()=>{
                            setWebUrl("https://sheriff.knoxcountytn.gov/index.php");
                            setShowWeb(true);
                        }}
                        style={styles.iconButton}
                    >
                        <MaterialCommunityIcons name="hours-24" style={styles.icons}/>
                        <Text style={styles.iconText}>24 Hour</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://sheriff.knoxcountytn.gov/inmate.php");
                            setShowWeb(true);
                        }}
                    >
                        <MaterialCommunityIcons name="handcuffs" style={styles.icons}/>
                        <Text style={styles.iconText}>Inmates</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://www.tn.gov/tbi/tbis-top-10-most-wanted.html");
                            setShowWeb(true);
                        }}
                    >
                        <MaterialIcons name="person-search" style={styles.icons}/>
                        <Text style={styles.iconText}>TBI Wanted</Text>

                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://knoxsheriff.org/safety-man/");
                            setShowWeb(true);
                        }}
                    >
                        <MaterialCommunityIcons name="safety-goggles" style={styles.icons}/>
                        <Text style={styles.iconText}>Safety Man</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://tnmap.tn.gov/sor/#");
                            setShowWeb(true);
                        }}
                    >
                        <FontAwesome name="intersex" style={styles.icons}/>
                        <Text style={styles.iconText}>SOR</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://knoxvilletnpolice.gov/");
                            setShowWeb(true);
                        }}
                    >
                        <MaterialCommunityIcons name="police-badge" style={styles.icons}/>
                        <Text style={styles.iconText}>KPD</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://knoxsheriff.org/");
                            setShowWeb(true);
                        }}
                    >
                        <MaterialCommunityIcons name="police-station" style={styles.icons}/>
                        <Text style={styles.iconText}>KCSO</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://crimeinsight.tbi.tn.gov/tops");
                            setShowWeb(true);
                        }}
                    >
                        <Foundation name="graph-pie" style={styles.icons}/>
                        <Text style={styles.iconText}>TBI Stats *Slow*</Text>
                    </Pressable>

                    <Pressable
                        style={styles.iconButton}
                        onPress={()=>{
                            setWebUrl("https://www.tn.gov/tbi/tennessees-missing-children.html");
                            setShowWeb(true);
                        }}
                    >
                        <MaterialCommunityIcons name="magnify" style={styles.icons}/>
                        <Text style={styles.iconText}>TN missing people</Text>
                    </Pressable>

                </View>
                <View style={styles.separator} darkColor="rgba(255,255,255,0.1)" />

                <Text style={styles.title}>Knox stats 📈</Text>
                <Text style={styles.lastFetchedText}>Last fetched: {new Date(fetchedDate).toLocaleString()}</Text>

                <Text style={styles.monthlyStatsTitle}>Monthly stats</Text>
                <View style={styles.statsContainer}>
                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.highStatNumber}>{formatWithCommas(stats.mClass5)}</Text> 
                        <Text style={styles.classText}> Class 5 reports</Text>
                    </View>
                
                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.highStatNumber}>{formatWithCommas(stats.mClass4)}</Text> 
                        <Text style={styles.classText}> Class 4 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.mClass3)}</Text> 
                        <Text style={styles.classText}> Class 3 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text  numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.mClass2)}</Text> 
                        <Text style={styles.classText}> Class 2 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.mClass1)}</Text> 
                        <Text style={styles.classText}> Class 1 reports</Text>
                    </View>
                
                </View>

                <Text style={styles.monthlyStatsTitle}>Sherlock lifelong stats</Text>
                <View style={styles.overallStatsContainer}>
                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.highStatNumber}>{formatWithCommas(stats.class5)}</Text> 
                        <Text style={styles.classText}> Class 5 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.highStatNumber}>{formatWithCommas(stats.class4)}</Text> 
                        <Text style={styles.classText}> Class 4 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.class3)}</Text> 
                        <Text style={styles.classText}> Class 3 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.class2)}</Text> 
                        <Text style={styles.classText}> Class 2 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.class1)}</Text> 
                        <Text style={styles.classText}> Class 1 reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.kpd_cip)}</Text> 
                        <Text style={styles.classText}>KPD reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.knox_zones)}</Text> 
                        <Text style={styles.classText}>KCSO reports</Text>
                    </View>

                    <View style={styles.statRowText}>
                        <Text numberOfLines={1} adjustsFontSizeToFit style={styles.statNumber}>{formatWithCommas(stats.utpd_clery)}</Text> 
                        <Text style={styles.classText}>UTPD reports</Text>
                    </View>

                </View>


            </ScrollView>
        </View>
    );
}

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

const styles = StyleSheet.create({
    container: {
        flex: 1,
        // marginTop: Constants.statusBarHeight,
        overflow:"scroll",
        backgroundColor: Colors.dark.tabBg,
    },
    title: {
        fontSize: 20,
        fontWeight: "bold",
        alignSelf: "center",
    },
    separator: {
        marginVertical: 15,
        height: 1,
        width: "80%",
        alignSelf: "center",
    },
    iconContainer:{
        display: "flex",
        alignItems: "center",
        height:height*.52,
        padding: 20,    
        flexDirection: "row",
        flexWrap: "wrap",
        columnGap: 20, 
        alignContent: "flex-start",
        backgroundColor: Colors.dark.tabBg,
    },
    iconButton:{
        marginLeft: "auto",
        marginRight: "auto",
        marginTop: 20,
        width:Dimensions.get("window").width*0.2,
        height:Dimensions.get("window").width*0.27,
    },
    icons:{
        color: Colors.dark.redPastel,
        fontSize: 60,
        backgroundColor: Colors.dark.alertBackground,
        borderRadius: 10,
        padding: 10,
        alignSelf: "center",
        justifyContent: "center",
        width:Dimensions.get("window").width*0.2,
    },
    iconText:{
        textAlign: "center",
        marginTop: 2,
    },
    web:{
        position: "absolute",
        marginTop: Constants.statusBarHeight,
        top: 0,
        width: Dimensions.get("window").width,
        height: Dimensions.get("window").height-50,
        zIndex: 100,
    },
    content:{
        position: "relative",
        zIndex: 1,
        marginTop: Constants.statusBarHeight,
        overflow: "scroll",
    },
    backIcon:{
        position: "absolute",
        top: 20,
        left: 20,
        zIndex: 100,
        backgroundColor: Colors.dark.background,
        borderRadius: 40,
        color: Colors.dark.redPastel,
    },
    statsContainer:{
        flex:1,
        flexDirection: "row",
        width: width*.9,
        flexWrap: "wrap",
        alignItems:"flex-start",
        backgroundColor: Colors.dark.background,
        alignSelf: "center",
        borderRadius: 10,
        marginBottom:20,
        padding:5,
    },
    highStatNumber:{
        color:Colors.dark.redPastel,
        fontSize: 50,
        marginLeft:20,
        flex:1,
        flexWrap:"nowrap",
    },
    statNumber:{
        color:Colors.dark.cyan,
        fontSize: 50,
        marginLeft:20,
        flexShrink: 1,
        flex:1,

    },
    statRowText:{
        // flex:1,
        flexDirection: "row",
        alignItems: "center",
        alignContent: "flex-start",
        columnGap: 10,
        backgroundColor: Colors.dark.background,
        width:"100%",
        maxWidth: "100%",
        flexWrap: "nowrap",
    },
    monthlyStatsTitle:{
        fontSize: 16,
        fontWeight: "bold",
        marginLeft: 20,
        marginBottom: 10,
    },
    overallStatsContainer:{
        flex:1,
        flexDirection: "row",
        // height: height*.5,
        width: width*.9,
        flexWrap: "wrap",
        alignItems:"flex-start",
        backgroundColor: Colors.dark.background,
        alignSelf: "center",
        borderRadius: 10,
        marginBottom:80,
        overflow: "scroll",
        padding:5,
    },
    classText:{
        fontSize:20,
        flexWrap:"nowrap",
        flex:1,
    },
    lastFetchedText:{
        fontSize: 14,
        marginBottom: 10,
        alignSelf: "center",
        fontWeight:"semibold",
        fontStyle: "italic",
    }
});