import { BackHandler, ListRenderItem, ListRenderItemInfo, Pressable, RefreshControl, StyleSheet } from 'react-native';
import { Text, View } from '../../../components/Themed';
import React, { useContext, useEffect } from 'react';
import { useFocusEffect, useNavigationState, useRoute } from '@react-navigation/native';
import constants from 'expo-constants';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import Colors from '../../../constants/Colors';
import { Badge, BadgeText, FlatList, set, Spinner, ToastDescription, VStack} from '@gluestack-ui/themed';
import { TouchableOpacity } from 'react-native-gesture-handler';
import global from '../../../constants/global';
import { router, useLocalSearchParams, useNavigation, useRouter } from 'expo-router';
import { getFeed, getInitPosts, getNewerPosts, getOlderPosts, getUserAlias } from '../../../components/backend';
import { getIncidentEmoji } from '../../../constants/emojis';
import PostListView from '../../../components/postListView';
import { storeData, getData } from '../../../components/storage';
import { Toast, useToast } from '@gluestack-ui/themed';
import { ToastTitle } from '@gluestack-ui/themed';
import { FeedContext } from '../../../components/feedContext';
import { useAuth } from '../../../components/authContext';
import { supabase } from '../../../components/supabase';
export default function Home() {
const [aliasemoji, setAliasEmoji] = React.useState('๐');
const [refreshing, setRefreshing] = React.useState(false);
const [cached, setCached] = React.useState(false);
const [feed, setFeed] = useContext(FeedContext) as any;
const [loading, setLoading] = React.useState(true);
const [noUser, setNoUser] = React.useState(true);
const [userLocation, setUserLocation] = React.useState<any>(null);
const [locStat, setLocStat] = React.useState<any>();
const [user, setUser] = React.useState(null);
let initRan = false;
const feedRef = React.useRef<any>(null);
const nav = useNavigation();
const auth = useAuth()
//Scrolls to top on tab click
useEffect(() => {
//@ts-ignore
const unsub = nav.getParent()?.addListener('tabPress', (e) => {
if(nav.isFocused()){
feedRef.current.scrollToOffset({ animated: true, offset: 0 });
}
})
return unsub;
}, [nav]);
function getInit(){
console.log("Fetching init posts");
getInitPosts().then((res) => {
initRan = true;
let temp:any[] = res.posts.concat(res.reportposts)
if(temp.length == 0){
console.log("No posts fetched on start. Using cache...");
setFeed(cached);
setRefreshing(false);
setLoading(false);
return;
}
setNoUser(false);
temp.sort(function(a:any, b:any){
return b.eid - a.eid;
})
//Makes sure there is no duplicates
for(let i=0;i<feed.length;i++){
for(let j=0;j<temp.length;j++){
if(feed[i].eid == temp[j].eid){
temp.splice(j,1);
}
}}
for(let i = 0; i < temp.length; i++){
temp[i].liked = false;
temp[i].likeCount=0;
temp[i].commentCount=0;
}
console.log("Init fetched: ", temp.length, "posts");
setRefreshing(false);
setFeed(temp);
setLoading(false);
}).catch((err) => {
console.log("Error fetching init posts: ", err);
if(err.message == "No current user"){
setNoUser(true);
return;
}else{
setFeed(cached)
}
})
setRefreshing(false);
}
//Fetches cache on mount
useEffect(() => {
// Fetches cached feed
getData("feedCache")
.then((res) => {
if (res && feed.length == 0) {
setCached(res);
}
})
.catch((err) => {
console.log("Error fetching cached feed: ", err);
})
}, []);
//Fetches init feed on login
useEffect(()=>{
const {data: { subscription }} = supabase.auth.onAuthStateChange((_event, session) => {
if(session?.user){
//Sets user alias and its emoji
getUserAlias()
.then((alias) => {
setNoUser(false);
let temp = getIncidentEmoji(alias.alias, "6", true);
global.aliasEmoji = temp[0];
global.aliasClass = temp[1];
global.alias = alias.alias;
setAliasEmoji(global.aliasEmoji);
})
.catch((err) => {
console.log("Could not fetch a user alias at home.", err);
if (err.message == "No current user") {
setNoUser(true);
return;
}
});
getInit();
}
})
return () => {
subscription.unsubscribe()
}
},[])
// Prevents the back button from exiting the app
useFocusEffect(
React.useCallback(() => {
const onBackPress = () => {
return true;
};
const noback = BackHandler.addEventListener("hardwareBackPress", onBackPress);
noback.remove()
}, [])
);
function handleRefresh(clear:boolean=false){
setRefreshing(true);
if(noUser){
console.log("No user, cannot refresh feed");
setRefreshing(false);
return;
}
//Uses oldest local post as reference for fetching more posts
//Will only get posts older than the oldest local post
if(feed.length == 0 || !feed){
getInit();
return;
}
getNewerPosts(feed[0].eid).then((res) => {
if (!res) {
console.log("Error fetching more posts");
setRefreshing(false);
return;
}
if (res.message) {
if (res.message == "Invalid request") {
console.log("Invalid request");
setRefreshing(false);
return;
}
setRefreshing(false);
return;
}
setNoUser(false);
if (res.posts.length == 0 && res.reportposts.length == 0) {
console.log("No more posts");
setRefreshing(false);
return;
}
let temp = res.posts.concat(res.reportposts);
temp.sort(function(a:any, b:any){
return b.eid - a.eid;
})
for(let i = 0; i < temp.length; i++){
temp[i].liked = false;
temp[i].likeCount=0;
}
if(clear){
storeData('feedCache', []).then(() => {
setFeed(temp);
}).catch((err) => {
console.log("Error clearing cache: ", err);
})
return;
}
storeData('feedCache', [...temp, ...feed]).then(() => {
console.log("Feed cache updated");
}).catch((err) => {
console.log("Error updating cache: ", err);
})
console.log("Fetched: ", temp.length, "posts");
setFeed([...temp, ...feed]);
}).catch((err) => {
console.log("Error fetching more posts: ", err);
if(err.message == "No current user"){
setNoUser(true);
return;
}
})
}
function handleEndReached(){
if(noUser || !feed || feed.length == 0){return};
setRefreshing(true);
//Checking for edge case where feed is empty.
//This mainly just applies to startup
if((feed.length == 0 || !feed)&&initRan){
setRefreshing(false);
getInit();
return;
}else if(!initRan && feed.length == 0){
setRefreshing(false);
return;
}
if(noUser || !feed[feed.length-1].eid){
return
}
//Uses oldest local post as reference for fetching more posts
getOlderPosts(feed[feed.length-1].eid).then((res) => {
if (!res) {
console.log("Error fetching more posts");
setRefreshing(false);
return;
}
if (res.message) {
if (res.message == "Invalid request") {
console.log("Invalid request");
setRefreshing(false);
return;
}
setRefreshing(false);
return;
}
if (res.posts.length == 0 && res.reportposts.length == 0) {
console.log("No more posts");
setRefreshing(false);
return;
}
let temp = res.posts.concat(res.reportposts);
temp.sort(function(a:any, b:any){
return b.eid - a.eid;
})
for(let i = 0; i < temp.length; i++){
temp[i].liked = false;
temp[i].likeCount=0;
}
storeData('feedCache', [...feed, ...temp]).then(() => {
console.log("Feed cache updated");
}).catch((err) => {
console.log("Error updating cache: ", err);
})
console.log("Fetched: ", temp.length, "posts");
setFeed([...feed, ...temp]);
setRefreshing(false);
})
}
return (
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
{/* <View style={styles.sherlock}> */}
<View style={styles.titleDiv}>
<Text style={styles.title}>Sherlock</Text>
<View style={styles.sherlock}>
<Badge
size="sm"
variant="solid"
action="info"
style={styles.beta}
>
<BadgeText ml={"auto"} mr={"auto"}>
Beta
</BadgeText>
</Badge>
{/* Will later be dynamic for location */}
<Text style={styles.location}>Knoxville</Text>
</View>
{/* </View> */}
</View>
<View
style={{
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: Colors.dark.tabBg,
}}
>
<View
style={[
styles.aliasHeader,
{
backgroundColor:
Colors.map[
("class" + global.aliasClass) as keyof typeof Colors.map
],
},
]}
>
<Text adjustsFontSizeToFit numberOfLines={1} style={[styles.emoji]}>
{" "}
{aliasemoji}{" "}
</Text>
</View>
{/* Notifications button */}
{/* <TouchableOpacity
onPress={() => {
router.push("/(home)/notifications");
}}
>
<MaterialCommunityIcons
name="bell"
size={30}
color={Colors.dark.redPastel}
/>
</TouchableOpacity> */}
</View>
</View>
<View style={styles.separator} darkColor="rgba(255,255,255,0.1)" />
{/* Main content view */}
<FlatList
key={"feed"}
style={styles.list}
data={feed}
ref={feedRef}
onEndReachedThreshold={0.1}
contentContainerStyle={{ paddingBottom: 70 }}
ListEmptyComponent={
<Text
style={{
textAlign: "center",
fontSize: 25,
justifyContent: "center",
marginTop: global.height * 0.3,
color: Colors.dark.redBright,
}}
>
This is awkward; No posts were found.{"\n\n"}
<Text style={{ fontSize: 20, color: Colors.dark.text }}>
Try refreshing or, if you're feeling fancy, add one!
</Text>
{"\n"}
๐โโ๏ธ
</Text>
}
refreshControl={
<RefreshControl
refreshing={refreshing}
colors={[Colors.dark.cyan, Colors.dark.redBright]}
progressBackgroundColor={Colors.dark.backgroundProfile}
onRefresh={() => {
handleRefresh(true);
}}
/>
}
onEndReached={() => {
handleEndReached();
}}
renderItem={(post: ListRenderItemInfo<any>) => {
return <PostListView post={post} />;
}}
/>
{/* Add post button */}
<TouchableOpacity
style={styles.newPost}
onPress={() => {
console.log("New Post");
// Navigate to new post screen
router.push("/(home)/postCreate");
}}
>
<MaterialCommunityIcons
name="text-box-plus"
size={50}
color={Colors.dark.redPastel}
/>
</TouchableOpacity>
{loading && (
<View style={styles.loading}>
<VStack style={styles.spinner}>
<Spinner size="large" color={Colors.dark.redBright} />
<Text style={{ textAlign: "center", fontSize: 20 }}>
Loading all the posts...{"\n"} ๐
</Text>
</VStack>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
spinner: {
top: global.height * 0.5,
backgroundColor: Colors.dark.backgroundModal,
borderRadius: 10,
padding: 10,
textAlign: "center",
alignSelf: "center",
},
loading: {
position: "absolute",
backgroundColor: Colors.dark.transparentTint,
height: global.height,
width: global.width,
},
sherlock: {
backgroundColor: Colors.dark.tabBg,
flexDirection: "column",
columnGap:0,
},
beta: {
height: 20,
backgroundColor:Colors.dark.transparentBlue,
width:45,
// flex:1,
// flexShrink:1,
borderRadius:50,
},
location: {
color: Colors.dark.redBright,
marginLeft: 0,
fontWeight: "bold",
textAlignVertical: 'center',
fontSize: 13,
// backgroundColor: Colors.dark.red,
height: 30,
},
titleDiv: {
flexDirection: "row",
backgroundColor: Colors.dark.tabBg,
columnGap: 0,
},
title: {
fontSize: 25,
fontWeight: "bold",
color: Colors.dark.cyan,
marginLeft: 0,
marginTop: 10,
},
container: {
flex: 1,
backgroundColor: Colors.dark.tabBg,
},
content: {
padding: 10,
width: global.width,
},
header: {
marginTop: constants.statusBarHeight,
flexDirection: "row",
justifyContent: "space-between",
padding: 10,
backgroundColor: Colors.dark.tabBg,
},
separator: {
marginVertical: 30,
height: 1,
width: global.width * 0.95,
alignSelf: "center",
marginTop: 0,
marginBottom: 5,
},
newPost: {
position: "absolute",
bottom: 80,
right: 20,
backgroundColor: Colors.dark.tabBg,
borderRadius: 20,
padding: 5,
},
aliasHeader: {
justifyContent: "center",
alignItems: "center",
height: global.height * 0.07,
width: global.height * 0.07,
borderWidth: 5,
borderColor: Colors.dark.backgroundProfile,
borderRadius: 10,
// marginRight: 15,
},
emoji: {
fontSize: 20,
},
list: {
width: global.width * 0.95,
alignSelf: "center",
marginBottom: 60,
backgroundColor: Colors.dark.tabBg,
},
});