44 Plus ,
55 ThumbsUp ,
66 UserRound ,
7+ CirclePlus ,
8+ LogOut ,
79 Star ,
810} from "lucide-react" ;
911import { AnimatePresence , motion } from "framer-motion" ;
@@ -63,6 +65,8 @@ const ProjectDetailPage = () => {
6365 const [ likeStatus , setLiked ] = useState ( false ) ;
6466 const [ userUuid , setUserUuid ] = useState ( null ) ;
6567 const [ cooldown , setCooldown ] = useState ( false ) ;
68+ const [ isMember , setIsMember ] = useState ( false ) ;
69+ const [ isReady , setIsReady ] = useState ( false ) ;
6670
6771 useEffect ( ( ) => {
6872 const fetchUserUuid = async ( ) => {
@@ -102,7 +106,14 @@ const ProjectDetailPage = () => {
102106 } ;
103107
104108 loadProjectInfo ( ) ;
105- } , [ id ] ) ;
109+ } , [ id , userUuid ] ) ;
110+
111+ useEffect ( ( ) => {
112+ if ( userData && project ) {
113+ setIsMember ( project . members . includes ( userData . id ) ) ;
114+ setIsReady ( true ) ;
115+ }
116+ } , [ userData , project ] ) ;
106117
107118 const handleFollow = async ( ) => {
108119 setFollowing ( ! following ) ;
@@ -125,6 +136,62 @@ const ProjectDetailPage = () => {
125136 }
126137 } ;
127138
139+ const handleJoin = async ( ) => {
140+ const confirmed = window . confirm (
141+ "Are you sure you want to join this project?"
142+ ) ;
143+ if ( ! confirmed ) return ;
144+ try {
145+ const response = await fetch (
146+ `${ SERVER_HOST } /projects/member/manage/${ id } ` ,
147+ {
148+ method : "PATCH" ,
149+ headers : {
150+ "Content-Type" : "application/json" ,
151+ } ,
152+ body : JSON . stringify ( {
153+ user_id : userData . id ,
154+ action : "join" ,
155+ } ) ,
156+ }
157+ ) ;
158+
159+ if ( response . ok ) {
160+ setIsMember ( true ) ;
161+ }
162+ } catch ( error ) {
163+ console . error ( "Error joining project:" , error ) ;
164+ }
165+ } ;
166+
167+ const handleLeave = async ( ) => {
168+ const confirmed = window . confirm (
169+ "Are you sure you want to leave this project?"
170+ ) ;
171+ if ( ! confirmed ) return ;
172+ try {
173+ const response = await fetch (
174+ `${ SERVER_HOST } /projects/member/manage/${ id } ` ,
175+ {
176+ method : "PATCH" ,
177+ headers : {
178+ "Content-Type" : "application/json" ,
179+ } ,
180+ body : JSON . stringify ( {
181+ user_id : userData . id ,
182+ action : "leave" ,
183+ } ) ,
184+ }
185+ ) ;
186+
187+ if ( response . ok ) {
188+ setIsMember ( false ) ;
189+ }
190+ } catch ( error ) {
191+ console . error ( "Error leaving project:" , error ) ;
192+ }
193+ } ;
194+
128195 const ai_feedback = async ( ) => {
129196 if ( cooldown ) return ;
130197 setCooldown ( true ) ;
@@ -463,6 +530,38 @@ const ProjectDetailPage = () => {
463530 </ Button >
464531 < span > { project . followers_count } Followers</ span >
465532 </ div >
533+
534+ < div className = "flex items-center gap-2" >
535+ { isReady && userUuid !== project . owner && (
536+ < >
537+ < Button
538+ variant = "ghost"
539+ size = "icon"
540+ onClick = { isMember ? handleLeave : handleJoin }
541+ className = {
542+ isMember
543+ ? "bg-red-500 hover:bg-red-100"
544+ : "bg-green-500 hover:bg-green-600"
545+ }
546+ >
547+ { isMember ? (
548+ < LogOut className = "h-6 w-6" />
549+ ) : (
550+ < CirclePlus className = "h-6 w-6" />
551+ ) }
552+ </ Button >
553+ < span
554+ className = {
555+ isMember
556+ ? "text-red-600 font-bold"
557+ : "text-green-500 font-bold"
558+ }
559+ >
560+ { isMember ? "Leave" : "Join" }
561+ </ span >
562+ </ >
563+ ) }
564+ </ div >
466565 </ div >
467566 </ div >
468567 </ div >
0 commit comments