Skip to content

Commit

Permalink
Adding Leaderboard updates (#46)
Browse files Browse the repository at this point in the history
* Fixed get followers, working on Leader refactor

* did leader refactor

* Fixed leader count pairing

* frontend addition part 1

* Wrote popular user component stub, working through popularLeaderboard

* more fe work

* hooked service, started PopularUser.tsx formatting

* Temp commit, broken fonts, need to add scrollable

* Working leaderboard scroll, popular user, Popular leaderboard. Next fix leaderboard page, rebase, then add AllTimeBests options

* Working leaderboard, pending allTimeBests

* Update Leader Model

* Added trending user crud routes, trending page

* Push for merge. All working, needs formatting to match designers in TrendingUser

* trending update

* attempt

* linter fixes

* more linter issues

* accidently pushed onboarding reducer thing

---------

Co-authored-by: CamPlume1 <[email protected]>
Co-authored-by: leoRysing <[email protected]>
  • Loading branch information
3 people authored Apr 17, 2024
1 parent 286a57e commit 4b1952e
Show file tree
Hide file tree
Showing 24 changed files with 806 additions and 8 deletions.
20 changes: 20 additions & 0 deletions backend/src/controllers/followings.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,26 @@ func (fol *FollowingController) GetFollowers(c *gin.Context) {
c.JSON(http.StatusOK, followings)
}

// GetLeaders godoc
//
// @Summary Gets the 10 users with the most followers
// @ID get-leaders
// @Tags user-followings
// @Produce json
// @Success 200 {object} []models.Leaders
// @Failure 404 {string} string "Failed to fetch leaders: 404 Error"
// @Router /api/leaders [get]
func (fol *FollowingController) GetLeaders(c *gin.Context) {
leaders, err := fol.followingService.GetLeaders()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users", "msg": err})
return
}
c.JSON(http.StatusOK, leaders)
}

//////////////////////////////////////////Create////////////////////////////////////////

// CreateFollowings godoc
//
// @Summary Creates a Followings relation
Expand Down
37 changes: 37 additions & 0 deletions backend/src/controllers/trending_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package controllers

import (
"backend/src/services"
"github.com/gin-gonic/gin"
"net/http"
)

type TrendingController struct {
trendingService *services.TrendingUserService
}

func NewTrendingController(trendingService *services.TrendingUserService) *TrendingController {
return &TrendingController{
trendingService: trendingService,
}
}

// GetTrending godoc
//
// @Summary Gets all trending Users
// @Description Returns all trending users as trending user objects
// @ID get-all-followings
// @Tags followings
// @Produce json
// @Success 200 {object} []models.Followings
// @Failure 404 {string} string "Failed to fetch followers: 404 Error"
// @Router /api/following/ [get]
func (tre *TrendingController) GetTrending(c *gin.Context) {
trending, err := tre.trendingService.GetTrendingUsers()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users", "msg": err})
return
}

c.JSON(http.StatusOK, trending)
}
10 changes: 9 additions & 1 deletion backend/src/db/migrations/5_USER_PORTFOLIO_V1.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,12 @@ CREATE TABLE user_portfolios
day_gain_pct NUMERIC(12, 2),
total_gain NUMERIC(12, 2),
total_gain_pct NUMERIC(12, 2)
);
);


INSERT INTO user_portfolios (user_id, day_gain, day_gain_pct, total_gain, total_gain_pct)
VALUES
('user_2chL8dX6HdbBAuvu3DDM9f9NzKK', 130, 14, 680, 93),
('user_2cpFbBLPGkPbszijtQneek7ZJxg', -14, -8, 680, 93),
('user_2dv5XFsCMYc4qLcsAnEJ1aUbxnk', 400, 3, 680, 93),
('user_2cwGfu9zcjsbxq5Lp8gy2rkVNlc', 200, 9, 680, 93);
9 changes: 9 additions & 0 deletions backend/src/models/leader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package models

type Leader struct {
FollowingUserID string `gorm:"type:varchar(255);" json:"following_user_id"`
FollowerCount uint `gorm:"type:int;" json:"follower_count"`

//Must be Preloaded
FollowingUser User `gorm:"foreignKey:FollowingUserID;references:ID" json:"leader_user"`
}
17 changes: 17 additions & 0 deletions backend/src/models/trending_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package models

type TrendingUser struct {
UserID string `gorm:"type:varchar(255);" json:"user_id"`
// Retrieve from positions
DayGainPct float64 `gorm:"type:numeric(12,2);not null" json:"day_gain_pct"`

//Must be Preloaded
TrendingUserReference User `gorm:"foreignKey:UserID;references:ID" json:"trending_user_reference"`
}

