Changeset 2730163


Ignore:
Timestamp:
01/31/26 22:34:40 (5 months ago)
Author:
Dimitar Arsov <dimitararsov04@…>
Branches:
main
Children:
d2af1a6
Parents:
2b08bed
Message:

show songs in playlists/albums

Files:
6 added
18 edited
1 moved

Legend:

Unmodified
Added
Removed
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/GlobalExceptionHandler.java

    r2b08bed r2730163  
    33import java.util.Map;
    44
     5import com.ukim.finki.develop.finkwave.exceptions.AlbumNotFoundException;
    56import com.ukim.finki.develop.finkwave.exceptions.FollowException;
    67import com.ukim.finki.develop.finkwave.exceptions.UserNotFoundException;
     
    4950    }
    5051
     52    @ExceptionHandler(AlbumNotFoundException.class)
     53    public ResponseEntity<Map<String, String>> handleAlbumNotFound(AlbumNotFoundException ex) {
     54        return ResponseEntity
     55                .status(HttpStatus.BAD_REQUEST)
     56                .body(Map.of("error", ex.getMessage()));
     57    }
     58
    5159
    5260
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/controller/NonAdminUserController.java

    r2b08bed r2730163  
    3535    }
    3636
    37     @GetMapping("/followers/{id}")
     37    @GetMapping("/{id}/followers")
    3838    public HttpEntity<List<NonAdminUserDto>>getFollowersForUser(@PathVariable Long id){
    3939        return ResponseEntity.ok(followService.getFollowersForUser(id));
    4040    }
    4141
    42     @GetMapping("/following/{id}")
     42    @GetMapping("/{id}/following")
    4343    public HttpEntity<List<NonAdminUserDto>>getFollowingForUser(@PathVariable Long id){
    4444        return ResponseEntity.ok(followService.getFollowingForUser(id));
    4545    }
    4646
    47     @PostMapping("/follow/{id}")
     47    @PostMapping("/{id}/follow")
    4848    public HttpEntity<NonAdminUserDto>followUser(@PathVariable Long id){
    4949        followService.toggleFollow(id);
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/ArtistContributionDto.java

    r2b08bed r2730163  
    1414    private String role;
    1515    private String entityType;
     16    private Boolean isLikedByCurrentUser;
    1617}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/PlaylistDto.java

    r2b08bed r2730163  
    44import lombok.Getter;
    55import lombok.Setter;
     6
     7import java.util.List;
    68
    79@AllArgsConstructor
     
    1416    private String creatorName;
    1517
     18    private List<MusicalEntityDto>songsInPlaylist;
     19
    1620}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/ArtistContributionRepository.java

    r2b08bed r2730163  
    1717            "ac.musicalEntity.title," +
    1818            "ac.musicalEntity.genre," +
    19             "ac.role, "+
    20         "CASE WHEN s.id IS NOT NULL THEN 'SONG' "+
    21         "WHEN a.id IS NOT NULL THEN 'ALBUM' "+
    22         "ELSE 'Unknown' END) "+
    23         "FROM ArtistContribution ac "+
    24         "LEFT JOIN Song s  ON s.musicalEntities.id=ac.musicalEntity.id "+
    25         "LEFT JOIN Album a ON a.musicalEntities.id=ac.musicalEntity.id "+
    26         "WHERE ac.artist.id=:artistId")
    27     List<ArtistContributionDto>findContributionsByArtistId(@Param("artistId")Long artistId);
     19            "ac.role, " +
     20            "CASE WHEN s.id IS NOT NULL THEN 'SONG' " +
     21            "WHEN a.id IS NOT NULL THEN 'ALBUM' " +
     22            "ELSE 'Unknown' END," +
     23            "(CASE WHEN l.id IS NOT NULL THEN true ELSE false END)) " +
     24            "FROM ArtistContribution ac " +
     25            "LEFT JOIN Song s  ON s.musicalEntities.id=ac.musicalEntity.id " +
     26            "LEFT JOIN Album a ON a.musicalEntities.id=ac.musicalEntity.id " +
     27            "LEFT JOIN Like l ON l.musicalEntity.id=ac.musicalEntity.id AND l.listener.id=:currentUserId " +
     28            "WHERE ac.artist.id=:artistId")
     29    List<ArtistContributionDto>findContributionsByArtistId(@Param("currentUserId")Long currentUserId,@Param("artistId")Long artistId);
    2830}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/FollowRepository.java

    r2b08bed r2730163  
    2525    boolean isFollowing(@Param("currentUserId") Long currentUSerId,@Param("selectedUserId")Long selectedUserId);
    2626
    27     List<Follow>getFollowsByFollowee_Id(Long id);
    2827
    2928    List<Follow>getFollowsByFollower_Id(Long id);
    3029
     30    @Query("SELECT f FROM Follow f " +
     31            "JOIN FETCH f.follower nu " +
     32            "JOIN FETCH nu.user " +
     33            "WHERE f.followee.id = :id")
     34    List<Follow> findFollowersWithProfile(@Param("id") Long id);
     35
     36    @Query("SELECT f FROM Follow f " +
     37            "JOIN FETCH f.followee nu " +
     38            "JOIN FETCH nu.user " +
     39            "WHERE f.follower.id = :id")
     40    List<Follow> findFollowingWithProfile(@Param("id") Long id);
     41
    3142}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/NonAdminUserRepository.java

    r2b08bed r2730163  
    1616
    1717    @Query("SELECT u FROM NonAdminUser u " +
    18             "WHERE LOWER(u.user.fullName) LIKE LOWER(CONCAT('%', :name, '%') )"+
    19             "OR  LOWER(u.user.username) LIKE LOWER(CONCAT('%', :name, '%' )) ")
     18            "WHERE u.user.fullName ILIKE CONCAT('%', :name, '%' ) "+
     19            "OR  u.user.username ILIKE CONCAT('%', :name, '%' ) ")
    2020    List<NonAdminUser> searchByName(String name);
    2121
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/SongRepository.java

    r2b08bed r2730163  
    1515
    1616
    17     @Query("SELECT NEW com.ukim.finki.develop.finkwave.model.dto.MusicalEntityDto( "+
     17    @Query("SELECT NEW com.ukim.finki.develop.finkwave.model.dto.MusicalEntityDto(" +
    1818            "s.id, " +
    1919            "me.title, " +
    2020            "me.genre, " +
    21             "'SONG'," +
     21            "'SONG', " +
    2222            "u.fullName, " +
    23             "(CASE WHEN l.id IS NOT NULL THEN true ELSE false END)) "+
    24             "FROM Song s "+
    25             "JOIN MusicalEntity  me on me.id=s.musicalEntities.id "+
    26             "JOIN User u on u.id=me.releasedBy.id "+
    27             "LEFT JOIN Like  l on l.musicalEntity.id=s.id AND l.listener.id=:currentUserId "+
    28             "WHERE s.album.id=:albumId"
    29 
     23            "(CASE WHEN :currentUserId IS NOT NULL AND l.id IS NOT NULL THEN true ELSE false END)) " +
     24            "FROM Song s " +
     25            "JOIN s.musicalEntities me " +
     26            "JOIN me.releasedBy a " +
     27            "JOIN a.nonAdminUser nau " +
     28            "JOIN nau.user u "+
     29            "LEFT JOIN Like l ON l.musicalEntity.id = s.id AND l.listener.id = :currentUserId " +
     30            "WHERE s.album.id = :albumId"
    3031    )
    3132    List<MusicalEntityDto>findSongsByAlbum(@Param("albumId") Long albumId, @Param("currentUserId")Long currentUserId);
     33
     34
     35    @Query("SELECT NEW com.ukim.finki.develop.finkwave.model.dto.MusicalEntityDto(" +
     36            "s.id, " +
     37            "me.title, " +
     38            "me.genre, " +
     39            "'SONG', " +
     40            "u.fullName, " +
     41            "(CASE WHEN :currentUserId IS NOT NULL AND l.id IS NOT NULL THEN true ELSE false END)) " +
     42            "FROM Song s "+
     43            "JOIN s.musicalEntities me "+
     44            "JOIN me.releasedBy a "+
     45            "JOIN a.nonAdminUser nau "+
     46            "JOIN nau.user u "+
     47            "JOIN PlaylistSong ps ON ps.song.id=s.id "+
     48            "LEFT JOIN Like l ON l.musicalEntity.id=s.id AND l.listener.id=:currentUserId "+
     49            "WHERE ps.playlist.id=:playlistId")
     50    List<MusicalEntityDto>findSongsByPlaylistId(@Param("playlistId")Long playlistId, @Param("currentUserId")Long currentUserId);
    3251}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AlbumService.java

    r2b08bed r2730163  
    11package com.ukim.finki.develop.finkwave.service;
    22
     3import com.ukim.finki.develop.finkwave.exceptions.AlbumNotFoundException;
    34import com.ukim.finki.develop.finkwave.model.Album;
    45import com.ukim.finki.develop.finkwave.model.MusicalEntity;
     
    2324    public MusicalEntityDto getAlbum(Long id){
    2425        if (!albumRepository.existsById(id)){
    25             throw new RuntimeException("Album not found");
     26            throw new AlbumNotFoundException(id);
    2627        }
    2728        Long currentUserId=authService.getCurrentUserID();
    2829
    29         Album album=albumRepository.findById(id).orElseThrow(()->new RuntimeException("Album not found"));
     30        Album album=albumRepository.findById(id).orElseThrow(()->new AlbumNotFoundException(id));
    3031        AlbumDto dto=new AlbumDto(album.getId(),
    3132                album.getMusicalEntities().getTitle(),
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ArtistService.java

    r2b08bed r2730163  
    1616
    1717    private final ArtistContributionRepository artistContributionRepository;
     18    private final AuthService authService;
    1819
    1920
    2021    @Transactional(readOnly=true)
    2122    public List<ArtistContributionDto> getArtistContributions(Long artistId){
    22         return artistContributionRepository.findContributionsByArtistId(artistId);
     23        Long currentUserId=authService.getCurrentUserID();
     24        return artistContributionRepository.findContributionsByArtistId(currentUserId,artistId);
    2325    }
    2426
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/FollowService.java

    r2b08bed r2730163  
    1616import java.util.ArrayList;
    1717import java.util.List;
     18import java.util.Set;
    1819import java.util.stream.Collectors;
    1920
     
    2930
    3031
     32    private boolean isFollowedByCurrentUser(Long currentUserId,Long otherUserId){
     33        Set<Long> currentUserFollows = followRepository.getFollowsByFollower_Id(currentUserId)
     34                .stream()
     35                .map(f -> f.getFollowee().getId())
     36                .collect(Collectors.toSet());
     37        return currentUserFollows.contains(otherUserId);
     38    }
    3139
    3240    public List<NonAdminUserDto>getFollowersForUser(Long id){
     
    3442            throw new FollowException("Cannot view for artist");
    3543        }
    36         List<Follow>followers=followRepository.getFollowsByFollowee_Id(id);
     44        List<Follow> followers = followRepository.findFollowersWithProfile(id);
    3745        Long currentUserId=authService.getCurrentUserID();
     46
     47
    3848        return followers.stream()
    39                 .map(f->{
    40                    NonAdminUserDto dto=mapper.toDto(f.getFollower(),null,null,null);
    41                    dto.setIsFollowedByCurrentUser(followRepository.isFollowing(currentUserId,f.getFollower().getId()));
    42                    return dto;
     49                .map(f -> {
     50                    NonAdminUser follower = f.getFollower();
     51                    NonAdminUserDto dto = mapper.toDto(follower, null, null, null);
     52
     53                    dto.setIsFollowedByCurrentUser(isFollowedByCurrentUser(currentUserId,follower.getId()));
     54                    return dto;
    4355                })
    4456                .toList();
     
    4961            throw new FollowException("Cannot view for artist");
    5062        }
    51         List<Follow>followers=followRepository.getFollowsByFollower_Id(id);
     63        List<Follow>followings=followRepository.getFollowsByFollower_Id(id);
    5264        Long currentUserId=authService.getCurrentUserID();
    53         return followers.stream()
     65        return followings.stream()
    5466                .map(f->{
     67                    NonAdminUser followee=f.getFollowee();
    5568                    NonAdminUserDto dto=mapper.toDto(f.getFollowee(),null,null,null);
    56                     dto.setIsFollowedByCurrentUser(followRepository.isFollowing(currentUserId,f.getFollowee().getId()));
     69                    dto.setIsFollowedByCurrentUser(isFollowedByCurrentUser(currentUserId,followee.getId()));
    5770                    return dto;
    5871                })
     
    6275
    6376
    64     public boolean toggleFollow(Long id){
     77    public void toggleFollow(Long id){
    6578        Long currentUserId=authService.getCurrentUserID();
    6679
     
    7487        if (followRepository.existsById(followId)){
    7588            followRepository.deleteById(followId);
    76             return false;
     89
    7790        }else{
    7891            NonAdminUser follower = nonAdminUserRepository.findById(currentUserId)
     
    8295
    8396            followRepository.save(new Follow(follower, followee));
    84             return true;
     97
    8598        }
    8699
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ListenerService.java

    r2b08bed r2730163  
    1313@Service
    1414@AllArgsConstructor
     15@Transactional(readOnly = true)
    1516public class ListenerService {
    16     private final LikeRepository likeRepository;
    17     private final PlaylistRepository playlistRepository;
    18     @Transactional(readOnly = true)
     17    private final LikeService likeService;
     18    private final PlaylistService playlistService;
     19
    1920    public List<MusicalEntityDto> getLikedEntities(Long listenerId) {
    20         return  likeRepository.findLikedEntitiesWithTypeByListenerId(listenerId);
     21        return  likeService.findLikedEntitiesWithTypeByListenerId(listenerId);
    2122
    2223
    2324    }
    2425
    25     @Transactional(readOnly = true)
     26
    2627    public List<Playlist>getPlaylistsCreatedByUser(Long listenerId){
    27         return playlistRepository.findByCreatorId(listenerId);
     28        return playlistService.findByCreatorId(listenerId);
    2829    }
    2930}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/NonAdminUserService.java

    r2b08bed r2730163  
    8282                        p.getName(),
    8383                        p.getCover(),
    84                         p.getCreatedBy().getNonAdminUser().getUser().getFullName()
     84                        p.getCreatedBy().getNonAdminUser().getUser().getFullName(),
     85                        null
    8586                )).toList();
    8687
  • frontend/src/App.tsx

    r2b08bed r2730163  
    77import Nav from "./pages/Nav";
    88import Register from "./pages/Register";
    9 import UserDetailView from "./pages/UserDetailView";
     9import UserDetail from "./pages/UserDetail";
    1010import MusicalCollection from "./pages/MusicalCollection";
    1111
     
    5252      {
    5353        path: "/users/:userId",
    54         element: <UserDetailView />,
     54        element: <UserDetail />,
    5555      },
    5656      {
  • frontend/src/components/userProfile/ListenerView.tsx

    r2b08bed r2730163  
    2020          <div className="flex items-center gap-2 mb-6 pb-2 border-b-2 border-gray-200">
    2121            <ListMusic className="w-6 h-6 text-gray-700" />
    22             <h3 className="text-2xl font-bold text-gray-800">My Playlists</h3>
     22            <h3 className="text-2xl font-bold text-gray-800">
     23              Created Playlists
     24            </h3>
    2325            <span className="text-sm text-gray-400 ml-1">
    2426              ({playlists.length})
     
    3133                key={playlist.id}
    3234                className="group cursor-pointer"
    33                 onClick={() => navigate(`/playlists/${playlist.id}`)}
     35                onClick={() => navigate(`/collection/playlist/${playlist.id}`)}
    3436              >
    3537                <div className="aspect-square rounded-md overflow-hidden bg-gray-100 mb-2 relative shadow-sm group-hover:shadow-lg transition-all">
     
    105107                key={album.id}
    106108                className="group cursor-pointer"
    107                 onClick={() => navigate(`/musical-entity/${album.id}`)}
     109                onClick={() => navigate(`/collection/album/${album.id}`)}
    108110              >
    109111                <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">
  • frontend/src/components/userProfile/UserListModal.tsx

    r2b08bed r2730163  
    2525          <button
    2626            onClick={onClose}
    27             className="text-gray-500 hover:text-black text-2xl"
     27            className="text-gray-500 hover:text-black text-2xl cursor-pointer"
    2828          >
    2929            &times;
  • frontend/src/pages/MusicalCollection.tsx

    r2b08bed r2730163  
    22import { useNavigate, useParams } from "react-router-dom";
    33import axiosInstance from "../api/axiosInstance";
    4 
    5 interface Song {
     4import type { Song, Album, Playlist } from "../utils/types";
     5import { handleError } from "../utils/error";
     6interface CollectionView {
    67  id: number;
    78  title: string;
    8   genre: string;
    9   type: "SONG";
    10   releasedBy: string;
    11   isLikedByCurrentUser?: boolean;
    12 }
    13 
    14 interface MusicalEntity {
    15   id: number;
    16   title: string;
    17   genre: string;
     9  genre?: string;
    1810  type: string;
    1911  releasedBy: string;
    2012  isLikedByCurrentUser?: boolean;
    21   songs?: Song[];
     13  songs: Song[];
    2214}
    2315
     
    2517  const { type, id } = useParams();
    2618  const navigate = useNavigate();
    27   const [collection, setCollection] = useState<MusicalEntity | null>(null);
     19  const [collection, setCollection] = useState<CollectionView | null>(null);
    2820  const [isLoading, setIsLoading] = useState(true);
    2921  const [error, setError] = useState<string | null>(null);
     22
     23  const normalizeCollection = (
     24    data: Album | Playlist,
     25    type: string,
     26  ): CollectionView => {
     27    if (type === "album") {
     28      const album = data as Album;
     29      return {
     30        id: album.id,
     31        title: album.title,
     32        genre: album.genre,
     33        type: album.type,
     34        releasedBy: album.releasedBy,
     35        isLikedByCurrentUser: album.isLikedByCurrentUser,
     36        songs: album.songs,
     37      };
     38    } else {
     39      const playlist = data as Playlist;
     40      return {
     41        id: playlist.id,
     42        title: playlist.name,
     43        genre: undefined,
     44        type: "PLAYLIST",
     45        releasedBy: playlist.creatorName,
     46        isLikedByCurrentUser: undefined,
     47        songs: playlist.songsInPlaylist,
     48      };
     49    }
     50  };
    3051
    3152  useEffect(() => {
     
    3758          type === "album" ? `/albums/${id}` : `/playlists/${id}`;
    3859        const response = await axiosInstance.get(endpoint);
    39         console.log(response.data);
    40         setCollection(response.data);
     60
     61        const normalized = normalizeCollection(response.data, type!);
     62        setCollection(normalized);
    4163      } catch (err: any) {
    42         setError(err.response?.data?.error || "Failed to load collection");
     64        setError(handleError(err));
    4365      } finally {
    4466        setIsLoading(false);
     
    6789      <button
    6890        onClick={() => navigate(-1)}
    69         className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200"
     91        className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 cursor-pointer"
    7092      >
    7193        ← Back
     
    88110            <div className="flex items-center gap-3 text-gray-700 mb-4">
    89111              <span className="font-semibold">{collection.releasedBy}</span>
    90               <span>•</span>
    91               <span className="text-gray-600">{collection.genre}</span>
     112              {collection.genre && (
     113                <>
     114                  <span>•</span>
     115                  <span className="text-gray-600">{collection.genre}</span>
     116                </>
     117              )}
    92118              {collection.songs && (
    93119                <>
     
    101127            </div>
    102128
    103             <button
    104               className="flex items-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200"
    105               aria-label={collection.isLikedByCurrentUser ? "Unlike" : "Like"}
    106             >
    107               <svg
    108                 className="w-5 h-5"
    109                 fill={collection.isLikedByCurrentUser ? "#ef4444" : "none"}
    110                 stroke={collection.isLikedByCurrentUser ? "#ef4444" : "#6b7280"}
    111                 strokeWidth="2"
    112                 viewBox="0 0 24 24"
     129            {type === "album" && (
     130              <button
     131                className="flex items-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors duration-200"
     132                aria-label={collection.isLikedByCurrentUser ? "Unlike" : "Like"}
    113133              >
    114                 <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" />
    115               </svg>
    116               <span className="text-sm font-medium text-gray-700">
    117                 {collection.isLikedByCurrentUser ? "Liked" : "Like"}
    118               </span>
    119             </button>
     134                <svg
     135                  className="w-5 h-5"
     136                  fill={collection.isLikedByCurrentUser ? "#ef4444" : "none"}
     137                  stroke={
     138                    collection.isLikedByCurrentUser ? "#ef4444" : "#6b7280"
     139                  }
     140                  strokeWidth="2"
     141                  viewBox="0 0 24 24"
     142                >
     143                  <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" />
     144                </svg>
     145                <span className="text-sm font-medium text-gray-700">
     146                  {collection.isLikedByCurrentUser ? "Liked" : "Like"}
     147                </span>
     148              </button>
     149            )}
    120150          </div>
    121151        </div>
  • frontend/src/pages/UserDetail.tsx

    r2b08bed r2730163  
    55import ListenerView from "../components/userProfile/ListenerView";
    66import UserListModal from "../components/userProfile/UserListModal";
     7import { handleError } from "../utils/error";
    78import type {
    89  MusicalEntity,
     
    4344    try {
    4445      const response = await axiosInstance.post<UserProfile>(
    45         `/users/follow/${userId}`,
     46        `/users/${userId}/follow`,
    4647      );
    4748      setUser(response.data);
    4849    } catch (err: any) {
    49       console.error(err.response?.data?.error);
     50      setError(handleError(err));
    5051    } finally {
    5152      setIsFollowing(false);
     
    5657    setIsLoadingModal(true);
    5758    try {
    58       const response = await axiosInstance.get(`/users/followers/${userId}`);
     59      const response = await axiosInstance.get(`/users/${userId}/followers`);
    5960      setModalUsers(response.data);
    6061      setModalTitle("Followers");
    6162      setShowModal(true);
    6263    } catch (err) {
    63       console.error("Failed to fetch followers");
     64      setError(handleError(err));
    6465    } finally {
    6566      setIsLoadingModal(false);
     
    6970    setIsLoadingModal(true);
    7071    try {
    71       const response = await axiosInstance.get(`/users/following/${userId}`);
     72      const response = await axiosInstance.get(`/users/${userId}/following`);
    7273      setModalUsers(response.data);
    7374      setModalTitle("Following");
    7475      setShowModal(true);
    75     } catch (err) {
    76       console.error("Failed to fetch following users");
     76    } catch (err: any) {
     77      setError(handleError(err));
    7778    } finally {
    7879      setIsLoadingModal(false);
     
    8283  const handleFollowInModal = async (targetId: number) => {
    8384    try {
    84       await axiosInstance.post(`/users/follow/${targetId}`);
     85      await axiosInstance.post(`/users/${targetId}/follow`);
    8586      setModalUsers((prevUsers) =>
    8687        prevUsers.map((u) => {
     
    9697      );
    9798
    98       if (user && user.id === targetId) {
    99         const response = await axiosInstance.get(`/users/${targetId}`);
    100         setUser(response.data);
    101       }
    102     } catch (err) {
    103       console.error("Failed to toggle follow in modal", err);
     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));
    104105    }
    105106  };
     
    110111      try {
    111112        const response = await axiosInstance.get(`/users/${userId}`);
    112         console.log(response.data);
    113113        setUser(response.data);
    114114      } catch (err: any) {
    115         const errorMessage =
    116           err.response?.data?.error || "Failed to fetch user";
    117         setError(errorMessage);
     115        setError(handleError(err));
    118116      }
    119117    };
     
    143141      <button
    144142        onClick={() => navigate(-1)}
    145         className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200"
     143        className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors duration-200 cursor-pointer"
    146144      >
    147145        ← Back
  • frontend/src/utils/types.ts

    r2b08bed r2730163  
    1313  role: string;
    1414  entityType: string;
     15  isLikedByCurrentUser: boolean;
    1516}
     17
    1618export interface MusicalEntity {
    1719  id: number;
     
    2022  type: string;
    2123  releasedBy: string;
     24  isLikedByCurrentUser?: boolean;
     25}
     26
     27export interface Song extends MusicalEntity {
     28  type: "SONG";
     29}
     30
     31export interface Album extends MusicalEntity {
     32  type: "ALBUM";
     33  songs: Song[];
    2234}
    2335
     
    2739  cover: string;
    2840  creatorName: string;
     41  songsInPlaylist: Song[];
    2942}
    3043
Note: See TracChangeset for help on using the changeset viewer.