Changeset 0f71490


Ignore:
Timestamp:
01/27/26 12:55:23 (6 months ago)
Author:
Dimitar Arsov <dimitararsov04@…>
Branches:
main
Children:
4e5cf92
Parents:
c6e1892
Message:

show follow/following button

Files:
11 edited

Legend:

Unmodified
Added
Removed
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/ArtistContributionDTO.java

    rc6e1892 r0f71490  
    1111    private Long musicalEntityId;
    1212    private String title;
     13    private String genre;
    1314    private String role;
    1415    private String entityType;
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/model/dto/NonAdminUserDTO.java

    rc6e1892 r0f71490  
    1818    private Long followers;
    1919    private Long following;
     20    private Boolean isFollowedByCurrentUser;
    2021
    2122
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/ArtistContributionRepository.java

    rc6e1892 r0f71490  
    1313public interface ArtistContributionRepository extends JpaRepository<ArtistContribution, ArtistContributionId> {
    1414
    15     @Query("SELECT ac.musicalEntity.id,ac.musicalEntity.title,ac.role, "+
    16         "CASE WHEN s.id IS NOT NULL THEN 'Song' "+
    17         "WHEN a.id IS NOT NULL THEN 'Album' "+
     15    @Query("SELECT ac.musicalEntity.id,ac.musicalEntity.title,ac.musicalEntity.genre,ac.role, "+
     16        "CASE WHEN s.id IS NOT NULL THEN 'SONG' "+
     17        "WHEN a.id IS NOT NULL THEN 'ALBUM' "+
    1818        "ELSE 'Unknown' END "+
    1919        "FROM ArtistContribution ac "+
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/repository/FollowRepository.java

    rc6e1892 r0f71490  
    22
    33import org.springframework.data.jpa.repository.JpaRepository;
     4import org.springframework.data.jpa.repository.Query;
     5import org.springframework.data.repository.query.Param;
    46import org.springframework.stereotype.Repository;
    57
     
    1618    long countByFollowerId(Long userId);
    1719
     20    @Query("SELECT CASE WHEN COUNT (f)>0 THEN  true ELSE false END " +
     21            "FROM Follow f "+
     22            "WHERE f.followee.id=:selectedUserId AND f.follower.id=:currentUserId")
     23    boolean isFollowing(@Param("currentUserId") Long currentUSerId,@Param("selectedUserId")Long selectedUserId);
     24
    1825}
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/ArtistService.java

    rc6e1892 r0f71490  
    2323
    2424        return results.stream()
    25         .map(row->new ArtistContributionDTO((Long)row[0], (String)row[1], (String)row[2], (String)row[3]))
     25        .map(row->new ArtistContributionDTO((Long)row[0], (String)row[1], (String)row[2],(String)row[3], (String)row[4]))
    2626        .collect(Collectors.toList());
    2727    }
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/AuthService.java

    rc6e1892 r0f71490  
    11package com.ukim.finki.develop.finkwave.service;
    22
     3import org.springframework.security.core.Authentication;
     4import org.springframework.security.core.context.SecurityContextHolder;
    35import org.springframework.security.crypto.password.PasswordEncoder;
    46import org.springframework.stereotype.Service;
     
    98100    }
    99101
     102    public Long getCurrentUserID(){
     103        Authentication authentication= SecurityContextHolder.getContext().getAuthentication();
     104        if (authentication == null || !authentication.isAuthenticated()) {
     105            throw new RuntimeException("User not authenticated");
     106        }
     107
     108        String username=authentication.getName();
     109
     110
     111        return userRepository.findByUsername(username).map(User::getId)
     112                .orElseThrow(()->new RuntimeException("User not found"));
     113    }
     114
     115
    100116    private User createNonAdminUser(AuthRequestDto authRequestDto) throws IOException {
    101117        User.UserBuilder userBuilder = User.builder()
  • finkwave/src/main/java/com/ukim/finki/develop/finkwave/service/NonAdminUserService.java

    rc6e1892 r0f71490  
    11package com.ukim.finki.develop.finkwave.service;
    22
    3 import com.ukim.finki.develop.finkwave.model.Artist;
    4 import com.ukim.finki.develop.finkwave.model.Listener;
    5 import com.ukim.finki.develop.finkwave.model.NonAdminUser;
    6 import com.ukim.finki.develop.finkwave.model.Playlist;
     3import com.ukim.finki.develop.finkwave.model.*;
    74import com.ukim.finki.develop.finkwave.model.dto.*;
    8 import com.ukim.finki.develop.finkwave.repository.ArtistRepository;
    9 import com.ukim.finki.develop.finkwave.repository.FollowRepository;
    10 import com.ukim.finki.develop.finkwave.repository.ListenerRepository;
    11 import com.ukim.finki.develop.finkwave.repository.NonAdminUserRepository;
     5import com.ukim.finki.develop.finkwave.repository.*;
    126import com.ukim.finki.develop.finkwave.service.mappers.NonAdminUserMapper;
    137import lombok.AllArgsConstructor;
     8import org.springframework.security.core.Authentication;
     9import org.springframework.security.core.context.SecurityContextHolder;
    1410import org.springframework.stereotype.Service;
    1511import org.springframework.transaction.annotation.Transactional;
     
    2723    private final ListenerService listenerService;
    2824    private final FollowRepository followRepository;
     25    private final AuthService authService;
    2926
    3027    @Transactional(readOnly = true)
     
    4441        Long followers=followRepository.countByFolloweeId(id);
    4542        Long following=followRepository.countByFollowerId(id);
     43        Long currentUserId= authService.getCurrentUserID();
     44
     45        boolean isFollowedByCurrentUser=false;
     46        if (currentUserId != null) {
     47            isFollowedByCurrentUser = followRepository.isFollowing(currentUserId, id);
     48        }
     49        NonAdminUserDTO dto;
    4650   
    4751        if (artistRepository.existsById(id)) {
    48             return getArtistProfile(id,followers,following);
     52             dto=getArtistProfile(id, followers, following);
     53             dto.setIsFollowedByCurrentUser(isFollowedByCurrentUser);
     54             return dto;
    4955        }
    5056     
    5157        if (listenerRepository.existsById(id)) {
    52             return getListenerProfile(id,followers,following);
     58            dto=getListenerProfile(id, followers, following);
     59            dto.setIsFollowedByCurrentUser(isFollowedByCurrentUser);
     60            return dto;
    5361        }
    5462       
     
    5967        Artist artist = artistRepository.findByIdWithUser(artistId)
    6068            .orElseThrow(() -> new RuntimeException("Artist not found with id: " + artistId));
    61        
    62      
     69
     70
    6371        ArtistMusicalEntitiesDTO entitiesDTO = new ArtistMusicalEntitiesDTO();
    6472        entitiesDTO.setContributions(artistService.getArtistContributions(artistId));
    65        
     73
    6674        return mapper.toArtistDTO(artist, entitiesDTO,followers,following);
    6775    }
     
    7078        Listener listener = listenerRepository.findByIdWithUser(listenerId)
    7179            .orElseThrow(() -> new RuntimeException("Listener not found with id: " + listenerId));
    72        
    73        
     80
     81
    7482        ListenerLikesDTO likesDTO = listenerService.getLikedEntities(listenerId);
    7583        List<PlaylistDTO>playlists=listenerService.getPlaylistsCreatedByUser(listenerId).stream()
     
    8088                        p.getCreatedBy().getNonAdminUser().getUser().getFullName()
    8189                )).toList();
    82        
     90
    8391        return mapper.toListenerDTO(listener, likesDTO,followers,following,playlists);
    8492    }
     
    8694
    8795    public List<NonAdminUserDTO>searchUsers(String name){
     96
     97        List<NonAdminUser>nonAdminUsers=nonAdminUserRepository.searchByName(name);
     98        if (nonAdminUsers.isEmpty()){
     99            throw new RuntimeException("No user found");
     100        }
     101
    88102        return nonAdminUserRepository.searchByName(name).stream()
    89103                .map(user -> {
     
    96110    }
    97111
     112
     113
    98114    private String determineType(Long id) {
    99115        if (artistRepository.existsById(id)) {
  • frontend/src/components/userProfile/AllUsersHelper.tsx

    rc6e1892 r0f71490  
    11import React, { useEffect, useState } from "react";
    22import { useNavigate } from "react-router-dom";
    3 
     3import axiosInstance from "../../api/axiosInstance";
    44interface User {
    55  id: number;
     
    1616  useEffect(() => {
    1717    const fetchUsers = async () => {
    18       const response = await fetch("http://localhost:8080/users/all");
    19       const data = await response.json();
    20       setUsers(data);
     18      const response = axiosInstance.get("/users/all");
     19      setUsers((await response).data);
    2120    };
    2221    fetchUsers();
     
    3029    e.preventDefault();
    3130    try {
    32       const response = await fetch(
    33         `http://localhost:8080/users/search?name=${searchedUser}`,
    34       );
    35       if (!response.ok) throw new Error("Search failed");
    36 
    37       const data = await response.json();
    38       setUsers(data);
     31      const response = await axiosInstance.get(`/users/search`, {
     32        params: { name: searchedUser },
     33      });
     34      setUsers(response.data);
    3935    } catch (err: any) {
    40       setError(err.message);
     36      const errorMessage = err.response?.data?.error || "Search failed";
     37      setError(errorMessage);
    4138    }
    4239  };
    4340
    4441  if (users.length == 0) return <div className="p-6">Loading...</div>;
     42  if (error) {
     43    return (
     44      <div className="p-6 bg-red-50 border border-red-200 text-red-700 rounded-lg">
     45        <h2 className="font-bold">Error</h2>
     46        <p>{error}</p>
     47      </div>
     48    );
     49  }
    4550
    4651  return (
  • frontend/src/components/userProfile/ArtistView.tsx

    rc6e1892 r0f71490  
    11import { useNavigate } from "react-router-dom";
    22import { Music, Disc3, Mic2 } from "lucide-react";
    3 
    4 interface ArtistContributionDTO {
    5   musicalEntityId: number;
    6   title: string;
    7   role: string;
    8   entityType: string;
    9 }
     3import type { ArtistContributionDTO } from "../../types";
    104
    115interface ArtistViewProps {
     
    1610  const navigate = useNavigate();
    1711
    18   const albums = contributions.filter((c) => c.entityType === "Album");
    19   const songs = contributions.filter((c) => c.entityType === "Song");
     12  const albums = contributions.filter((c) => c.entityType === "ALBUM");
     13  const songs = contributions.filter((c) => c.entityType === "SONG");
    2014
    2115  const getRoleColor = (role: string) => {
     
    3125  return (
    3226    <div className="mt-8">
    33       {/* Albums Section */}
    3427      {albums.length > 0 && (
    3528        <div className="mb-12">
     
    9184                  </h3>
    9285                  <div className="flex items-center gap-2 mt-1">
    93                     <Mic2 className="w-4 h-4 text-gray-400" />
     86                    {song.genre}
    9487                  </div>
    9588                </div>
  • frontend/src/components/userProfile/UserDetailView.tsx

    rc6e1892 r0f71490  
    33import ArtistView from "./ArtistView";
    44import ListenerView from "./ListenerView";
    5 
    6 interface MusicalEntityDTO {
    7   id: number;
    8   title: string;
    9   genre: string;
    10   type: string;
    11 }
    12 
    13 interface ArtistContributionDTO {
    14   musicalEntityId: number;
    15   title: string;
    16   role: string;
    17   entityType: string;
    18 }
    19 
    20 interface Playlist {
    21   id: number;
    22   name: string;
    23   cover: string;
    24   creatorName: string;
    25 }
     5import axiosInstance from "../../api/axiosInstance";
     6import type { ArtistContributionDTO } from "../../types";
     7import type { MusicalEntityDTO } from "../../types";
     8import type { Playlist } from "../../types";
    269
    2710interface User {
     
    3215  followers: number;
    3316  following: number;
     17  isFollowedByCurrentUser: boolean;
    3418
    3519  musicalEntities?: {
     
    4529
    4630const UserDetail = () => {
     31  // user refers to the selected user NOT to the user from context
    4732  const { userId } = useParams();
    4833  const navigate = useNavigate();
     
    5439      setError(null);
    5540      try {
    56         const response = await fetch(`http://localhost:8080/users/${userId}`);
     41        const response = await axiosInstance.get(`/users/${userId}`);
    5742
    58         if (!response.ok) {
    59           const errorData = await response.json();
    60           throw new Error(errorData.error || "Failed to fetch user");
    61         }
    62 
    63         const data = await response.json();
    64         setUser(data);
     43        setUser(response.data);
    6544      } catch (err: any) {
    66         setError(err.message);
     45        const errorMessage =
     46          err.response?.data?.error || "Failed to fetch user";
     47        setError(errorMessage);
    6748      }
    6849    };
     
    11596            </div>
    11697
    117             <button className="px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors font-medium">
    118               Follow
     98            <button className="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg shadow-md transition-colors duration-200 cursor-pointer">
     99              {user.isFollowedByCurrentUser ? "Unfollow" : "Follow"}
    119100            </button>
    120101          </div>
  • frontend/src/types.ts

    rc6e1892 r0f71490  
    11export interface User {
    2         username: string;
    3         fullName: string;
    4         email?: string;
    5         profilePhoto?: string | null;
    6         role?: "ADMIN" | "NONADMIN";
     2  username: string;
     3  fullName: string;
     4  email?: string;
     5  profilePhoto?: string | null;
     6  role?: "ADMIN" | "NONADMIN";
    77}
     8
     9export interface ArtistContributionDTO {
     10  musicalEntityId: number;
     11  title: string;
     12  genre: string;
     13  role: string;
     14  entityType: string;
     15}
     16export interface MusicalEntityDTO {
     17  id: number;
     18  title: string;
     19  genre: string;
     20  type: string;
     21}
     22
     23export interface Playlist {
     24  id: number;
     25  name: string;
     26  cover: string;
     27  creatorName: string;
     28}
Note: See TracChangeset for help on using the changeset viewer.