- Timestamp:
- 02/01/26 17:29:49 (5 months ago)
- Branches:
- main
- Children:
- 0792d02
- Parents:
- 2730163
- Location:
- frontend/src
- Files:
-
- 4 edited
-
components/userProfile/ArtistView.tsx (modified) (5 diffs)
-
components/userProfile/ListenerView.tsx (modified) (7 diffs)
-
pages/UserDetail.tsx (modified) (4 diffs)
-
utils/types.ts (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/components/userProfile/ArtistView.tsx
r2730163 rd2af1a6 1 1 import { useNavigate } from "react-router-dom"; 2 import { Music, Disc3 } from "lucide-react"; 2 import { useState } from "react"; 3 import { Music, Disc3, Play, Plus } from "lucide-react"; 3 4 import type { ArtistContribution } from "../../utils/types"; 5 import axiosInstance from "../../api/axiosInstance"; 4 6 5 7 interface ArtistViewProps { … … 9 11 const ArtistView = ({ contributions }: ArtistViewProps) => { 10 12 const navigate = useNavigate(); 11 12 const albums = contributions.filter((c) => c.entityType === "ALBUM"); 13 const songs = contributions.filter((c) => c.entityType === "SONG"); 13 const [items, setItems] = useState(contributions); 14 const [toast, setToast] = useState<{ message: string; show: boolean }>({ 15 message: "", 16 show: false, 17 }); 18 19 const albums = items.filter((c) => c.entityType === "ALBUM"); 20 const songs = items.filter((c) => c.entityType === "SONG"); 14 21 15 22 const getRoleColor = (role: string) => { 16 23 const colors: { [key: string]: string } = { 17 COMPOSER: "bg-purple- 500",18 PERFORMER: "bg-blue- 500",19 PRODUCER: "bg-green- 500",20 MAIN_VOCAL: "bg-pink- 500",24 COMPOSER: "bg-purple-100 text-purple-700", 25 PERFORMER: "bg-blue-100 text-blue-700", 26 PRODUCER: "bg-green-100 text-green-700", 27 MAIN_VOCAL: "bg-pink-100 text-pink-700", 21 28 }; 22 return colors[role] || "bg-gray-500"; 29 return colors[role] || "bg-gray-100 text-gray-700"; 30 }; 31 32 const showToast = (message: string) => { 33 setToast({ message, show: true }); 34 setTimeout(() => { 35 setToast({ message: "", show: false }); 36 }, 2000); 37 }; 38 39 const handleLike = async (id: number, title: string) => { 40 try { 41 const response = await axiosInstance.post(`/musical-entity/${id}/like`); 42 const data = response.data; 43 44 setItems((prevItems) => 45 prevItems.map((item) => 46 item.id === data.entityId 47 ? { ...item, isLikedByCurrentUser: data.isLiked } 48 : item, 49 ), 50 ); 51 52 showToast( 53 data.isLiked ? `Liked "${title}"` : `Removed ${title} from likes`, 54 ); 55 } catch (err: any) { 56 showToast(err.response?.data?.error); 57 } 23 58 }; 24 59 25 60 return ( 26 61 <div className="mt-8"> 62 {toast.show && ( 63 <div className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 animate-fade-in-up"> 64 <div className="bg-gray-900 text-white px-6 py-3 rounded-full shadow-lg text-sm font-medium"> 65 {toast.message} 66 </div> 67 </div> 68 )} 69 27 70 {albums.length > 0 && ( 28 71 <div className="mb-12"> 29 72 <div className="flex items-center gap-3 mb-6"> 30 <Disc3 className="w- 8 h-8 text-purple-600" />31 <h2 className="text- 3xl font-bold">Albums</h2>73 <Disc3 className="w-6 h-6 text-gray-700" /> 74 <h2 className="text-2xl font-bold text-gray-800">Albums</h2> 32 75 </div> 33 76 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> 34 {albums.map((album , index) => (77 {albums.map((album) => ( 35 78 <div 36 key={ index}79 key={album.id} 37 80 className="group cursor-pointer" 38 onClick={() => 39 navigate(`/collection/album/${album.musicalEntityId}`) 40 } 81 onClick={() => navigate(`/collection/album/${album.id}`)} 41 82 > 42 <div className="relative aspect-square bg- linear-to-br from-purple-400 via-pink-400 to-blue-400 rounded-lg mb-3 overflow-hidden shadow-lg group-hover:shadow-2xl transition-all duration-300 group-hover:scale-105">83 <div className="relative aspect-square bg-gradient-to-br from-blue-400 to-purple-500 rounded-lg mb-3 overflow-hidden shadow-md group-hover:shadow-lg transition-all duration-300"> 43 84 <div className="absolute inset-0 flex items-center justify-center"> 44 <Disc3 className="w-20 h-20 text-white opacity-40" /> 45 </div> 46 <div className="absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/60 to-transparent p-4"> 85 <Disc3 className="w-16 h-16 text-white opacity-30" /> 86 </div> 87 88 <button 89 className="absolute top-2 right-2 p-2 bg-white/90 hover:bg-white rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100 cursor-pointer" 90 title={album.isLikedByCurrentUser ? "Unlike" : "Like"} 91 onClick={(e) => { 92 e.stopPropagation(); 93 handleLike(album.id, album.title); 94 }} 95 > 96 <svg 97 className="w-5 h-5" 98 fill={album.isLikedByCurrentUser ? "#ef4444" : "none"} 99 stroke={ 100 album.isLikedByCurrentUser ? "#ef4444" : "#6b7280" 101 } 102 strokeWidth="2" 103 viewBox="0 0 24 24" 104 > 105 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 106 </svg> 107 </button> 108 109 <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-3"> 47 110 <span 48 className={`text-xs px-2 py-1 rounded-full text-white${getRoleColor(album.role)}`}111 className={`text-xs px-2 py-1 rounded-full font-medium ${getRoleColor(album.role)}`} 49 112 > 50 113 {album.role.replace("_", " ")} … … 52 115 </div> 53 116 </div> 54 <h3 className="font-semibold text- base line-clamp-2group-hover:text-blue-600 transition-colors">117 <h3 className="font-semibold text-sm line-clamp-2 text-gray-900 group-hover:text-blue-600 transition-colors"> 55 118 {album.title} 56 119 </h3> … … 64 127 <div className="mb-12"> 65 128 <div className="flex items-center gap-3 mb-6"> 66 <Music className="w- 8 h-8 text-blue-600" />67 <h2 className="text- 3xl font-bold">Songs</h2>129 <Music className="w-6 h-6 text-gray-700" /> 130 <h2 className="text-2xl font-bold text-gray-800">Songs</h2> 68 131 </div> 69 132 <div className="space-y-2"> 70 {songs.map((song , index) => (133 {songs.map((song) => ( 71 134 <div 72 key={index} 73 className="group flex items-center gap-4 p-4 rounded-lg hover:bg-gray-50 transition-all cursor-pointer border border-transparent hover:border-gray-200" 74 onClick={() => 75 navigate(`/musical-entity/${song.musicalEntityId}`) 76 } 135 key={song.id} 136 className="group relative flex items-center gap-4 p-3 rounded-lg hover:bg-gray-50 transition-all cursor-pointer" 137 onClick={() => navigate(`/musical-entity/${song.id}`)} 77 138 > 78 <div className="shrink-0 w-14 h-14 bg-linear-to-br from-blue-400 to-cyan-400 rounded flex items-center justify-center group-hover:scale-110 transition-transform shadow-md"> 79 <Music className="w-7 h-7 text-white" /> 80 </div> 139 <div className="relative shrink-0"> 140 <div className="w-12 h-12 bg-gradient-to-br from-blue-400 to-purple-500 rounded flex items-center justify-center shadow-sm"> 141 <Music className="w-6 h-6 text-white" /> 142 </div> 143 <button 144 className="absolute inset-0 bg-black/60 rounded flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200" 145 aria-label="Play song" 146 > 147 <Play className="w-6 h-6 text-white fill-white" /> 148 </button> 149 </div> 150 81 151 <div className="flex-1 min-w-0"> 82 <h3 className="font-semibold text- lggroup-hover:text-blue-600 transition-colors truncate">152 <h3 className="font-semibold text-base text-gray-900 group-hover:text-blue-600 transition-colors truncate"> 83 153 {song.title} 84 154 </h3> 85 155 <div className="flex items-center gap-2 mt-1"> 86 {song.genre} 87 </div> 88 </div> 156 <span className="text-sm text-gray-600">{song.genre}</span> 157 </div> 158 </div> 159 89 160 <span 90 className={`px- 4 py-2 rounded-full text-white text-sm font-medium ${getRoleColor(song.role)} shadow-md`}161 className={`px-3 py-1 rounded-full text-sm font-medium ${getRoleColor(song.role)}`} 91 162 > 92 163 {song.role.replace("_", " ")} 93 164 </span> 165 166 <div className="flex items-center gap-2"> 167 <button 168 className="p-2 hover:bg-gray-200 rounded-full transition-colors duration-200 cursor-pointer" 169 title="Add to playlist" 170 > 171 <Plus className="w-5 h-5 text-gray-600" /> 172 </button> 173 174 <button 175 className="p-2 hover:bg-gray-200 rounded-full transition-colors duration-200 cursor-pointer" 176 title={song.isLikedByCurrentUser ? "Unlike" : "Like"} 177 onClick={(e) => { 178 e.stopPropagation(); 179 handleLike(song.id, song.title); 180 }} 181 > 182 <svg 183 className="w-5 h-5" 184 fill={song.isLikedByCurrentUser ? "#ef4444" : "none"} 185 stroke={song.isLikedByCurrentUser ? "#ef4444" : "#6b7280"} 186 strokeWidth="2" 187 viewBox="0 0 24 24" 188 > 189 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 190 </svg> 191 </button> 192 </div> 94 193 </div> 95 194 ))} … … 100 199 {contributions.length === 0 && ( 101 200 <div className="flex flex-col items-center justify-center py-16 text-gray-400"> 102 <Music className="w-2 4 h-24mb-4 opacity-20" />103 <p className="text- xlfont-medium">No contributions yet</p>201 <Music className="w-20 h-20 mb-4 opacity-20" /> 202 <p className="text-lg font-medium">No contributions yet</p> 104 203 <p className="text-sm mt-2">Start creating music to see it here</p> 105 204 </div> -
frontend/src/components/userProfile/ListenerView.tsx
r2730163 rd2af1a6 1 1 import { useNavigate } from "react-router-dom"; 2 import { Heart, ListMusic, Music, Album } from "lucide-react";2 import { Heart, ListMusic, Music, Album, Bookmark } from "lucide-react"; 3 3 import type { Playlist, MusicalEntity } from "../../utils/types"; 4 4 import axiosInstance from "../../api/axiosInstance"; 5 import { useState } from "react"; 5 6 interface ListenerViewProps { 6 7 likedEntities: MusicalEntity[] | []; … … 10 11 const ListenerView = ({ likedEntities, playlists }: ListenerViewProps) => { 11 12 const navigate = useNavigate(); 12 13 const likedSongs = likedEntities?.filter((e) => e.type === "SONG"); 14 const likedAlbums = likedEntities?.filter((e) => e.type === "ALBUM"); 13 const [items, setItems] = useState(likedEntities); 14 const [toast, setToast] = useState<{ message: string; show: boolean }>({ 15 message: "", 16 show: false, 17 }); 18 19 const showToast = (message: string) => { 20 setToast({ message, show: true }); 21 setTimeout(() => { 22 setToast({ message: "", show: false }); 23 }, 2000); 24 }; 25 26 const likedSongs = items.filter((e) => e.type === "SONG"); 27 const likedAlbums = items.filter((e) => e.type === "ALBUM"); 28 29 const handleSavePlaylist = (e: React.MouseEvent, playlistId: number) => { 30 e.stopPropagation(); 31 32 console.log("Save playlist:", playlistId); 33 }; 34 35 const handleLike = async (id: number, title: string) => { 36 try { 37 const response = await axiosInstance.post(`/musical-entity/${id}/like`); 38 const data = response.data; 39 40 setItems((prevItems) => 41 prevItems.map((item) => 42 item.id === data.entityId 43 ? { ...item, isLikedByCurrentUser: data.isLiked } 44 : item, 45 ), 46 ); 47 48 showToast( 49 data.isLiked ? `Liked "${title}"` : `Removed "${title}" from likes`, 50 ); 51 } catch (err: any) { 52 showToast(err.response?.data?.error); 53 } 54 }; 15 55 16 56 return ( 17 57 <div className="mt-8 space-y-12"> 58 {toast.show && ( 59 <div className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 animate-fade-in-up"> 60 <div className="bg-gray-900 text-white px-6 py-3 rounded-full shadow-lg text-sm font-medium"> 61 {toast.message} 62 </div> 63 </div> 64 )} 18 65 {playlists && playlists.length > 0 && ( 19 66 <section> 20 <div className="flex items-center gap- 2 mb-6 pb-2 border-b-2 border-gray-200">67 <div className="flex items-center gap-3 mb-6"> 21 68 <ListMusic className="w-6 h-6 text-gray-700" /> 22 69 <h3 className="text-2xl font-bold text-gray-800"> 23 70 Created Playlists 24 71 </h3> 25 <span className="text-sm text-gray-400 ml-1"> 26 ({playlists.length}) 27 </span> 28 </div> 29 30 <div className="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4"> 72 <span className="text-sm text-gray-500">({playlists.length})</span> 73 </div> 74 75 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> 31 76 {playlists.map((playlist) => ( 32 77 <div … … 35 80 onClick={() => navigate(`/collection/playlist/${playlist.id}`)} 36 81 > 37 <div className=" aspect-square rounded-md overflow-hidden bg-gray-100 mb-2 relative shadow-smgroup-hover:shadow-lg transition-all">82 <div className="relative aspect-square rounded-lg overflow-hidden bg-gray-100 mb-3 shadow-md group-hover:shadow-lg transition-all"> 38 83 {playlist.cover ? ( 39 84 <img 40 85 src={playlist.cover} 41 86 alt={playlist.name} 42 className="w-full h-full object-cover group-hover:opacity-90 transition-opacity"87 className="w-full h-full object-cover" 43 88 /> 44 89 ) : ( 45 <div className="w-full h-full bg-gradient-to-br from- gray-200 to-gray-300 flex items-center justify-center">46 <ListMusic className="w-1 0 h-10 text-gray-400" />90 <div className="w-full h-full bg-gradient-to-br from-blue-400 to-purple-500 flex items-center justify-center"> 91 <ListMusic className="w-16 h-16 text-white opacity-30" /> 47 92 </div> 48 93 )} 49 </div> 50 <p className="text-sm font-medium text-gray-800 truncate group-hover:text-blue-600 transition-colors"> 94 95 {/* Save button overlay */} 96 <button 97 onClick={(e) => handleSavePlaylist(e, playlist.id)} 98 className="absolute top-2 right-2 p-2 bg-white/90 hover:bg-white rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100" 99 title={playlist.isSavedByCurrentUser ? "Unsave" : "Save"} 100 > 101 <Bookmark 102 className={`w-5 h-5 ${ 103 playlist.isSavedByCurrentUser 104 ? "fill-blue-600 text-blue-600" 105 : "text-gray-600" 106 }`} 107 /> 108 </button> 109 </div> 110 <p className="text-sm font-semibold text-gray-900 truncate group-hover:text-blue-600 transition-colors"> 51 111 {playlist.name} 112 </p> 113 <p className="text-xs text-gray-500 truncate"> 114 {playlist.creatorName} 52 115 </p> 53 116 </div> … … 59 122 {likedSongs && likedSongs.length > 0 && ( 60 123 <section> 61 <div className="flex items-center gap- 2 mb-6 pb-2 border-b-2 border-gray-200">124 <div className="flex items-center gap-3 mb-6"> 62 125 <Heart className="w-6 h-6 text-red-500 fill-red-500" /> 63 126 <h3 className="text-2xl font-bold text-gray-800">Liked Songs</h3> 64 <span className="text-sm text-gray- 400 ml-1">127 <span className="text-sm text-gray-500"> 65 128 ({likedSongs?.length}) 66 129 </span> 67 130 </div> 68 131 69 <div className="space-y- 1">132 <div className="space-y-2"> 70 133 {likedSongs.map((song, index) => ( 71 134 <div 72 135 key={song.id} 73 className="flex items-center gap-4 p-3 rounded hover:bg-gray-50 cursor-pointer group transition-colors"136 className="flex items-center gap-4 p-3 rounded-lg hover:bg-gray-50 cursor-pointer group transition-colors" 74 137 onClick={() => navigate(`/musical-entity/${song.id}`)} 75 138 > 76 <span className="text- sm text-gray-400 w-8 text-right font-medium">139 <span className="text-gray-500 font-medium w-8 text-center"> 77 140 {index + 1} 78 141 </span> 79 <div className="w-12 h-12 rounded bg-gradient-to-br from- red-100 to-pink-100 flex items-center justify-center flex-shrink-0">80 <Music className="w-6 h-6 text- red-600" />142 <div className="w-12 h-12 rounded bg-gradient-to-br from-blue-400 to-purple-500 flex items-center justify-center flex-shrink-0 shadow-sm"> 143 <Music className="w-6 h-6 text-white" /> 81 144 </div> 82 145 <div className="flex-1 min-w-0"> 83 <p className="font- medium text-gray-800 truncate group-hover:text-blue-600 transition-colors">146 <p className="font-semibold text-gray-900 truncate group-hover:text-blue-600 transition-colors"> 84 147 {song.title} 85 148 </p> 86 <p className="text-xs text-gray-500">{song.genre}</p> 87 </div> 149 <p className="text-sm text-gray-600 truncate"> 150 {song.releasedBy} 151 </p> 152 </div> 153 <span className="text-sm text-gray-600 px-3 py-1 bg-gray-100 rounded-full"> 154 {song.genre} 155 </span> 156 157 <button 158 onClick={(e) => { 159 e.stopPropagation(); 160 handleLike(song.id, song.title); 161 }} 162 className="p-2 hover:bg-gray-200 rounded-full transition-colors duration-200 cursor-pointer" 163 title={song.isLikedByCurrentUser ? "Unlike" : "Like"} 164 > 165 <svg 166 className="w-5 h-5" 167 fill={song.isLikedByCurrentUser ? "#ef4444" : "none"} 168 stroke={song.isLikedByCurrentUser ? "#ef4444" : "#6b7280"} 169 strokeWidth="2" 170 viewBox="0 0 24 24" 171 > 172 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 173 </svg> 174 </button> 88 175 </div> 89 176 ))} … … 94 181 {likedAlbums && likedAlbums.length > 0 && ( 95 182 <section> 96 <div className="flex items-center gap- 2 mb-6 pb-2 border-b-2 border-gray-200">97 <Album className="w-6 h-6 text- blue-600" />183 <div className="flex items-center gap-3 mb-6"> 184 <Album className="w-6 h-6 text-gray-700" /> 98 185 <h3 className="text-2xl font-bold text-gray-800">Liked Albums</h3> 99 <span className="text-sm text-gray- 400 ml-1">186 <span className="text-sm text-gray-500"> 100 187 ({likedAlbums.length}) 101 188 </span> 102 189 </div> 103 190 104 <div className="grid grid-cols- 3 md:grid-cols-4 lg:grid-cols-5 gap-5">191 <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> 105 192 {likedAlbums.map((album) => ( 106 193 <div … … 109 196 onClick={() => navigate(`/collection/album/${album.id}`)} 110 197 > 111 <div className="aspect-square rounded-lg overflow-hidden bg-gradient-to-br from-blue-100 to-indigo-100 mb-3 flex items-center justify-center shadow-sm group-hover:shadow-md transition-all"> 112 <Album className="w-16 h-16 text-blue-600 opacity-60" /> 113 </div> 114 <p className="font-medium text-sm text-gray-800 truncate group-hover:text-blue-600 transition-colors"> 198 <div className="relative aspect-square rounded-lg overflow-hidden bg-gradient-to-br from-blue-400 to-purple-500 mb-3 flex items-center justify-center shadow-md group-hover:shadow-lg transition-all"> 199 <Album className="w-16 h-16 text-white opacity-30" /> 200 201 <button 202 onClick={(e) => { 203 e.stopPropagation(); 204 handleLike(album.id, album.title); 205 }} 206 className="absolute top-2 right-2 p-2 bg-white/90 hover:bg-white rounded-full shadow-md transition-all duration-200 opacity-0 group-hover:opacity-100 cursor-pointer" 207 title={album.isLikedByCurrentUser ? "Unlike" : "Like"} 208 > 209 <svg 210 className="w-5 h-5" 211 fill={album.isLikedByCurrentUser ? "#ef4444" : "none"} 212 stroke={ 213 album.isLikedByCurrentUser ? "#ef4444" : "#6b7280" 214 } 215 strokeWidth="2" 216 viewBox="0 0 24 24" 217 > 218 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 219 </svg> 220 </button> 221 </div> 222 <p className="font-semibold text-sm text-gray-900 truncate group-hover:text-blue-600 transition-colors"> 115 223 {album.title} 116 224 </p> 117 <p className="text-xs text-gray-500 mt-0.5">{album.genre}</p> 225 <p className="text-xs text-gray-600 truncate mt-1"> 226 {album.releasedBy} 227 </p> 118 228 </div> 119 229 ))} … … 125 235 likedEntities.length === 0 && 126 236 (!playlists || playlists.length === 0) && ( 127 <div className="flex flex-col items-center justify-center py-16 text-gray-300"> 128 <p className="text-lg font-medium text-gray-400"> 129 Nothing here yet 130 </p> 131 <p className="text-sm text-gray-400 mt-1"> 237 <div className="flex flex-col items-center justify-center py-16 text-gray-400"> 238 <Music className="w-20 h-20 mb-4 opacity-20" /> 239 <p className="text-lg font-medium">Nothing here yet</p> 240 <p className="text-sm mt-2"> 132 241 Start exploring music to build your collection 133 242 </p> -
frontend/src/pages/UserDetail.tsx
r2730163 rd2af1a6 13 13 } from "../utils/types"; 14 14 15 interface FollowStatus { 16 isFollowing: boolean; 17 followerCount: number; 18 followingCount: number; 19 } 20 15 21 interface Artist extends BaseNonAdminUser { 16 22 userType: "ARTIST"; … … 43 49 setIsFollowing(true); 44 50 try { 45 const response = await axiosInstance.post< UserProfile>(51 const response = await axiosInstance.post<FollowStatus>( 46 52 `/users/${userId}/follow`, 47 53 ); 48 setUser(response.data); 54 setUser((prev) => { 55 if (!prev) return null; 56 return { 57 ...prev, 58 isFollowedByCurrentUser: response.data.isFollowing, 59 followers: response.data.followerCount, 60 following: response.data.followingCount, 61 }; 62 }); 49 63 } catch (err: any) { 50 64 setError(handleError(err)); 51 65 } finally { 52 66 setIsFollowing(false); 67 } 68 }; 69 70 const handleFollowInModal = async (targetId: number) => { 71 try { 72 const response = await axiosInstance.post<FollowStatus>( 73 `/users/${targetId}/follow`, 74 ); 75 76 setModalUsers((prevUsers) => 77 prevUsers.map((u) => 78 u.id === targetId 79 ? { ...u, isFollowedByCurrentUser: response.data.isFollowing } 80 : u, 81 ), 82 ); 83 84 // if (user && user.id === targetId) { 85 // setUser((prev) => { 86 // if (!prev) return null; 87 // return { 88 // ...prev, 89 // isFollowedByCurrentUser: response.data.isFollowing, 90 // followers: response.data.followerCount, 91 // }; 92 // }); 93 // } 94 } catch (err: any) { 95 setError(handleError(err)); 53 96 } 54 97 }; … … 81 124 }; 82 125 83 const handleFollowInModal = async (targetId: number) => {84 try {85 await axiosInstance.post(`/users/${targetId}/follow`);86 setModalUsers((prevUsers) =>87 prevUsers.map((u) => {88 if (u.id === targetId) {89 const isNowFollowing = !u.isFollowedByCurrentUser;90 return {91 ...u,92 isFollowedByCurrentUser: isNowFollowing,93 };94 }95 return u;96 }),97 );98 99 // if (user && user.id === targetId) {100 // const response = await axiosInstance.get(`/users/${targetId}`);101 // setUser(response.data);102 // }103 } catch (err: any) {104 setError(handleError(err));105 }106 };107 108 126 useEffect(() => { 109 127 const fetchUser = async () => { … … 111 129 try { 112 130 const response = await axiosInstance.get(`/users/${userId}`); 131 console.log(response.data); 113 132 setUser(response.data); 114 133 } catch (err: any) { -
frontend/src/utils/types.ts
r2730163 rd2af1a6 8 8 9 9 export interface ArtistContribution { 10 musicalEntityId: number;10 id: number; 11 11 title: string; 12 12 genre: string; … … 40 40 creatorName: string; 41 41 songsInPlaylist: Song[]; 42 isSavedByCurrentUser: boolean; 42 43 } 43 44
Note:
See TracChangeset
for help on using the changeset viewer.
