diff --git a/backend/internal/constants/platnm.go b/backend/internal/constants/platnm.go new file mode 100644 index 00000000..f2fec3f7 --- /dev/null +++ b/backend/internal/constants/platnm.go @@ -0,0 +1,7 @@ +package constants + +const ( + PostReaction = 1 + Rating = 5 + RecommendationLike = 1 +) diff --git a/backend/internal/storage/postgres/schema/playlist.go b/backend/internal/storage/postgres/schema/playlist.go index 7663c8eb..50e42dd6 100644 --- a/backend/internal/storage/postgres/schema/playlist.go +++ b/backend/internal/storage/postgres/schema/playlist.go @@ -30,7 +30,6 @@ func (r *PlaylistRepository) CreatePlaylist(ctx context.Context, playlist models return err } return nil - } func (r *PlaylistRepository) AddToUserOnQueue(ctx context.Context, id string, track models.Track) error { diff --git a/backend/internal/storage/postgres/schema/recommendation.go b/backend/internal/storage/postgres/schema/recommendation.go index a6e00a5f..66a3e134 100644 --- a/backend/internal/storage/postgres/schema/recommendation.go +++ b/backend/internal/storage/postgres/schema/recommendation.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "errors" + "platnm/internal/constants" "platnm/internal/errs" "platnm/internal/models" "strconv" @@ -175,6 +176,11 @@ func (r *RecommendationRepository) UpdateRecommendation(ctx context.Context, rec return err } + _, err = r.Exec(ctx, `UPDATE "user" SET platnm = platnm + $1 WHERE id = $2`, constants.RecommendationLike, recommendation.RecommenderId) + if err != nil { + return err + } + return nil } diff --git a/backend/internal/storage/postgres/schema/review.go b/backend/internal/storage/postgres/schema/review.go index f8e09082..8abf4522 100644 --- a/backend/internal/storage/postgres/schema/review.go +++ b/backend/internal/storage/postgres/schema/review.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "platnm/internal/constants" "platnm/internal/errs" "platnm/internal/models" "strconv" @@ -100,6 +101,11 @@ func (r *ReviewRepository) CreateReview(ctx context.Context, review *models.Revi } } + _, err := r.Exec(ctx, `UPDATE "user" SET platnm = platnm + $1 WHERE id = $2`, constants.Rating, review.UserID) + if err != nil { + return nil, err + } + return review, nil } @@ -535,54 +541,131 @@ func (r *ReviewRepository) GetUserFollowingReviewsOfMedia(ctx context.Context, m } -func (r *ReviewRepository) GetReviewsByUserID(ctx context.Context, id string) ([]*models.Review, error) { +func (r *ReviewRepository) GetReviewsByUserID(ctx context.Context, userId string) ([]*models.Preview, error) { - rows, err := r.Query(ctx, "SELECT id, user_id, media_id, media_type, rating, title, comment, created_at, updated_at, draft FROM review WHERE user_id = $1 ORDER BY updated_at DESC", id) + query := ` + SELECT + r.id, + r.user_id, + u.username, + u.display_name, + u.profile_picture, + r.media_type, + r.media_id, + r.rating, + r.title, + r.comment, + r.created_at, + r.updated_at, + COALESCE(a.cover, t.cover) AS media_cover, + COALESCE(a.title, t.title) AS media_title, + COALESCE(a.artists, t.artists) AS media_artist, + ARRAY_AGG(tag.name) FILTER (WHERE tag.name IS NOT NULL) AS tags + FROM review r + INNER JOIN "user" u ON u.id = r.user_id + LEFT JOIN ( + SELECT t.title, t.id, STRING_AGG(ar.name, ', ') AS artists, cover + FROM track t + JOIN track_artist ta on t.id = ta.track_id + JOIN artist ar ON ta.artist_id = ar.id + JOIN album a on t.album_id = a.id + GROUP BY t.id, cover, t.title + ) t ON r.media_type = 'track' AND r.media_id = t.id + LEFT JOIN ( + SELECT a.id, a.title, STRING_AGG(ar.name, ', ') AS artists, cover + FROM album a + JOIN album_artist aa on a.id = aa.album_id + JOIN artist ar ON aa.artist_id = ar.id + GROUP BY a.id, cover, a.title + ) a ON r.media_type = 'album' AND r.media_id = a.id + LEFT JOIN review_tag rt ON r.id = rt.review_id + LEFT JOIN tag tag ON rt.tag_id = tag.id + LEFT JOIN ( + SELECT post_id as review_id, COUNT(*) AS vote_count + FROM user_vote + WHERE post_type = 'review' + GROUP BY post_id + ) v ON r.id = v.review_id + WHERE r.user_id = $1 + GROUP BY r.id, r.user_id, u.username, u.display_name, u.profile_picture, r.media_type, r.media_id, r.rating, r.comment, r.created_at, r.updated_at, media_cover, media_title, media_artist, v.vote_count + ` - if !rows.Next() { - return []*models.Review{}, nil - } + rows, err := r.Query(ctx, query, userId) if err != nil { - return []*models.Review{}, err + fmt.Println(err) + return nil, err } - defer rows.Close() - var reviews []*models.Review - for rows.Next() { - var review models.Review - var title sql.NullString + var previews []*models.Preview - if err := rows.Scan( - &review.ID, - &review.UserID, - &review.MediaID, - &review.MediaType, - &review.Rating, + // Scan results into the feedPosts slice + for rows.Next() { + var preview models.Preview + var title, comment sql.NullString // Use sql.NullString for nullable strings + err := rows.Scan( + &preview.ReviewID, + &preview.UserID, + &preview.Username, + &preview.DisplayName, + &preview.ProfilePicture, + &preview.MediaType, + &preview.MediaID, + &preview.Rating, &title, - &review.Comment, - &review.CreatedAt, - &review.UpdatedAt, - &review.Draft, - ); err != nil { + &comment, + &preview.CreatedAt, + &preview.UpdatedAt, + &preview.MediaCover, + &preview.MediaTitle, + &preview.MediaArtist, + &preview.Tags, + ) + if err != nil { + fmt.Println(err) return nil, err } + // Assign comment to feedPost.Comment, handling null case + if comment.Valid { + preview.Comment = &comment.String // Point to the string if valid + } else { + preview.Comment = nil // Set to nil if null + } + if title.Valid { - review.Title = &title.String // Point to the string if valid + preview.Title = &title.String // Point to the string if valid } else { - review.Title = nil // Set to nil if null + preview.Title = nil // Set to nil if null + } + + // Ensure tags is an empty array if null + if preview.Tags == nil { + preview.Tags = []string{} } - reviews = append(reviews, &review) + // Fetch review statistics for the current review + reviewStat, err := r.GetReviewStats(ctx, strconv.Itoa(preview.ReviewID)) + if err != nil { + return nil, err + } + + // If reviewStat is not nil, populate the corresponding fields in FeedPost + if reviewStat != nil { + preview.ReviewStat = *reviewStat + } + + // Append the populated FeedPost to the feedPosts slice + previews = append(previews, &preview) } + // Check for errors after looping through rows if err := rows.Err(); err != nil { - return []*models.Review{}, err + return nil, err } - return reviews, nil + return previews, nil } func (r *ReviewRepository) GetUserReviewOfTrack(ctx context.Context, mediaId string, userId string) (*models.Review, error) { @@ -1134,6 +1217,11 @@ func (r *ReviewRepository) UserVote(ctx context.Context, userID string, postID s // check if the review has more than 10 upvotes and if so notify the person that made the review review, _ := r.GetReviewByID(ctx, postID) + _, err = r.Exec(ctx, `UPDATE "user" SET platnm = platnm + $1 WHERE id = $2`, constants.RecommendationLike, review.UserID) + if err != nil { + return err + } + if review.ReviewStat.Upvotes >= 10 { // if we have more 10 upvotes on the review now _, err = r.Exec(ctx, ` @@ -1144,13 +1232,17 @@ func (r *ReviewRepository) UserVote(ctx context.Context, userID string, postID s if err != nil { return err } - } } else if postType == "comment" { comment, _ := r.GetCommentByCommentID(ctx, postID) + _, err = r.Exec(ctx, `UPDATE "user" SET platnm = platnm + $1 WHERE id = $2`, constants.PostReaction, comment.UserID) + if err != nil { + return err + } + if comment.Upvotes >= 10 { // if we have more 10 upvotes on the comment now _, err = r.Exec(ctx, ` diff --git a/backend/internal/storage/postgres/schema/user.go b/backend/internal/storage/postgres/schema/user.go index bf4c0a75..8ef62d3a 100644 --- a/backend/internal/storage/postgres/schema/user.go +++ b/backend/internal/storage/postgres/schema/user.go @@ -217,25 +217,19 @@ func (r *UserRepository) UpdateUserOnboard(ctx context.Context, email string, en func (r *UserRepository) GetUserProfile(ctx context.Context, id uuid.UUID) (*models.Profile, error) { profile := &models.Profile{} - query := `SELECT u.id, u.username, u.display_name, COUNT(DISTINCT followers.follower_id) AS follower_count, COUNT(DISTINCT followed.followee_id) AS followed_count, u.bio, u.profile_picture + query := `SELECT u.id, u.username, u.display_name, COUNT(DISTINCT followers.follower_id) AS follower_count, COUNT(DISTINCT followed.followee_id) AS followed_count, u.bio, u.profile_picture, u.platnm FROM "user" u LEFT JOIN follower followers ON followers.followee_id = u.id LEFT JOIN follower followed ON followed.follower_id = u.id WHERE u.id = $1 GROUP BY u.id, u.username, u.display_name, u.bio, u.profile_picture;` - err := r.db.QueryRow(ctx, query, id).Scan(&profile.UserID, &profile.Username, &profile.DisplayName, &profile.Followers, &profile.Followed, &profile.Bio, &profile.ProfilePicture) + err := r.db.QueryRow(ctx, query, id).Scan(&profile.UserID, &profile.Username, &profile.DisplayName, &profile.Followers, &profile.Followed, &profile.Bio, &profile.ProfilePicture, &profile.Score) if err != nil { print(err.Error(), "unable to find profile") return nil, err } - score, err := r.CalculateScore(ctx, id) - if err != nil { - return nil, err - } - profile.Score = score - return profile, nil } diff --git a/backend/internal/storage/storage.go b/backend/internal/storage/storage.go index 584ffe3a..38adec96 100644 --- a/backend/internal/storage/storage.go +++ b/backend/internal/storage/storage.go @@ -35,7 +35,6 @@ type UserRepository interface { GetConnections(ctx context.Context, id uuid.UUID, limit int, offset int) (models.Connections, error) GetProfileByName(ctx context.Context, name string) ([]*models.Profile, error) GetNotifications(ctx context.Context, id string) ([]*models.Notification, error) - // GetProfileByUser(ctx context.Context, userName string) (*models.Profile, error) } type ReviewRepository interface { diff --git a/frontend/app/(tabs)/profile.tsx b/frontend/app/(tabs)/profile.tsx index 98740ca8..b89863c5 100644 --- a/frontend/app/(tabs)/profile.tsx +++ b/frontend/app/(tabs)/profile.tsx @@ -96,7 +96,7 @@ export default function ProfileScreen() { {/* Activity icon with notification badge */} handleActivityPress(userId)} style={styles.activityIconContainer} > diff --git a/frontend/app/(tabs)/search.tsx b/frontend/app/(tabs)/search.tsx index 83038687..1765c469 100644 --- a/frontend/app/(tabs)/search.tsx +++ b/frontend/app/(tabs)/search.tsx @@ -77,7 +77,6 @@ const SearchPage: React.FC = () => { return ( - {isSearchActive ? ( handleActivityPress(userId)} style={styles.activityIconContainer} > diff --git a/frontend/app/Activity.tsx b/frontend/app/Activity.tsx index 6c05116f..9eb1f3cc 100644 --- a/frontend/app/Activity.tsx +++ b/frontend/app/Activity.tsx @@ -9,15 +9,19 @@ import { SafeAreaView, } from "react-native"; import Icon from "react-native-vector-icons/Ionicons"; -import { router } from "expo-router"; +import { router, useLocalSearchParams } from "expo-router"; import axios from "axios"; import ReviewCard from "@/components/ReviewCard"; import { useAuthContext } from "@/components/AuthProvider"; +import ReviewPreview from "@/components/ReviewPreview"; const Activity = () => { const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL; - const [userReviews, setUserReviews] = useState(); - const { userId } = useAuthContext(); + const [userReviews, setUserReviews] = useState(); + const loggedInUser = useAuthContext().userId; + const { userId } = useLocalSearchParams<{ + userId: string; + }>(); const handleDraftPress = () => { console.log("Draft Button pressed"); @@ -27,7 +31,7 @@ const Activity = () => { useEffect(() => { const fetchUserReviews = async () => { try { - const response = await axios.get(`${BASE_URL}/reviews/${userId}`); + const response = await axios.get(`${BASE_URL}/reviews/user/${userId}`); setUserReviews(response.data); } catch (error) { console.error("Error fetching user reviews:", error); @@ -53,20 +57,16 @@ const Activity = () => { {/* Draft Button */} - - Drafts - + {loggedInUser === userId && ( + + Drafts + + )} {/* User Reviews Section */} {userReviews && userReviews.length > 0 ? ( userReviews.map((review, index) => { - return ( - - ); + return ; }) ) : ( No reviews found. diff --git a/frontend/components/ReviewPreview.tsx b/frontend/components/ReviewPreview.tsx index 85b62530..3ff8d181 100644 --- a/frontend/components/ReviewPreview.tsx +++ b/frontend/components/ReviewPreview.tsx @@ -301,7 +301,16 @@ const ReviewPreview: React.FC = ({ preview }) => { - {preview.title} + {preview.tags && preview.tags.length > 0 && ( + + {preview.tags.map((tag, index) => ( + + {tag} + + ))} + + )} + {preview.title && {preview.title}} {isEditable ? ( = ({ preview }) => { )} - - {preview.tags && preview.tags.length > 0 && ( - - {preview.tags.map((tag, index) => ( - - {tag} - - ))} - - )} @@ -539,11 +538,13 @@ const styles = StyleSheet.create({ marginTop: 5, width: 175, textAlign: "left", + marginBottom: 5, }, artistName: { fontSize: 13, color: "#666", textAlign: "left", + marginBottom: 15, }, ratingContainer: { justifyContent: "flex-start", @@ -557,6 +558,7 @@ const styles = StyleSheet.create({ }, title: { fontWeight: 700, + marginTop: 10, }, commentText: { fontSize: 14, @@ -571,11 +573,11 @@ const styles = StyleSheet.create({ tagsContainer: { flexDirection: "row", flexWrap: "wrap", // Allows wrapping to a new line - marginVertical: 8, + marginTop: 8, gap: 8, // Space between tags }, tag: { - backgroundColor: "rgba(242, 128, 55, 0.65)", + backgroundColor: "#FDE1D5", paddingVertical: 5, paddingHorizontal: 12, borderRadius: 20, diff --git a/frontend/components/search/AlbumSearchCard.tsx b/frontend/components/search/AlbumSearchCard.tsx index 867a1cd8..69e9938d 100644 --- a/frontend/components/search/AlbumSearchCard.tsx +++ b/frontend/components/search/AlbumSearchCard.tsx @@ -2,24 +2,25 @@ import React from "react"; import { router } from "expo-router"; import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native"; -interface AlbumSearchCardProps { +interface SearchCardProps { id: number; rank: number; artist_name: string; album_name: string; cover: string; + type?: string; } -const AlbumSearchCard: React.FC = ({ +const SearchCard: React.FC = ({ id, rank, artist_name, album_name, cover, + type, }) => { const placeholderImage = "https://upload.wikimedia.org/wikipedia/en/thumb/d/d5/Taylor_Swift_-_1989_%28Taylor%27s_Version%29.png/220px-Taylor_Swift_-_1989_%28Taylor%27s_Version%29.png"; - return ( = ({ router.push({ pathname: "/MediaPage", params: { - mediaType: "album", + mediaType: type, mediaId: id, }, }) } > - {/* Rank */} - {rank}. - - {/* Album Cover */} + {rank ? `${rank}. ` : ""} = ({ /> - {/* Record Image */} = ({ - {/* Album and Artist Name */} {album_name} {artist_name} @@ -79,14 +75,14 @@ const styles = StyleSheet.create({ fontSize: 18, fontWeight: "600", lineHeight: 20, - marginRight: 6, // Spacing between rank and cover image + marginRight: 6, marginTop: -85, }, coverContainer: { - zIndex: 2, // Ensure cover is on top + zIndex: 2, }, recordContainer: { - position: "absolute", // Position record on top of cover + position: "absolute", bottom: 5, left: "50%", transform: [{ translateX: 0 }], @@ -116,4 +112,4 @@ const styles = StyleSheet.create({ }, }); -export default AlbumSearchCard; +export default SearchCard; diff --git a/frontend/components/search/Filter.tsx b/frontend/components/search/Filter.tsx index f67779ba..dcb0e3dd 100644 --- a/frontend/components/search/Filter.tsx +++ b/frontend/components/search/Filter.tsx @@ -15,7 +15,7 @@ const Filter: React.FC = ({ return ( - {filterOptions.map((filter) => ( + {filterOptions.map((filter: FilterOption) => ( onFilterChange(filter)} diff --git a/frontend/components/search/ProfileChip.tsx b/frontend/components/search/ProfileChip.tsx index b78b898f..cd90c9d4 100644 --- a/frontend/components/search/ProfileChip.tsx +++ b/frontend/components/search/ProfileChip.tsx @@ -29,23 +29,16 @@ const ProfileChip: React.FC = ({ } > - {/* Record image background */} - - {/* Profile picture overlaid in the center */} - {profile_picture ? ( - - ) : ( - - )} + {display_name} diff --git a/frontend/components/search/Profiles.tsx b/frontend/components/search/Profiles.tsx index bf6d162d..59d45ecb 100644 --- a/frontend/components/search/Profiles.tsx +++ b/frontend/components/search/Profiles.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet, ScrollView } from "react-native"; +import { View, Text, StyleSheet } from "react-native"; import ProfileChip from "@/components/search/ProfileChip"; type ProfileChipProps = { @@ -8,6 +8,7 @@ type ProfileChipProps = { const Profiles = ({ profiles }: ProfileChipProps) => { const limitedProfiles = profiles?.slice(0, 2); + return ( Profiles @@ -15,10 +16,10 @@ const Profiles = ({ profiles }: ProfileChipProps) => { {limitedProfiles?.map((profile) => ( ))} diff --git a/frontend/components/search/SearchResults.tsx b/frontend/components/search/SearchResults.tsx index 641f09e5..f4648c1b 100644 --- a/frontend/components/search/SearchResults.tsx +++ b/frontend/components/search/SearchResults.tsx @@ -1,10 +1,16 @@ import React, { useState } from "react"; -import { StyleSheet, View, Text, ScrollView, Dimensions } from "react-native"; +import { + StyleSheet, + View, + Text, + ScrollView, + TouchableOpacity, +} from "react-native"; import SongChip from "@/components/search/SongChip"; -import AlbumSearchCard from "@/components/search/AlbumSearchCard"; +import SearchCard from "@/components/search/AlbumSearchCard"; import ProfileChip from "@/components/search/ProfileChip"; import Filter from "@/components/search/Filter"; -import { takeWhile } from "lodash"; +import AntDesign from "@expo/vector-icons/AntDesign"; interface SearchResultsProps { songs: Media[]; @@ -28,8 +34,7 @@ const SearchResults: React.FC = ({ return No results found; } - const filterOptions = ["all", "songs", "albums", "profile"]; - + const filterOptions: FilterOption[] = ["all", "songs", "albums", "profile"]; const [selectedFilter, setSelectedFilter] = useState("all"); const handleFilterChange = (filter: FilterOption) => { @@ -45,70 +50,145 @@ const SearchResults: React.FC = ({ /> - {(selectedFilter === "all" || selectedFilter === "profile") && - profiles?.length > 0 && ( - + {(selectedFilter === "all" || selectedFilter === "profile") && ( + + {selectedFilter === "all" ? ( + setSelectedFilter("profile")}> + + Profiles + + + ) : ( Profiles - {profiles?.map((profile, idx) => ( - - ))} - - )} - - {(selectedFilter === "all" || selectedFilter === "songs") && ( - - Albums - - {albums.map((album, index) => ( - - ))} + )} + + {profiles == null || profiles.length === 0 ? ( + No profiles found + ) : selectedFilter === "all" ? ( + (console.log(profiles[0]), + profiles + .slice(0, 2) + .map((profile, idx) => ( + + ))) + ) : ( + profiles.map((profile, idx) => ( + + )) + )} )} + {(selectedFilter === "all" || selectedFilter === "songs") && ( - - Songs - - {songs?.map((song, index) => ( - - ))} - + + {selectedFilter === "all" ? ( + setSelectedFilter("songs")}> + + Songs + + + ) : ( + Songs + )} + {songs.length === 0 ? ( + No songs found + ) : selectedFilter === "songs" ? ( + + {songs?.map((song, idx) => ( + + + + ))} + + ) : ( + + + + {songs?.slice(0, 9).map((song, idx) => ( + + + + ))} + + + + )} )} - {(selectedFilter === "all" || selectedFilter === "songs") && ( - - Songs - - {songs?.map((song, index) => ( - - ))} - + {(selectedFilter === "all" || selectedFilter === "albums") && ( + + {selectedFilter === "all" ? ( + setSelectedFilter("albums")}> + + Albums + + + ) : ( + Albums + )} + + {albums.length === 0 ? ( + No albums found + ) : ( + (selectedFilter === "all" ? albums.slice(0, 2) : albums)?.map( + (album, idx) => ( + + + + ), + ) + )} + )} @@ -118,43 +198,25 @@ const SearchResults: React.FC = ({ }; const styles = StyleSheet.create({ + profileContainer: { + flexDirection: "row", + flexWrap: "wrap", + justifyContent: "flex-start", + }, title: { fontSize: 24, fontWeight: "bold", - padding: 16, + paddingVertical: 16, }, container: { flex: 1, - paddingHorizontal: 16, - }, - headerContainer: { - marginTop: 10, - marginBottom: 20, - fontSize: 16, - textAlign: "center", - fontWeight: "600", - color: "#000000", - }, - section: { - marginTop: 20, - }, - sectionTitle: { - fontSize: 20, - fontWeight: "600", - marginBottom: 10, - color: "#434343", + paddingHorizontal: 24, }, loadingText: { textAlign: "center", marginTop: 20, color: "#666666", }, - twoColumnList: { - flex: 1, - flexDirection: "row", - flexWrap: "wrap", - justifyContent: "space-between", - }, resultGrid: { flexDirection: "row", flexWrap: "wrap", @@ -165,18 +227,21 @@ const styles = StyleSheet.create({ width: "100%", }, albumsList: { - width: "100%", + width: "50%", marginBottom: 16, - paddingHorizontal: 4, }, - albumsList: { - width: "48%", // Slightly less than 50% to allow for spacing + gridContainer: { + flexDirection: "row", + flexWrap: "wrap", + width: 500, + }, + gridItem: { + width: "33.33%", marginBottom: 16, - paddingHorizontal: 4, }, noResults: { textAlign: "center", - marginTop: 20, + marginTop: 6, color: "#666666", }, }); diff --git a/frontend/components/search/SongChip.tsx b/frontend/components/search/SongChip.tsx index a61c8244..83df0b28 100644 --- a/frontend/components/search/SongChip.tsx +++ b/frontend/components/search/SongChip.tsx @@ -7,7 +7,7 @@ interface SongChipProps { title: string; artist_name: string; cover: string; - rank?: number; + rank: number; } const SongChip: React.FC = ({ @@ -30,7 +30,7 @@ const SongChip: React.FC = ({ }) } > - {rank !== undefined && {rank}.} + {rank && {`${rank}. `}} @@ -61,8 +61,8 @@ const styles = StyleSheet.create({ width: 20, }, cover: { - width: 40, - height: 40, + width: 32, + height: 32, borderRadius: 4, backgroundColor: "#e0e0e0", }, @@ -74,6 +74,7 @@ const styles = StyleSheet.create({ noRankTextContainer: { marginLeft: 8, }, + title: { fontSize: 13, color: "#000000", diff --git a/frontend/components/search/TopAlbums.tsx b/frontend/components/search/TopAlbums.tsx index 0f8bebca..b2d19d9a 100644 --- a/frontend/components/search/TopAlbums.tsx +++ b/frontend/components/search/TopAlbums.tsx @@ -1,8 +1,6 @@ import React from "react"; -import { useState } from "react"; - -import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native"; -import AlbumSearchCard from "@/components/search/AlbumSearchCard"; +import { View, Text, StyleSheet } from "react-native"; +import SearchCard from "@/components/search/AlbumSearchCard"; import { ScrollView } from "react-native"; type AlbumCardProps = { @@ -10,9 +8,6 @@ type AlbumCardProps = { }; const TopAlbums = ({ albums }: AlbumCardProps) => { - const image = - "https://upload.wikimedia.org/wikipedia/en/thumb/d/d5/Taylor_Swift_-_1989_%28Taylor%27s_Version%29.png/220px-Taylor_Swift_-_1989_%28Taylor%27s_Version%29.png"; - return ( Top Albums @@ -23,11 +18,12 @@ const TopAlbums = ({ albums }: AlbumCardProps) => { showsHorizontalScrollIndicator={false} > {albums?.map((album, index) => ( - diff --git a/frontend/components/search/TopSongs.tsx b/frontend/components/search/TopSongs.tsx index 941a5cc6..dc26f99c 100644 --- a/frontend/components/search/TopSongs.tsx +++ b/frontend/components/search/TopSongs.tsx @@ -6,7 +6,6 @@ type SongCardProp = { }; const TopSongs = ({ songs }: SongCardProp) => { - // Take only the first 9 songs const topNineSongs = songs?.slice(0, 9); return ( @@ -38,13 +37,12 @@ const styles = StyleSheet.create({ gridContainer: { flexDirection: "row", flexWrap: "wrap", - paddingHorizontal: 20, - width: 500, // Fixed width + paddingHorizontal: 16, + width: 500, }, gridItem: { width: "33.33%", marginBottom: 16, - fontWeight: "light", }, title: { fontSize: 20, @@ -52,6 +50,7 @@ const styles = StyleSheet.create({ paddingBottom: 12, paddingTop: 32, fontWeight: "bold", + padding: 16, }, }); diff --git a/frontend/hooks/useProfile.ts b/frontend/hooks/useProfile.ts index e0904032..5cf2ad1d 100644 --- a/frontend/hooks/useProfile.ts +++ b/frontend/hooks/useProfile.ts @@ -28,6 +28,7 @@ export function useProfile(userId: string) { const response = await axios.get( `${BASE_URL}/users/profile/id/${userId}`, ); + console.log("Profile:", response.data); const profile = { user_id: response.data.user_id, username: response.data.username, @@ -38,6 +39,7 @@ export function useProfile(userId: string) { followed: response.data.followed, score: response.data.score, }; + setUserProfile(profile); setBio(response.data.bio.String); } catch (error) { @@ -82,8 +84,11 @@ export function useProfile(userId: string) { } }, [userId]); - const handleActivityPress = () => { - router.push("/Activity"); + const handleActivityPress = (userId: string) => { + router.push({ + pathname: "/Activity", + params: { userId: userId }, + }); }; const handleOnQueuePress = () => { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ef2dee8f..46645e65 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8042,16 +8042,6 @@ "regenerator-transform": "^0.10.0" } }, - "node_modules/babel-plugin-transform-regenerator/node_modules/regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dependencies": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, "node_modules/babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", @@ -9705,6 +9695,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -11700,6 +11691,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15276,10 +15268,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "punycode": "^2.1.1" }, "engines": { "node": ">= 6" @@ -17885,6 +17876,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -19641,6 +19633,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -19781,6 +19774,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" }