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"
}