11import { useState , useEffect } from 'react' ;
22import { useNavigate , useParams } from 'react-router-dom' ;
3+ import { useRecoilValue } from 'recoil' ;
34
45import theme from '@styles/theme' ;
5-
6+ import { OtherUserAtom } from '@recoil/util/OtherUser' ;
7+ import { getCurrentUserId } from '@utils/getCurrentUserId' ;
68import { createMatchingApi } from '@apis/matching' ;
79import { getUserPostListApi } from '@apis/post' ;
810import { getUserInfoApi } from '@apis/user' ;
@@ -23,165 +25,166 @@ import { StyledText } from '@components/Text/StyledText';
2325import TopBar from '@components/TopBar' ;
2426import UserProfile from '@components/UserProfile' ;
2527
26- import type { UserPostSummary } from '@apis/post/dto' ; // type 명시
27- import type { UserInfoData } from '@apis/user/dto' ; // type 명시
28+ import type { UserPostSummary } from '@apis/post/dto' ;
29+ import type { UserInfoData } from '@apis/user/dto' ;
2830
29- import ButtonSecondary from './ButtonSecondary/index' ; // 상대 경로 index 명시
30- import NavbarProfile from './NavbarProfile/index' ; // 상대 경로 index 명시
31+ import ButtonSecondary from './ButtonSecondary/index' ;
32+ import NavbarProfile from './NavbarProfile/index' ;
3133
3234import {
33- ProfileContainer ,
34- Header ,
35- StatsContainer ,
36- Stat ,
37- StatNumber ,
38- StatLabel ,
39- PostsContainer ,
40- AddButton ,
41- NoPostWrapper ,
42- Button ,
35+ ProfileContainer ,
36+ Header ,
37+ StatsContainer ,
38+ Stat ,
39+ StatNumber ,
40+ StatLabel ,
41+ PostsContainer ,
42+ AddButton ,
43+ NoPostWrapper ,
44+ Button ,
4345} from './styles' ;
4446
4547const Profile : React . FC = ( ) => {
46- const { userId } = useParams < { userId : string } > ( ) ;
47- const profileUserId = Number ( userId ) ;
48- const loggedInUserId = Number ( localStorage . getItem ( 'current_user_id' ) ) ;
49-
50- const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
51- const [ posts , setPosts ] = useState < UserPostSummary [ ] > ( [ ] ) ;
52- const [ totalPosts , setTotalPosts ] = useState < number > ( 0 ) ;
53- const [ userInfo , setUserInfo ] = useState < UserInfoData | null > ( null ) ;
54- const [ isBottomSheetOpen , setIsBottomSheetOpen ] = useState ( false ) ;
55- const [ isOptionsBottomSheetOpen , setIsOptionsBottomSheetOpen ] = useState ( false ) ; // 추가
56- const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
57- const [ modalContent , setModalContent ] = useState ( '' ) ;
58- const navigate = useNavigate ( ) ;
59-
60- const isMyPage = loggedInUserId === profileUserId ;
61-
62- useEffect ( ( ) => {
63- const fetchData = async ( ) => {
64- try {
65- const response = await getUserInfoApi ( profileUserId ) ;
66- const postResponse = await getUserPostListApi ( 1 , 10 , profileUserId ) ;
67- setUserInfo ( response . data ) ;
68- setPosts ( postResponse . data . post ) ;
69- setTotalPosts ( postResponse . data . totalPostsCount ) ;
70- } catch ( error ) {
71- console . error ( '데이터 가져오기 실패:' , error ) ;
72- } finally {
73- setIsLoading ( false ) ;
74- }
75- } ;
76- fetchData ( ) ;
77- } , [ profileUserId ] ) ;
78-
79- const createMatching = async ( message : string ) => {
80- const matchingRequestData = {
81- requesterId : loggedInUserId ,
82- targetId : profileUserId ,
83- message : message ,
84- } ;
85-
86- try {
87- await createMatchingApi ( matchingRequestData ) ;
88- handleModalOpen ( `${ userInfo ?. nickname } 님에게 대표 OOTD와 \n한 줄 메세지를 보냈어요!` ) ;
89- } catch ( error ) {
90- console . error ( '매칭 신청 오류:' , error ) ;
91- handleModalOpen ( '매칭 신청에 실패했습니다.' ) ;
92- }
93- } ;
94-
95- const handleModalOpen = ( message : string ) => {
96- setIsBottomSheetOpen ( false ) ;
97- setModalContent ( message ) ;
98- setIsModalOpen ( true ) ;
99- } ;
100-
101- if ( isLoading ) return < Loading /> ;
102-
103- return (
104- < OODDFrame >
105- < ProfileContainer >
106- { isMyPage && (
107- < AddButton onClick = { ( ) => navigate ( '/post/upload/photo/select' ) } >
108- < img src = { button_plus } alt = "Add" />
109- </ AddButton >
110- ) }
111-
112- { isMyPage ? (
113- < NavbarProfile />
114- ) : (
115- < TopBar
116- RightButtonSrc = { MoreSvg }
117- LeftButtonSrc = { BackSvg }
118- onClickLeftButton = { ( ) => navigate ( - 1 ) }
119- onClickRightButton = { ( ) => setIsOptionsBottomSheetOpen ( true ) }
120- />
121- ) }
122-
123- < Header >
124- < UserProfile
125- userImg = { userInfo ?. profilePictureUrl || imageBasic }
126- nickname = { userInfo ?. nickname || '알수없음' }
127- bio = { userInfo ?. bio || '소개글이 없습니다.' }
128- />
129- </ Header >
130-
131- { isMyPage ? < ButtonSecondary /> : < Button onClick = { ( ) => setIsBottomSheetOpen ( true ) } > 매칭신청</ Button > }
132-
133- < StatsContainer >
134- < Stat >
135- < StatLabel > OOTD</ StatLabel >
136- < StatNumber > { totalPosts || 0 } </ StatNumber >
137- </ Stat >
138- { isMyPage && (
139- < Stat >
140- < StatLabel > 코멘트</ StatLabel >
141- < StatNumber > { posts . reduce ( ( sum , post ) => sum + ( post . postCommentsCount || 0 ) , 0 ) } </ StatNumber >
142- </ Stat >
143- ) }
144- < Stat >
145- < StatLabel > 좋아요</ StatLabel >
146- < StatNumber > { posts . reduce ( ( sum , post ) => sum + ( post . postLikesCount || 0 ) , 0 ) } </ StatNumber >
147- </ Stat >
148- </ StatsContainer >
149-
150- < PostsContainer >
151- { posts . length > 0 ? (
152- posts . map ( ( post ) => < PostItem key = { post . id } post = { post } /> )
153- ) : (
154- < NoPostWrapper >
155- < StyledText $textTheme = { { style : 'headline1-medium' } } color = { theme . colors . gray [ 400 ] } >
156- 게시물이 없어요.
157- </ StyledText >
158- </ NoPostWrapper >
159- ) }
160- </ PostsContainer >
161-
162- { isMyPage && < NavBar /> }
163-
164- < CommentBottomSheet
165- isBottomSheetOpen = { isBottomSheetOpen }
166- commentProps = { {
167- content : `${ userInfo ?. nickname } 님에게 대표 OOTD와 함께 전달될\n 한 줄 메세지를 보내보세요!` ,
168- sendComment : createMatching ,
169- } }
170- handleCloseBottomSheet = { ( ) => setIsBottomSheetOpen ( false ) }
171- />
172-
173- < OptionsBottomSheet
174- domain = "user"
175- targetId = { { userId : profileUserId || - 1 } }
176- targetNickname = { userInfo ?. nickname || '알 수 없음' }
177- isBottomSheetOpen = { isOptionsBottomSheetOpen }
178- onClose = { ( ) => setIsOptionsBottomSheetOpen ( false ) }
179- />
180-
181- { isModalOpen && < Modal content = { modalContent } onClose = { ( ) => setIsModalOpen ( false ) } /> }
182- </ ProfileContainer >
183- </ OODDFrame >
184- ) ;
48+ const { userId } = useParams < { userId : string } > ( ) ;
49+ const profileUserId = Number ( userId ) ;
50+ const loggedInUserId = getCurrentUserId ( ) ;
51+ const otherUser = useRecoilValue ( OtherUserAtom ) ;
52+
53+ const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
54+ const [ posts , setPosts ] = useState < UserPostSummary [ ] > ( [ ] ) ;
55+ const [ totalPosts , setTotalPosts ] = useState < number > ( 0 ) ;
56+ const [ userInfo , setUserInfo ] = useState < UserInfoData | null > ( null ) ;
57+ const [ isBottomSheetOpen , setIsBottomSheetOpen ] = useState ( false ) ;
58+ const [ isOptionsBottomSheetOpen , setIsOptionsBottomSheetOpen ] = useState ( false ) ;
59+ const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
60+ const [ modalContent , setModalContent ] = useState ( '' ) ;
61+ const navigate = useNavigate ( ) ;
62+
63+ const isMyPage = loggedInUserId === profileUserId ;
64+
65+ useEffect ( ( ) => {
66+ const fetchData = async ( ) => {
67+ try {
68+ const response = await getUserInfoApi ( profileUserId ) ;
69+ const postResponse = await getUserPostListApi ( 1 , 10 , profileUserId ) ;
70+ setUserInfo ( response . data ) ;
71+ setPosts ( postResponse . data . post ) ;
72+ setTotalPosts ( postResponse . data . totalPostsCount ) ;
73+ } catch ( error ) {
74+ console . error ( '데이터 가져오기 실패:' , error ) ;
75+ } finally {
76+ setIsLoading ( false ) ;
77+ }
78+ } ;
79+ fetchData ( ) ;
80+ } , [ profileUserId ] ) ;
81+
82+ const createMatching = async ( message : string ) => {
83+ const matchingRequestData = {
84+ requesterId : loggedInUserId ,
85+ targetId : otherUser ?. id || profileUserId ,
86+ message : message ,
87+ } ;
88+
89+ try {
90+ await createMatchingApi ( matchingRequestData ) ;
91+ handleModalOpen ( `${ userInfo ?. nickname } 님에게 대표 OOTD와 \n한 줄 메세지를 보냈어요!` ) ;
92+ } catch ( error ) {
93+ console . error ( '매칭 신청 오류:' , error ) ;
94+ handleModalOpen ( '매칭 신청에 실패했습니다.' ) ;
95+ }
96+ } ;
97+
98+ const handleModalOpen = ( message : string ) => {
99+ setIsBottomSheetOpen ( false ) ;
100+ setModalContent ( message ) ;
101+ setIsModalOpen ( true ) ;
102+ } ;
103+
104+ if ( isLoading ) return < Loading /> ;
105+
106+ return (
107+ < OODDFrame >
108+ < ProfileContainer >
109+ { isMyPage && (
110+ < AddButton onClick = { ( ) => navigate ( '/post/upload/photo/select' ) } >
111+ < img src = { button_plus } alt = "Add" />
112+ </ AddButton >
113+ ) }
114+
115+ { isMyPage ? (
116+ < NavbarProfile />
117+ ) : (
118+ < TopBar
119+ RightButtonSrc = { MoreSvg }
120+ LeftButtonSrc = { BackSvg }
121+ onClickLeftButton = { ( ) => navigate ( - 1 ) }
122+ onClickRightButton = { ( ) => setIsOptionsBottomSheetOpen ( true ) }
123+ />
124+ ) }
125+
126+ < Header >
127+ < UserProfile
128+ userImg = { userInfo ?. profilePictureUrl || imageBasic }
129+ nickname = { userInfo ?. nickname || '알수없음' }
130+ bio = { userInfo ?. bio || '소개글이 없습니다.' }
131+ />
132+ </ Header >
133+
134+ { isMyPage ? < ButtonSecondary /> : < Button onClick = { ( ) => setIsBottomSheetOpen ( true ) } > 매칭신청</ Button > }
135+
136+ < StatsContainer >
137+ < Stat >
138+ < StatLabel > OOTD</ StatLabel >
139+ < StatNumber > { totalPosts || 0 } </ StatNumber >
140+ </ Stat >
141+ { isMyPage && (
142+ < Stat >
143+ < StatLabel > 코멘트</ StatLabel >
144+ < StatNumber > { posts . reduce ( ( sum , post ) => sum + ( post . postCommentsCount || 0 ) , 0 ) } </ StatNumber >
145+ </ Stat >
146+ ) }
147+ < Stat >
148+ < StatLabel > 좋아요</ StatLabel >
149+ < StatNumber > { posts . reduce ( ( sum , post ) => sum + ( post . postLikesCount || 0 ) , 0 ) } </ StatNumber >
150+ </ Stat >
151+ </ StatsContainer >
152+
153+ < PostsContainer >
154+ { posts . length > 0 ? (
155+ posts . map ( ( post ) => < PostItem key = { post . id } post = { post } /> )
156+ ) : (
157+ < NoPostWrapper >
158+ < StyledText $textTheme = { { style : 'headline1-medium' } } color = { theme . colors . gray [ 400 ] } >
159+ 게시물이 없어요.
160+ </ StyledText >
161+ </ NoPostWrapper >
162+ ) }
163+ </ PostsContainer >
164+
165+ { isMyPage && < NavBar /> }
166+
167+ < CommentBottomSheet
168+ isBottomSheetOpen = { isBottomSheetOpen }
169+ commentProps = { {
170+ content : `${ userInfo ?. nickname } 님에게 대표 OOTD와 함께 전달될\n 한 줄 메세지를 보내보세요!` ,
171+ sendComment : createMatching ,
172+ } }
173+ handleCloseBottomSheet = { ( ) => setIsBottomSheetOpen ( false ) }
174+ />
175+
176+ < OptionsBottomSheet
177+ domain = "user"
178+ targetId = { { userId : profileUserId || - 1 } }
179+ targetNickname = { userInfo ?. nickname || '알 수 없음' }
180+ isBottomSheetOpen = { isOptionsBottomSheetOpen }
181+ onClose = { ( ) => setIsOptionsBottomSheetOpen ( false ) }
182+ />
183+
184+ { isModalOpen && < Modal content = { modalContent } onClose = { ( ) => setIsModalOpen ( false ) } /> }
185+ </ ProfileContainer >
186+ </ OODDFrame >
187+ ) ;
185188} ;
186189
187- export default Profile ;
190+ export default Profile ;
0 commit comments