2025-03-01 13:43:42 -05:00
|
|
|
// app/(tabs)/social/global.tsx
|
2025-03-24 15:55:58 -04:00
|
|
|
import React, { useState, useCallback, useRef } from 'react';
|
2025-03-21 22:21:45 -04:00
|
|
|
import { View, FlatList, RefreshControl, TouchableOpacity } from 'react-native';
|
2025-03-01 13:43:42 -05:00
|
|
|
import { Text } from '@/components/ui/text';
|
2025-03-21 22:21:45 -04:00
|
|
|
import EnhancedSocialPost from '@/components/social/EnhancedSocialPost';
|
2025-03-24 15:55:58 -04:00
|
|
|
import { ChevronUp, Globe } from 'lucide-react-native';
|
2025-03-21 22:21:45 -04:00
|
|
|
import { router } from 'expo-router';
|
2025-03-23 15:53:34 -04:00
|
|
|
import { withOfflineState } from '@/components/social/SocialOfflineState';
|
2025-03-24 15:55:58 -04:00
|
|
|
import { useSocialFeed } from '@/lib/hooks/useSocialFeed';
|
2025-03-01 13:43:42 -05:00
|
|
|
|
2025-03-23 15:53:34 -04:00
|
|
|
function GlobalScreen() {
|
2025-03-21 22:21:45 -04:00
|
|
|
const {
|
2025-03-24 15:55:58 -04:00
|
|
|
feedItems,
|
2025-03-21 22:21:45 -04:00
|
|
|
loading,
|
2025-03-24 15:55:58 -04:00
|
|
|
refresh,
|
|
|
|
isOffline
|
|
|
|
} = useSocialFeed({
|
|
|
|
feedType: 'global',
|
|
|
|
limit: 30
|
|
|
|
});
|
|
|
|
|
|
|
|
// Convert feed items to the format expected by the UI
|
|
|
|
const entries = React.useMemo(() => {
|
|
|
|
return feedItems.map(item => ({
|
|
|
|
id: item.id,
|
|
|
|
eventId: item.id,
|
|
|
|
event: item.originalEvent,
|
|
|
|
content: item.parsedContent,
|
|
|
|
timestamp: item.createdAt * 1000,
|
|
|
|
type: item.type as any
|
|
|
|
}));
|
|
|
|
}, [feedItems]);
|
2025-03-21 22:21:45 -04:00
|
|
|
|
|
|
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
|
|
const [showNewButton, setShowNewButton] = useState(false);
|
2025-03-24 15:55:58 -04:00
|
|
|
const [newEntries, setNewEntries] = useState<any[]>([]);
|
2025-03-21 22:21:45 -04:00
|
|
|
|
|
|
|
// Use ref for FlatList to scroll to top
|
|
|
|
const listRef = useRef<FlatList>(null);
|
|
|
|
|
|
|
|
// Show new entries button when we have new content
|
2025-03-24 15:55:58 -04:00
|
|
|
React.useEffect(() => {
|
2025-03-21 22:21:45 -04:00
|
|
|
if (newEntries.length > 0) {
|
|
|
|
setShowNewButton(true);
|
|
|
|
}
|
|
|
|
}, [newEntries.length]); // Depend on length, not array reference
|
|
|
|
|
|
|
|
// Handle showing new entries
|
|
|
|
const handleShowNewEntries = useCallback(() => {
|
2025-03-24 15:55:58 -04:00
|
|
|
setNewEntries([]);
|
2025-03-21 22:21:45 -04:00
|
|
|
setShowNewButton(false);
|
|
|
|
// Scroll to top
|
|
|
|
listRef.current?.scrollToOffset({ offset: 0, animated: true });
|
2025-03-24 15:55:58 -04:00
|
|
|
}, []);
|
2025-03-21 22:21:45 -04:00
|
|
|
|
2025-03-24 15:55:58 -04:00
|
|
|
// Handle refresh - updated to use forceRefresh parameter
|
2025-03-21 22:21:45 -04:00
|
|
|
const handleRefresh = useCallback(async () => {
|
|
|
|
setIsRefreshing(true);
|
|
|
|
try {
|
2025-03-24 15:55:58 -04:00
|
|
|
console.log('[GlobalScreen] Starting manual refresh (force=true)');
|
|
|
|
// Use force=true to bypass cooldown
|
|
|
|
await refresh(true);
|
2025-03-21 22:21:45 -04:00
|
|
|
// Add a slight delay to ensure the UI updates
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
2025-03-24 15:55:58 -04:00
|
|
|
console.log('[GlobalScreen] Manual refresh completed successfully');
|
2025-03-21 22:21:45 -04:00
|
|
|
} catch (error) {
|
|
|
|
console.error('Error refreshing feed:', error);
|
|
|
|
} finally {
|
|
|
|
setIsRefreshing(false);
|
|
|
|
}
|
2025-03-24 15:55:58 -04:00
|
|
|
}, [refresh]);
|
2025-03-21 22:21:45 -04:00
|
|
|
|
|
|
|
// Handle post selection - simplified for testing
|
2025-03-24 15:55:58 -04:00
|
|
|
const handlePostPress = useCallback((entry: any) => {
|
2025-03-21 22:21:45 -04:00
|
|
|
// Just show an alert with the entry info for testing
|
2025-03-24 15:55:58 -04:00
|
|
|
alert(`Selected ${entry.type} with ID: ${entry.id || entry.eventId}`);
|
2025-03-21 22:21:45 -04:00
|
|
|
|
|
|
|
// Alternatively, log to console for debugging
|
|
|
|
console.log(`Selected ${entry.type}:`, entry);
|
2025-03-01 13:43:42 -05:00
|
|
|
}, []);
|
2025-03-24 15:55:58 -04:00
|
|
|
|
|
|
|
// Memoize render item to prevent re-renders
|
|
|
|
const renderItem = useCallback(({ item }: { item: any }) => (
|
2025-03-21 22:21:45 -04:00
|
|
|
<EnhancedSocialPost
|
2025-03-24 15:55:58 -04:00
|
|
|
item={{
|
|
|
|
id: item.id || item.eventId,
|
|
|
|
type: item.type,
|
|
|
|
originalEvent: item.originalEvent || item.event,
|
|
|
|
parsedContent: item.parsedContent || item.content,
|
|
|
|
createdAt: item.createdAt || (item.timestamp ? item.timestamp / 1000 : Date.now() / 1000)
|
|
|
|
}}
|
2025-03-21 22:21:45 -04:00
|
|
|
onPress={() => handlePostPress(item)}
|
|
|
|
/>
|
|
|
|
), [handlePostPress]);
|
|
|
|
|
2025-03-24 15:55:58 -04:00
|
|
|
// Header component
|
|
|
|
const renderHeaderComponent = useCallback(() => (
|
|
|
|
<View className="p-4 border-b border-border bg-primary/5 mb-2">
|
|
|
|
<View className="flex-row items-center mb-2">
|
|
|
|
<Globe size={20} className="mr-2 text-primary" />
|
|
|
|
<Text className="text-lg font-bold">Community Feed</Text>
|
|
|
|
</View>
|
|
|
|
<Text className="text-muted-foreground">
|
|
|
|
Discover workout content from the broader POWR community.
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
), []);
|
|
|
|
|
2025-03-21 22:21:45 -04:00
|
|
|
// Memoize empty component
|
|
|
|
const renderEmptyComponent = useCallback(() => (
|
|
|
|
loading ? (
|
|
|
|
<View className="flex-1 items-center justify-center p-8">
|
2025-03-24 15:55:58 -04:00
|
|
|
<Text>Loading community content from your relays...</Text>
|
2025-03-21 22:21:45 -04:00
|
|
|
</View>
|
|
|
|
) : (
|
|
|
|
<View className="flex-1 items-center justify-center p-8">
|
2025-03-24 15:55:58 -04:00
|
|
|
<Text>{isOffline ? "No cached global content available" : "No global content found"}</Text>
|
|
|
|
{isOffline && (
|
|
|
|
<Text className="text-muted-foreground text-center mt-2">
|
|
|
|
You're currently offline. Connect to the internet to see the latest content.
|
|
|
|
</Text>
|
|
|
|
)}
|
2025-03-21 22:21:45 -04:00
|
|
|
</View>
|
|
|
|
)
|
2025-03-24 15:55:58 -04:00
|
|
|
), [loading, isOffline]);
|
2025-03-21 22:21:45 -04:00
|
|
|
|
2025-03-01 13:43:42 -05:00
|
|
|
return (
|
2025-03-21 22:21:45 -04:00
|
|
|
<View className="flex-1">
|
|
|
|
{showNewButton && (
|
|
|
|
<TouchableOpacity
|
|
|
|
className="absolute top-2 left-0 right-0 z-10 mx-auto w-40 rounded-full bg-primary py-2 px-4 flex-row items-center justify-center"
|
|
|
|
onPress={handleShowNewEntries}
|
|
|
|
activeOpacity={0.8}
|
|
|
|
>
|
|
|
|
<ChevronUp size={16} color="white" />
|
|
|
|
<Text className="text-white font-medium ml-1">
|
|
|
|
New Posts ({newEntries.length})
|
|
|
|
</Text>
|
|
|
|
</TouchableOpacity>
|
|
|
|
)}
|
2025-03-24 15:55:58 -04:00
|
|
|
|
2025-03-21 22:21:45 -04:00
|
|
|
<FlatList
|
|
|
|
ref={listRef}
|
2025-03-24 15:55:58 -04:00
|
|
|
data={entries as any[]}
|
2025-03-21 22:21:45 -04:00
|
|
|
keyExtractor={(item) => item.id}
|
|
|
|
renderItem={renderItem}
|
|
|
|
refreshControl={
|
|
|
|
<RefreshControl
|
|
|
|
refreshing={isRefreshing}
|
|
|
|
onRefresh={handleRefresh}
|
|
|
|
/>
|
|
|
|
}
|
2025-03-24 15:55:58 -04:00
|
|
|
ListHeaderComponent={renderHeaderComponent}
|
2025-03-21 22:21:45 -04:00
|
|
|
ListEmptyComponent={renderEmptyComponent}
|
2025-03-24 15:55:58 -04:00
|
|
|
contentContainerStyle={{ flexGrow: 1, paddingBottom: 16 }}
|
2025-03-21 22:21:45 -04:00
|
|
|
/>
|
|
|
|
</View>
|
2025-03-01 13:43:42 -05:00
|
|
|
);
|
2025-03-23 15:53:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Export the component wrapped with the offline state HOC
|
|
|
|
export default withOfflineState(GlobalScreen);
|