Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/GenerateNU/platnm into frie…
Browse files Browse the repository at this point in the history
…nds_rating_page
  • Loading branch information
ddusichka committed Dec 4, 2024
2 parents 47f6188 + 3b8402a commit 0fa5809
Show file tree
Hide file tree
Showing 21 changed files with 370 additions and 222 deletions.
7 changes: 7 additions & 0 deletions backend/internal/constants/platnm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package constants

const (
PostReaction = 1
Rating = 5
RecommendationLike = 1
)
1 change: 0 additions & 1 deletion backend/internal/storage/postgres/schema/playlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions backend/internal/storage/postgres/schema/recommendation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"errors"
"platnm/internal/constants"
"platnm/internal/errs"
"platnm/internal/models"
"strconv"
Expand Down Expand Up @@ -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

}
Expand Down
148 changes: 120 additions & 28 deletions backend/internal/storage/postgres/schema/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
"platnm/internal/constants"
"platnm/internal/errs"
"platnm/internal/models"
"strconv"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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, `
Expand All @@ -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, `
Expand Down
10 changes: 2 additions & 8 deletions backend/internal/storage/postgres/schema/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
1 change: 0 additions & 1 deletion backend/internal/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/(tabs)/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default function ProfileScreen() {
<View style={styles.topIconsContainer}>
{/* Activity icon with notification badge */}
<TouchableOpacity
onPress={handleActivityPress}
onPress={() => handleActivityPress(userId)}
style={styles.activityIconContainer}
>
<Icon name="activity" size={24} color="#000" />
Expand Down
1 change: 0 additions & 1 deletion frontend/app/(tabs)/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ const SearchPage: React.FC = () => {
return (
<View style={styles.container}>
<SearchBar onSearch={handleSearch} />

<ScrollView>
{isSearchActive ? (
<SearchResults
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/(tabs)/user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default function ProfilePage() {
<View style={styles.header}>
<View style={styles.topIconsContainer}>
<TouchableOpacity
onPress={handleActivityPress}
onPress={() => handleActivityPress(userId)}
style={styles.activityIconContainer}
>
<Icon name="activity" size={24} color="#000" />
Expand Down
28 changes: 14 additions & 14 deletions frontend/app/Activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Review[]>();
const { userId } = useAuthContext();
const [userReviews, setUserReviews] = useState<Preview[]>();
const loggedInUser = useAuthContext().userId;
const { userId } = useLocalSearchParams<{
userId: string;
}>();

const handleDraftPress = () => {
console.log("Draft Button pressed");
Expand All @@ -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);
Expand All @@ -53,20 +57,16 @@ const Activity = () => {
</SafeAreaView>

{/* Draft Button */}
<TouchableOpacity style={styles.draftButton} onPress={handleDraftPress}>
<Text style={styles.draftButtonText}>Drafts</Text>
</TouchableOpacity>
{loggedInUser === userId && (
<TouchableOpacity style={styles.draftButton} onPress={handleDraftPress}>
<Text style={styles.draftButtonText}>Drafts</Text>
</TouchableOpacity>
)}

{/* User Reviews Section */}
{userReviews && userReviews.length > 0 ? (
userReviews.map((review, index) => {
return (
<ReviewCard
key={index}
rating={review.rating}
comment={review.comment}
/>
);
return <ReviewPreview key={index} preview={review} />;
})
) : (
<Text style={styles.noReviewsText}>No reviews found.</Text>
Expand Down
Loading

0 comments on commit 0fa5809

Please sign in to comment.