From eaf49b86b5160f853020c8d61963a42016c5e25d Mon Sep 17 00:00:00 2001 From: IceCandle Date: Fri, 17 Jan 2025 18:02:35 +0900 Subject: [PATCH] Revert "Follow & edit" This reverts commit cfb6743733ee859d188dc7d0eceb8bdb7caf606a. --- src/components/profile/FollowButton.tsx | 62 -------- src/components/profile/ProfileInfo.tsx | 144 ++++-------------- .../profile/ProfilePictureUpload.tsx | 77 ---------- src/components/profile/ProfileTabs.tsx | 49 +++--- src/components/shared/PostGrid.tsx | 87 +---------- src/components/shared/PostModal.tsx | 119 --------------- src/pages/ExplorePage.tsx | 22 +-- src/pages/ProfilePage.tsx | 91 ++--------- src/types/post.ts | 15 +- src/types/user.ts | 8 +- 10 files changed, 81 insertions(+), 593 deletions(-) delete mode 100644 src/components/profile/FollowButton.tsx delete mode 100644 src/components/profile/ProfilePictureUpload.tsx delete mode 100644 src/components/shared/PostModal.tsx diff --git a/src/components/profile/FollowButton.tsx b/src/components/profile/FollowButton.tsx deleted file mode 100644 index f83f7a6..0000000 --- a/src/components/profile/FollowButton.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -interface FollowButtonProps { - userId: number; - onFollowChange?: (isFollowing: boolean) => void; -} - -const FollowButton = ({ userId, onFollowChange }: FollowButtonProps) => { - const [isFollowing, setIsFollowing] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const navigate = useNavigate(); - - const handleFollow = async () => { - try { - setIsLoading(true); - const token = localStorage.getItem('access_token'); - if (token === null) { - void navigate('/'); - return; - } - - const endpoint = isFollowing ? 'unfollow' : 'follow'; - const response = await fetch( - `http://3.34.185.81:8000/api/follower/${endpoint}?follow_id=${userId}`, - { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); - - if (!response.ok) { - throw new Error(`Failed to ${endpoint}`); - } - - setIsFollowing(!isFollowing); - onFollowChange?.(!isFollowing); - } catch (error) { - console.error('Error following/unfollowing:', error); - } finally { - setIsLoading(false); - } - }; - - return ( - - ); -}; - -export default FollowButton; \ No newline at end of file diff --git a/src/components/profile/ProfileInfo.tsx b/src/components/profile/ProfileInfo.tsx index fcc3bdf..6fd7e32 100644 --- a/src/components/profile/ProfileInfo.tsx +++ b/src/components/profile/ProfileInfo.tsx @@ -1,139 +1,57 @@ -import { Link as LinkIcon } from 'lucide-react'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { Settings } from 'lucide-react'; -import FollowButton from './FollowButton'; - -interface ProfileInfoProps { - userId: number; +type ProfileInfoProps = { username: string; posts: number; followers: number; following: number; fullName: string; bio: string; - website: string | null; - profileImage: string; -} +}; const ProfileInfo = ({ - userId, username, posts, followers, following, fullName, bio, - website, - profileImage, }: ProfileInfoProps) => { - const [isCurrentUser, setIsCurrentUser] = useState(false); - const [currentFollowers, setCurrentFollowers] = useState(followers); - const navigate = useNavigate(); - - useEffect(() => { - const checkCurrentUser = async () => { - try { - const token = localStorage.getItem('access_token'); - if (token === null) { - void navigate('/'); - return; - } - - const response = await fetch('http://3.34.185.81:8000/api/user/profile', { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (!response.ok) { - throw new Error('Failed to fetch user profile'); - } - - interface UserProfile { - user_id: number; - } - - const data: UserProfile = await response.json() as UserProfile; - setIsCurrentUser(data.user_id === userId); - } catch (error) { - console.error('Error checking current user:', error); - } - }; - - void checkCurrentUser(); - }, [userId, navigate]); - - const handleFollowChange = (isFollowing: boolean) => { - setCurrentFollowers((prev) => (isFollowing ? prev + 1 : prev - 1)); - }; - - const handleEditProfile = () => { - void navigate(`/settings`); - }; - return (
-
-
- 0 ? profileImage : '/placeholder.svg'} - alt={username} - className="w-full h-full object-cover" - /> -
- -
-
-

{username}

-
- {isCurrentUser ? ( - - ) : ( - - )} -
+
+ {username} +
+
+

{username}

+ +
- -
-
- {posts} - posts -
-
- {currentFollowers} - followers -
-
- {following} - following -
-
- -
-

{fullName}

- {bio.length > 0 &&

{bio}

} - {website !== null && website.length > 0 && ( - - - {website} - - )} +
+ + {posts} posts + + + {followers} followers + + + {following} following +
+
+

{fullName}

+

{bio}

+
); }; -export default ProfileInfo; \ No newline at end of file +export default ProfileInfo; diff --git a/src/components/profile/ProfilePictureUpload.tsx b/src/components/profile/ProfilePictureUpload.tsx deleted file mode 100644 index f36184f..0000000 --- a/src/components/profile/ProfilePictureUpload.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Camera } from 'lucide-react'; -import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -interface ProfilePictureUploadProps { - currentImage: string; - onSuccess: (newImageUrl: string) => void; -} - -const ProfilePictureUpload = ({ currentImage, onSuccess }: ProfilePictureUploadProps) => { - const [isUploading, setIsUploading] = useState(false); - const navigate = useNavigate(); - - const handleImageUpload = async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (file == null) return; - - try { - setIsUploading(true); - const token = localStorage.getItem('access_token'); - if (token == null) { - void navigate('/'); - return; - } - - const formData = new FormData(); - formData.append('profile_image', file); - - const response = await fetch('http://3.34.185.81:8000/api/user/profile/edit', { - method: 'PATCH', - headers: { - 'Authorization': `Bearer ${token}`, - }, - body: formData, - }); - - if (!response.ok) throw new Error('Failed to update profile picture'); - - const data = (await response.json()) as { profile_image: string }; - onSuccess(data.profile_image); - } catch (error) { - console.error('Error uploading profile picture:', error); - } finally { - setIsUploading(false); - } - }; - - return ( -
- 0 ? currentImage : '/placeholder.svg'} - alt="Profile" - className="w-full h-full object-cover rounded-full" - /> - - {isUploading && ( -
-
-
- )} -
- ); -}; - -export default ProfilePictureUpload; \ No newline at end of file diff --git a/src/components/profile/ProfileTabs.tsx b/src/components/profile/ProfileTabs.tsx index a45a16b..c51fef6 100644 --- a/src/components/profile/ProfileTabs.tsx +++ b/src/components/profile/ProfileTabs.tsx @@ -1,38 +1,35 @@ -import { Grid } from 'lucide-react'; +import { Bookmark, Grid } from 'lucide-react'; import { useState } from 'react'; -import type { APIPost } from '../../types/post'; import PostGrid from '../shared/PostGrid'; -import PostModal from '../shared/PostModal'; -interface ProfileTabsProps { - postIds: number[]; -} - -const ProfileTabs = ({ postIds }: ProfileTabsProps) => { - const [selectedPost, setSelectedPost] = useState(null); +const ProfileTabs = () => { + const [activeTab, setActiveTab] = useState('posts'); return (
-
-
- -
+
+ +
- - { setSelectedPost(post); }} - /> - - {selectedPost !== null && ( - { setSelectedPost(null); }} /> - )} + {activeTab === 'posts' && } + {activeTab === 'saved' &&
저장됨
}
); }; -export default ProfileTabs; \ No newline at end of file +export default ProfileTabs; diff --git a/src/components/shared/PostGrid.tsx b/src/components/shared/PostGrid.tsx index face7f0..48e56d3 100644 --- a/src/components/shared/PostGrid.tsx +++ b/src/components/shared/PostGrid.tsx @@ -1,90 +1,13 @@ -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import type { APIPost } from '../../types/post'; - -interface PostGridProps { - postIds: number[]; - onPostClick: (post: APIPost) => void; -} - -const PostGrid = ({ postIds, onPostClick }: PostGridProps) => { - const [posts, setPosts] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const navigate = useNavigate(); - - useEffect(() => { - const fetchPosts = async () => { - try { - setIsLoading(true); - const token = localStorage.getItem('access_token'); - if (token === null) { - void navigate('/'); - return; - } - - const postsPromises = postIds.map(id => - fetch(`http://3.34.185.81:8000/api/post/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).then(res => res.json()) - ); - - const postsData = await Promise.all(postsPromises); - setPosts(postsData); - } catch (error) { - console.error('Error fetching posts:', error); - } finally { - setIsLoading(false); - } - }; - - void fetchPosts(); - }, [postIds, navigate]); - - const getFullImageUrl = (url: string) => { - if (url.startsWith('http')) return url; - return `http://3.34.185.81:8000/${url.replace(/^\/+/, '')}`; - }; - - if (isLoading) { - return ( -
- {Array.from({ length: 6 }).map((_, index) => ( -
- ))} -
- ); - } +const PostGrid = () => { + const posts = Array(4).fill(null); return (
- {posts.map((post) => ( -
{ onPostClick(post); }} - className="aspect-square relative group cursor-pointer" - > - {post.post_text -
-
- {((post.post_text != null) || (post.location != null)) && ( -
- {(post.post_text != null) &&

{post.post_text}

} - {(post.location != null) &&

{post.location}

} -
- )} -
-
-
+ {posts.map((_, index) => ( +
))}
); }; -export default PostGrid; \ No newline at end of file +export default PostGrid; diff --git a/src/components/shared/PostModal.tsx b/src/components/shared/PostModal.tsx deleted file mode 100644 index dfa83c1..0000000 --- a/src/components/shared/PostModal.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { X } from 'lucide-react'; -import { useEffect, useState } from 'react'; - -import type { APIPost, FeedPost } from '../../types/post'; - -interface PostModalProps { - post: APIPost | FeedPost; - onClose: () => void; -} - -interface UserResponse { - username: string; - // Add other fields as needed -} - -const PostModal = ({ post, onClose }: PostModalProps) => { - const [username, setUsername] = useState(''); - - useEffect(() => { - // If it's a feed post, we already have the username - if ('username' in post) { - setUsername(post.username); - return; - } - - // If it's an API post, we need to fetch the username - const fetchUsername = async () => { - try { - const token = localStorage.getItem('access_token'); - if (token === null) return; - - const response = await fetch(`http://3.34.185.81:8000/api/user/profile/${post.user_id}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (!response.ok) throw new Error('Failed to fetch user'); - - const data = await response.json() as UserResponse; - if (typeof data.username === 'string') { - setUsername(data.username); - } - } catch (error) { - console.error('Error fetching username:', error); - } - }; - - void fetchUsername(); - }, [post]); - - const getImageUrl = () => { - if ('imageUrl' in post) return post.imageUrl; - return post.file_url[0] != null ? - `http://3.34.185.81:8000/${post.file_url[0].replace(/^\/+/, '')}` : - '/placeholder.svg'; - }; - - const getCaption = () => { - if ('caption' in post) return post.caption; - return post.post_text; - }; - - const getTimestamp = () => { - if ('timestamp' in post) return post.timestamp; - return post.creation_date; - }; - - return ( -
-
-
{ e.stopPropagation(); }} - > - {/* Image Side */} -
- {getCaption() -
- - {/* Info Side */} -
- {/* Header */} -
- {username} - {username} - -
- - {/* Caption */} -
- {getCaption() !== null &&

{getCaption()}

} - {'location' in post && post.location !== null && ( -

- {post.location} -

- )} -

- {getTimestamp()} -

-
-
-
-
-
- ); -}; - -export default PostModal; \ No newline at end of file diff --git a/src/pages/ExplorePage.tsx b/src/pages/ExplorePage.tsx index 359a611..826d4f6 100644 --- a/src/pages/ExplorePage.tsx +++ b/src/pages/ExplorePage.tsx @@ -1,31 +1,13 @@ -import { useState } from 'react'; - import MobileBar from '../components/layout/MobileBar'; import SideBar from '../components/layout/SideBar'; import PostGrid from '../components/shared/PostGrid'; -import PostModal from '../components/shared/PostModal'; -import type { APIPost } from '../types/post'; const ExplorePage = () => { - const [selectedPost, setSelectedPost] = useState(null); - - // This is a placeholder. In a real app, you'd fetch this from an API - const dummyPostIds = [1, 2, 3, 4, 5, 6]; - return (
- - {selectedPost !== null && ( - { setSelectedPost(null); }} - /> - )} +
@@ -37,4 +19,4 @@ const ExplorePage = () => { ); }; -export default ExplorePage; \ No newline at end of file +export default ExplorePage; diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 89481e8..84fb5c2 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -1,5 +1,3 @@ -import { Settings } from 'lucide-react'; -import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import MobileBar from '../components/layout/MobileBar'; @@ -8,89 +6,30 @@ import SideBar from '../components/layout/SideBar'; import Highlights from '../components/profile/Highlights'; import ProfileInfo from '../components/profile/ProfileInfo'; import ProfileTabs from '../components/profile/ProfileTabs'; -import type { UserProfile } from '../types/user'; const ProfilePage = () => { const { username } = useParams(); - const [profile, setProfile] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchProfile = async () => { - if (username === undefined) return; - - try { - setIsLoading(true); - const token = localStorage.getItem('access_token'); - - if (token === null) { - throw new Error('No authentication token found'); - } - - const response = await fetch(`http://3.34.185.81:8000/api/user/${username}`, { - headers: { - 'Authorization': `Bearer ${token}`, - }, - }); - - if (!response.ok) { - throw new Error('Failed to fetch profile'); - } - - const data = await response.json() as UserProfile; - setProfile(data); - setError(null); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to load profile'); - console.error('Error fetching profile:', err); - } finally { - setIsLoading(false); - } - }; - - void fetchProfile(); - }, [username]); - - if (isLoading) { - return ( -
-
-
- ); - } - - if (error !== null || profile === null) { - return ( -
-
Error: {error ?? 'Profile not found'}
-
- ); - } + const profileData = { + username: username as string, + posts: 4, + followers: 100, + following: 100, + fullName: 'User1', + bio: 'User1 Bio', + }; return (
-
- - + + +
+

{profileData.fullName}

+

{profileData.bio}

- - +
@@ -102,4 +41,4 @@ const ProfilePage = () => { ); }; -export default ProfilePage; \ No newline at end of file +export default ProfilePage; diff --git a/src/types/post.ts b/src/types/post.ts index 02c409d..d0d8b47 100644 --- a/src/types/post.ts +++ b/src/types/post.ts @@ -1,13 +1,4 @@ -export interface APIPost { - post_id: number; - user_id: number; - post_text: string | null; - location: string | null; - creation_date: string; - file_url: string[]; -} - -export interface FeedPost { +export interface Post { id: number; username: string; imageUrl: string; @@ -18,8 +9,6 @@ export interface FeedPost { } export interface PostsProps { - posts: FeedPost[]; + posts: Post[]; postsPerPage: number; } - -export type Post = FeedPost; \ No newline at end of file diff --git a/src/types/user.ts b/src/types/user.ts index f5b6673..edf19a3 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -10,10 +10,8 @@ export interface UserProfile { birthday: string | null; introduce: string | null; website: string | null; - follower_count: number; - following_count: number; - followers: number[]; - following: number[]; + followers: number; + following: number; post_count: number; post_ids: number[]; -} \ No newline at end of file +}