import React, { useState, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { motion } from 'framer-motion'; import Navbar from './Navbar'; import Footer from './footer'; import './ProfilePage.css'; const UserProfile = () => { const navigate = useNavigate(); const { username } = useParams(); const [isLoggedIn, setIsLoggedIn] = useState(false); const [isImageLoaded, setIsImageLoaded] = useState(false); const [newComment, setNewComment] = useState(''); const [userData, setUserData] = useState({ username: '', bio: 'No bio yet', profilePicture: '', posts: [], followers: [], following: [], postsCount: 0, }); const [loggedInUserData, setLoggedInUserData] = useState({ username: '', following: [] }); const [showPostDetailModal, setShowPostDetailModal] = useState(false); const [selectedPost, setSelectedPost] = useState(null); const [likedPosts, setLikedPosts] = useState([]); const [isFollowing, setIsFollowing] = useState(false); const [isUpdatingFollow, setIsUpdatingFollow] = useState(false); const [pins, setPins] = useState([]); const [showFollowModal, setShowFollowModal] = useState(false); const [followListType, setFollowListType] = useState(''); // 'followers' or 'following' const [followList, setFollowList] = useState([]); const [isLoadingFollowList, setIsLoadingFollowList] = useState(false); // Default profile picture URL const defaultProfilePic = 'https://journeypoint-bucket.s3.us-east-2.amazonaws.com/uploads/default_profile_pic.jpg'; // Check login status and fetch data useEffect(() => { const storedUsername = localStorage.getItem('username'); const loginStatus = localStorage.getItem('login'); if (loginStatus === 'true' && storedUsername) { setIsLoggedIn(true); setLoggedInUserData(prev => ({ ...prev, username: storedUsername })); fetchLoggedInUserData(storedUsername); fetchUserData(username); fetchUserPosts(username); fetchUserPins(username); } else { navigate('/login'); } }, [navigate, username]); useEffect(() => { if (loggedInUserData.username && userData.username) { fetchUserPosts(userData.username); } }, [isFollowing, loggedInUserData.username, userData.username]); // Check if logged in user is following this profile useEffect(() => { if (loggedInUserData.username && userData.followers) { setIsFollowing(userData.followers.includes(loggedInUserData.username)); } }, [loggedInUserData.username, userData.followers]); // Fetch user pins const fetchUserPins = async (username) => { try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/pins?username=${username}`); const data = await response.json(); setPins(Array.isArray(data) ? data : []); } catch (error) { console.error('Error fetching pins:', error); } }; // Fetch user profile data const fetchUserData = async (username) => { try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/getuser?username=${username}`); const user = await response.json(); if (user.length > 0) { setUserData(prev => ({ ...prev, username: user[0].username, bio: user[0].bio || 'No bio yet', profilePicture: user[0].profile_picture, followers: user[0].followers || [], following: user[0].following || [], })); } } catch (error) { console.error('Error fetching user data:', error); } }; // Fetch logged in user data const fetchLoggedInUserData = async (username) => { try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/getuser?username=${username}`); const user = await response.json(); if (user.length > 0) { setLoggedInUserData(prev => ({ ...prev, following: user[0].following || [] })); } } catch (error) { console.error('Error fetching logged in user data:', error); } }; const fetchUserPosts = async (username) => { try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/userposts?username=${username}`); const data = await response.json(); // Check if viewing own profile or is a follower const isOwnProfile = loggedInUserData.username === username; const isFollower = userData.followers.includes(loggedInUserData.username); // Filter posts based on visibility const filteredPosts = Array.isArray(data) ? data.filter(post => isOwnProfile || isFollower || post.public) : []; setUserData(prev => ({ ...prev, posts: filteredPosts, postsCount: filteredPosts.length })); } catch (error) { console.error('Error fetching user posts:', error); } }; const showFollowList = async (type) => { setFollowListType(type); setIsLoadingFollowList(true); setShowFollowModal(true); try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/getuser?username=${username}`); const user = await response.json(); if (user.length > 0) { const usernames = type === 'followers' ? user[0].followers : user[0].following; // Fetch details for each user in the list const usersData = await Promise.all( usernames.map(async (username) => { const userResponse = await fetch(`https://jp-backend-kc80.onrender.com/api/getuser?username=${username}`); const user = await userResponse.json(); return user[0]; }) ); setFollowList(usersData.filter(user => user)); // Filter out any undefined users } } catch (error) { console.error('Error fetching follow list:', error); } finally { setIsLoadingFollowList(false); } }; // Handle follow/unfollow action const handleFollow = async () => { if (!loggedInUserData.username || !username || isUpdatingFollow) return; setIsUpdatingFollow(true); try { if (isFollowing) { // Unfollow - remove logged in user from profile's followers and remove profile from logged in user's following const formData = new FormData(); formData.append("unfollow_username", username); const response1 = await fetch(`https://jp-backend-kc80.onrender.com/api/userupdate/${loggedInUserData.username}/unfollow`, { method: 'PATCH', body: formData, }); const result1 = await response1.json(); if (!response1.ok) throw new Error(result1.message || 'Failed to remove follower'); // Update local state based on successful responses setUserData(prev => ({ ...prev, followers: prev.followers.filter(follower => follower !== loggedInUserData.username) })); setLoggedInUserData(prev => ({ ...prev, following: prev.following.filter(following => following !== username) })); } else { // Follow - add logged in user to profile's followers and add profile to logged in user's following const formData = new FormData(); formData.append("add_follower", loggedInUserData.username); const response1 = await fetch(`https://jp-backend-kc80.onrender.com/api/userupdate/${userData.username}`, { method: 'PATCH', body: formData, }); const result1 = await response1.json(); if (!response1.ok) throw new Error(result1.message || 'Failed to add follower' + loggedInUserData.username + userData.username + result1.add_follower); const formData2 = new FormData(); formData2.append("add_following", username); const response2 = await fetch(`https://jp-backend-kc80.onrender.com/api/userupdate/${loggedInUserData.username}`, { method: 'PATCH', body: formData2, }); const result2 = await response2.json(); if (!response2.ok) throw new Error(result2.message || 'Failed to add following'); // Update local state based on successful responses setUserData(prev => ({ ...prev, followers: [...prev.followers, loggedInUserData.username] })); setLoggedInUserData(prev => ({ ...prev, following: [...prev.following, username] })); } // Toggle follow state after successful updates setIsFollowing(!isFollowing); } catch (error) { console.error('Error updating follow status:', error); alert(error.message || 'Failed to update follow status. Please try again.'); } finally { setIsUpdatingFollow(false); } }; const handleAddComment = async (e) => { e.preventDefault(); if (!newComment.trim() || !selectedPost || !loggedInUserData.username) return; try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/userposts/${selectedPost.id}/comment`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: loggedInUserData.username, comment: newComment }), }); if (!response.ok) throw new Error('Failed to add comment'); // Create the new comment entry const newCommentEntry = { [loggedInUserData.username]: newComment }; // Update the selected post setSelectedPost(prev => ({ ...prev, comments: { ...(prev.comments || {}), ...newCommentEntry } })); // Update the post in the main posts list setUserData(prev => ({ ...prev, posts: prev.posts.map(post => post.id === selectedPost.id ? { ...post, comments: { ...(post.comments || {}), ...newCommentEntry } } : post ) })); setNewComment(''); } catch (error) { console.error('Error adding comment:', error); alert('Failed to add comment. Please try again.'); } }; const handleLikePost = async (postId) => { // Check if user already liked this post if (likedPosts.includes(postId)) { return; } try { const response = await fetch(`https://jp-backend-kc80.onrender.com/api/userposts/${postId}/like`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) throw new Error('Failed to like post'); const result = await response.json(); // Update liked posts state setLikedPosts(prev => [...prev, postId]); // Update the selected post's like count setSelectedPost(prev => ({ ...prev, likes: result.likes })); // Also update the post in the main posts list setUserData(prev => ({ ...prev, posts: prev.posts.map(post => post.id === postId ? { ...post, likes: result.likes } : post ) })); } catch (error) { console.error('Error liking post:', error); alert('Failed to like post. Please try again.'); } }; const canViewPost = (post) => { return ( loggedInUserData.username === userData.username || // Own post isFollowing || // Is a follower post.public // Public post ); }; // Then update your handlePostClick: const handlePostClick = (post) => { if (!canViewPost(post)) { alert("You must follow this user to view their private posts"); return; } setSelectedPost(post); setShowPostDetailModal(true); }; return (
{/* Profile Header Section */}
Profile setIsImageLoaded(true)} onError={(e) => { e.target.onerror = null; e.target.src = defaultProfilePic; setIsImageLoaded(true); }} />
{userData.username}
{userData.postsCount} posts showFollowList('followers')} > {userData.followers.length} followers showFollowList('following')} > {userData.following.length} following
{userData.bio}
{loggedInUserData.username !== username && ( )}
{/* Posts Grid Section */} {userData.posts.length > 0 ? (
{userData.posts.map((post, index) => (
handlePostClick(post)}> {`Post
❤️ {post.likes || 0} 💬 {Object.keys(post.comments || {}).length}
))}
) : (
{loggedInUserData.username === username ? ( <>

Share Photos

When you share photos, they will appear on your profile.

) : isFollowing ? (

{userData.username} hasn't posted anything yet.

) : (

{userData.username} only shares posts with followers.

)}
)}
{/* Post Detail Modal */} {showPostDetailModal && selectedPost && (
setShowPostDetailModal(false)}> e.stopPropagation()} >
Post
Profile {userData.username}

{selectedPost.description}

Tags: {selectedPost.tags || 'No tags'}
{/* Add this section for the pin location */} {selectedPost.pin_id && (
Location: {pins.find(pin => pin.id === selectedPost.pin_id)?.pin_name || 'Unknown location'}
)}
💬 {Object.keys(selectedPost.comments || {}).length} comments
{/* Comments section */}

Comments

{Object.keys(selectedPost.comments || {}).length > 0 ? (
{Object.entries(selectedPost.comments || {}).map(([username, comment]) => (
{username}: {comment}
))}
) : (

No comments yet

)}
{isLoggedIn && (
setNewComment(e.target.value)} />
)}
Posted on: {new Date(selectedPost.created_at).toLocaleDateString()}
)} {showFollowModal && (
setShowFollowModal(false)}> e.stopPropagation()} >

{followListType === 'followers' ? 'Followers' : 'Following'}

{isLoadingFollowList ? (
Loading...
) : followList.length > 0 ? (
{followList.map((user) => (
{ setShowFollowModal(false); navigate(`/profile/${user.username}`); }} > {user.username} { e.target.onerror = null; e.target.src = defaultProfilePic; }} /> {user.username}
))}
) : (
No {followListType === 'followers' ? 'followers' : 'following'} yet
)}
)}
); }; export default UserProfile;