import React, { useState, useEffect } from 'react'; import { StyleSheet, SafeAreaView, FlatList } from 'react-native'; import { Card, Text, Button } from 'react-native-paper'; import API from './../API.js'; import Post from './../components/Post.js'; import PostPopularUsers from '../components/PostPopularUsers.js'; import GlobalState from '../contexts/GlobalState.js'; import * as Linking from 'expo-linking'; import { posthog } from './../PostHog.js'; import * as Updates from 'expo-updates'; import { useSnapshot } from 'valtio'; import i18n from "../i18nMessages.js"; import AsyncStorage from '@react-native-async-storage/async-storage'; const FEED_LOG_PREFIX = '[Feed]'; const logFeed = (...args) => { if (__DEV__) console.log(FEED_LOG_PREFIX, ...args); }; const storeFeed = async (value) => { try { const jsonValue = JSON.stringify(value) await AsyncStorage.setItem('feed', jsonValue) } catch (e) { } } const getFeed = async () => { try { const value = await AsyncStorage.getItem('feed') if (value !== null) { return JSON.parse(value); } } catch (e) { return [] } } let prevLink = ''; const handleURL = (url, navigation) => { const { hostname, path, queryParams } = Linking.parse(url); if (!path) return; if (path.includes("feed/post/")) { const postid = path.substring(10); return navigation.navigate('SinglePost', { postid }); } if (path.includes("feed/")) { const profileid = path.substring(5); return navigation.navigate('Profile', { profileid }); } if (path === 'alert') { alert(queryParams.str); } else { //alert(path + " ::: " + queryParams); } } async function onFetchUpdateAsync() { if (__DEV__) return; try { const update = await Updates.checkForUpdateAsync(); if (update.isAvailable) { await Updates.fetchUpdateAsync(); await Updates.reloadAsync(); } } catch (error) { // You can also add an alert() to see the error message in case of an error when fetching updates. console.log(`Error fetching latest Expo update: ${error}`); } } let Feed = ({ navigation, route }) => { const gState = useSnapshot(GlobalState); const viewer = gState.me || {}; let [Posts, setPosts] = useState([]); const flatListRef = React.useRef() logFeed('render', { posts: Posts.length, hasRouteParams: !!route?.params, reRender: !!route?.params?.reRender, targetProfile: route?.params?.profileid || null, }); const url = Linking.useURL(); useEffect(() => { if (prevLink === url || !url) return; prevLink = url; logFeed('deep-link', url); handleURL(url, navigation); }, [url]); useEffect(() => { let subscribed = true; const getData = async () => { logFeed('load:start', { reRender: !!route?.params?.reRender, targetProfile: route?.params?.profileid || null, }); // TODO: Check for internet connection const internet = true; if (internet) { //byPass and load let loggedIn = await API.isLoggedIn(); logFeed('session:checked', { loggedIn }); if (!loggedIn) return navigation.reset({ index: 0, routes: [{ name: 'Login' }], }); if (route.params && route.params.profileid) { logFeed('redirect:profile', { profileid: route.params.profileid }); return navigation.navigate('Profile', { profileid: route.params.profileid }); } } if (!route.params?.reRender) { API.getMe().then((me) => { if (subscribed && me && me._id) { GlobalState.me = me; posthog.identify(me.userid, { name: me.profile?.firstName, profileid: me._id, is_superuser: me.superuser, } ); posthog.capture('login'); } }); let cacheFeed = await getFeed() || []; logFeed('cache:read', { count: cacheFeed.length }); if (cacheFeed.length && subscribed) setPosts(cacheFeed); } await onFetchUpdateAsync(); flatListRef.current?.scrollToOffset({ animated: true, offset: 0 }) let posts = await API.getPosts(); if (subscribed) { const safePosts = Array.isArray(posts) ? posts : []; setPosts(safePosts); storeFeed(safePosts); logFeed('network:loaded', { count: safePosts.length, cached: true }); } logFeed('load:end'); } getData() return () => { subscribed = false; logFeed('load:cleanup'); } }, [route.params]); const renderPost = (({ item }) => { if (item.nonOrganicType === 'PopularUsers' || item.nonOrganicType === 'PopularGroups') { if (item.nonOrganicType === 'PopularUsers') { return () } return (<>); } return (); }); const missingProfilePhoto = !viewer?.profile?.photo; const missingDescription = !String(viewer?.profile?.description || '').trim(); const shouldShowProfileNudge = missingProfilePhoto || missingDescription; const renderProfileNudge = () => { if (!shouldShowProfileNudge) return null; return ( {i18n.t("message.completeProfileTitle")} {i18n.t("message.completeProfileBody")} {i18n.t("message.completeProfileMissingHint")} ); }; return ( item.lastUpdated || item._id || item.createdAt} //This may refresh the component //ListHeaderComponent={ setPosts([newPost, ...Posts])} />} refreshing={Posts.length === 0} onRefresh={() => { logFeed('refresh:start'); API.getPosts().then((data) => { const safePosts = Array.isArray(data) ? data : []; setPosts(safePosts); storeFeed(safePosts); logFeed('refresh:end', { count: safePosts.length, cached: true }); }); }} initialNumToRender={3} maxToRenderPerBatch={3} removeClippedSubviews={true} style={styles.container} ref={flatListRef} /> ); } export default Feed; const styles = StyleSheet.create({ container: { backgroundColor: "#edf2f7", }, profileNudgeCard: { marginHorizontal: 10, marginTop: 10, marginBottom: 6, }, profileNudgeTitle: { fontSize: 18, fontWeight: "700", marginBottom: 4, }, profileNudgeBody: { color: "#4b5563", }, profileNudgeHint: { marginTop: 8, color: "#6b7280", fontSize: 12, }, });