func NewTrendingUser(userID string, gainPCT float64) *TrendingUser {
return &TrendingUser{
UserID: userID,
DayGainPct: gainPCT,
}
}
12 changes: 12 additions & 0 deletions backend/src/routes/followings.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
func SetupFollowingRoutes(router *gin.Engine, db *gorm.DB) {
followingService := services.NewFollowingService(db)
followingController := controllers.NewFollowingController(followingService)
trendingService := services.NewTrendingUserService(db)
trendingController := controllers.NewTrendingController(trendingService)

followingRoutes := router.Group("/following")
{
Expand All @@ -38,4 +40,14 @@ func SetupFollowingRoutes(router *gin.Engine, db *gorm.DB) {
followersRoutes.GET("/:following_user_id", followingController.GetFollowers)
}

leaderRoutes := router.Group("/leaders")
{
//Get the 10 users with the most followers
leaderRoutes.GET("/", followingController.GetLeaders)
}
trendingRoutes := router.Group("/trending")
{
trendingRoutes.GET("/", trendingController.GetTrending)
}

}
34 changes: 34 additions & 0 deletions backend/src/services/followings.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,40 @@ func (fol *FollowingService) GetAllFollowings() ([]models.Followings, error) {
return following, nil
}

// GetLeaders retrieves the users with the most followers
//
// This method queries the database to fetch all Followings relations, including details about the follower and followed users.
//
// Returns:
// - A slice of User models containing the retrieved User relations.
// - An error if any database operation fails.
//
// Finish writing this sql query in the below Gorm function
// SQL Equivalent:
// SELECT followed
// FROM followings
// GROUP BY followed
// ORDER BY COUNT(*) DESC;

// TODO
func (fol *FollowingService) GetLeaders() ([]models.Leader, error) {

var leaders []models.Leader

if err := fol.DB.Table("followings").
//Preload("FollowerUser").
Preload("FollowingUser").
Select("following_user_id, COUNT(*) as follower_count").
Group("following_user_id").
Order("follower_count DESC").
Limit(10).
Find(&leaders).Error; err != nil {
return nil, err
}

return leaders, nil
}

// GetTimeline retrieves all followings where the specified user is the follower.
//
// This method queries the database to fetch all followings where the given user is the follower,
Expand Down
32 changes: 32 additions & 0 deletions backend/src/services/trending_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package services

import (
"backend/src/models"
"gorm.io/gorm"
)

type TrendingUserService struct {
DB *gorm.DB
}

func NewTrendingUserService(db *gorm.DB) *TrendingUserService {
return &TrendingUserService{
DB: db,
}
}

func (tre *TrendingUserService) GetTrendingUsers() ([]models.TrendingUser, error) {

var trending []models.TrendingUser

if err := tre.DB.Table("user_portfolios").
Preload("TrendingUserReference").
Select("user_id, day_gain_pct").
Order("day_gain_pct DESC").
Limit(10).
Find(&trending).Error; err != nil {
return nil, err
}

return trending, nil
}
Binary file added frontend/assets/followers_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/temp_pfp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/trend-down-red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/trend-up-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions frontend/components/PopularLeaderboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { ScrollView, View } from 'react-native';
import { Leader } from '../types/types';
import PopularUser from './PopularUser';



type PopularProps = {
leaderboard: Leader[];
}

const PopularLeaderboard: React.FC<PopularProps> = ({leaderboard}: PopularProps) => {

return (
<ScrollView>
{
leaderboard.map((leader, index) => (
<React.Fragment key={index}>
<PopularUser leader={leader} />
{index < leaderboard.length - 1 && <Separator />}
</React.Fragment>
))}
</ScrollView>
)
}
const Separator = () => <View style={{ height: 15 }} />;

export default PopularLeaderboard;
28 changes: 28 additions & 0 deletions frontend/components/PopularTrendingBoard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { ScrollView, View } from 'react-native';
import { Trending } from '../types/types';
import TrendingUser from './TrendingUser';



type TrendingProps = {
trendingboard: Trending[];
}

const PopularTrendingBoard: React.FC<TrendingProps> = ({trendingboard}: TrendingProps) => {

return (
<ScrollView>
{
trendingboard.map((trending, index) => (
<React.Fragment key={index}>
<TrendingUser trending={trending} />
{index < trendingboard.length - 1 && <Separator />}
</React.Fragment>
))}
</ScrollView>
)
}
const Separator = () => <View style={{ height: 15 }} />;

export default PopularTrendingBoard;
104 changes: 104 additions & 0 deletions frontend/components/PopularUser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
import React from 'react';
import {Image, StyleSheet, Text, View} from 'react-native';
import { Leader } from '../types/types';

type PopularUserProps = {
leader: Leader;
}

const PopularUser: React.FC<PopularUserProps> = ({ leader }: PopularUserProps) => {
return (
<View style={styles.container}>
{/* Column for image */}
<View style={[styles.column, styles.imageColumn]}>
<View style={styles.imageContainer}>
<Image
source={{uri: leader.leader_user.image_url}}
style={styles.image}
/>
</View>
</View>

<View style={[styles.column, styles.textColumn]}>
<View style={styles.textContainer}>
<Text style={styles.nameText}>
{leader.leader_user.first_name} {leader.leader_user.last_name}
</Text>
<Text style={styles.actionText}>
Recent: Recent actions will display{"\n"} here
</Text>
</View>
</View>


<View style={[styles.column, styles.followersColumn]}>
<Image
source={require("../assets/followers_logo.png")}
style={styles.followersLogo}
/>
<Text style={styles.followersText}>
{leader.follower_count}
</Text>
</View>
</View>
);
};

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 10,
},
column: {
flex: 1,
},
imageColumn: {
flex: 0.17,
},
textColumn: {
flex: 0.65,
},
followersColumn: {
flex: 0.18,
flexDirection: 'row', // Check
justifyContent: 'flex-end', //check
},
imageContainer: {
width: 40,
height: 40,
borderRadius: 20,
overflow: 'hidden',
},
image: {
width: "100%",
height: "100%",
},
textContainer: {
flex: 1,
},
actionText: {
fontSize: 8,
color: '#787878',
},
nameText: {
fontWeight: 'bold',
fontSize: 12,
marginBottom: 1,
color: '#787878',
},
followersText: {
alignSelf: 'flex-end',
color: '#787878',
},
followersLogo: {
width: 20,
height: 20,
marginRight: 5,
}
});

export default PopularUser;
Loading

0 comments on commit 4b1952e

Please sign in to comment.