diff --git a/backend/src/controllers/followings.go b/backend/src/controllers/followings.go index 654f96fa..f527ea9a 100644 --- a/backend/src/controllers/followings.go +++ b/backend/src/controllers/followings.go @@ -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 diff --git a/backend/src/controllers/trending_user.go b/backend/src/controllers/trending_user.go new file mode 100644 index 00000000..e557c510 --- /dev/null +++ b/backend/src/controllers/trending_user.go @@ -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) +} diff --git a/backend/src/db/migrations/5_USER_PORTFOLIO_V1.sql b/backend/src/db/migrations/5_USER_PORTFOLIO_V1.sql index 560c4901..bc9f02af 100644 --- a/backend/src/db/migrations/5_USER_PORTFOLIO_V1.sql +++ b/backend/src/db/migrations/5_USER_PORTFOLIO_V1.sql @@ -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) -); \ No newline at end of file +); + + +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); \ No newline at end of file diff --git a/backend/src/models/leader.go b/backend/src/models/leader.go new file mode 100644 index 00000000..b1ad965f --- /dev/null +++ b/backend/src/models/leader.go @@ -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"` +} diff --git a/backend/src/models/trending_user.go b/backend/src/models/trending_user.go new file mode 100644 index 00000000..4bd956e6 --- /dev/null +++ b/backend/src/models/trending_user.go @@ -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, + } +} diff --git a/backend/src/routes/followings.go b/backend/src/routes/followings.go index 270a43ae..e381d804 100644 --- a/backend/src/routes/followings.go +++ b/backend/src/routes/followings.go @@ -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") { @@ -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) + } + } diff --git a/backend/src/services/followings.go b/backend/src/services/followings.go index 5e658368..be6b6bcb 100644 --- a/backend/src/services/followings.go +++ b/backend/src/services/followings.go @@ -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, diff --git a/backend/src/services/trending_user.go b/backend/src/services/trending_user.go new file mode 100644 index 00000000..3a9ed07a --- /dev/null +++ b/backend/src/services/trending_user.go @@ -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 +} diff --git a/frontend/assets/followers_logo.png b/frontend/assets/followers_logo.png new file mode 100644 index 00000000..9d7615ff Binary files /dev/null and b/frontend/assets/followers_logo.png differ diff --git a/frontend/assets/temp_pfp.png b/frontend/assets/temp_pfp.png new file mode 100644 index 00000000..deebde98 Binary files /dev/null and b/frontend/assets/temp_pfp.png differ diff --git a/frontend/assets/trend-down-red.png b/frontend/assets/trend-down-red.png new file mode 100644 index 00000000..e3163ad2 Binary files /dev/null and b/frontend/assets/trend-down-red.png differ diff --git a/frontend/assets/trend-up-green.png b/frontend/assets/trend-up-green.png new file mode 100644 index 00000000..37d430ac Binary files /dev/null and b/frontend/assets/trend-up-green.png differ diff --git a/frontend/components/PopularLeaderboard.tsx b/frontend/components/PopularLeaderboard.tsx new file mode 100644 index 00000000..93371238 --- /dev/null +++ b/frontend/components/PopularLeaderboard.tsx @@ -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 = ({leaderboard}: PopularProps) => { + + return ( + + { + leaderboard.map((leader, index) => ( + + + {index < leaderboard.length - 1 && } + + ))} + + ) +} +const Separator = () => ; + +export default PopularLeaderboard; \ No newline at end of file diff --git a/frontend/components/PopularTrendingBoard.tsx b/frontend/components/PopularTrendingBoard.tsx new file mode 100644 index 00000000..dec1d470 --- /dev/null +++ b/frontend/components/PopularTrendingBoard.tsx @@ -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 = ({trendingboard}: TrendingProps) => { + + return ( + + { + trendingboard.map((trending, index) => ( + + + {index < trendingboard.length - 1 && } + + ))} + + ) +} +const Separator = () => ; + +export default PopularTrendingBoard; \ No newline at end of file diff --git a/frontend/components/PopularUser.tsx b/frontend/components/PopularUser.tsx new file mode 100644 index 00000000..626f32ab --- /dev/null +++ b/frontend/components/PopularUser.tsx @@ -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 = ({ leader }: PopularUserProps) => { + return ( + + {/* Column for image */} + + + + + + + + + + {leader.leader_user.first_name} {leader.leader_user.last_name} + + + Recent: Recent actions will display{"\n"} here + + + + + + + + + {leader.follower_count} + + + + ); +}; + +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; diff --git a/frontend/components/TabHeader.tsx b/frontend/components/TabHeader.tsx new file mode 100644 index 00000000..f4a9927a --- /dev/null +++ b/frontend/components/TabHeader.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; + +type TabParams = { + activeTab: string, + allTabs: string[], + setTab: React.Dispatch>, +} + +const TabHeader: React.FC = ({activeTab, allTabs, setTab}: TabParams) => { + return ( + + { + allTabs.map((item) => { + return ( + setTab(item)}> + {item} + + ) + }) + } + + ) +} +const styles = StyleSheet.create({ + container: { + display: "flex", + justifyContent: 'space-between', + width: "100%", + flexDirection: "row", + gap: 12, + marginBottom: 12, + marginTop: 12, + }, + active: { + color: "#333333", + fontSize: 24, + borderBottomColor: "#333333", + borderBottomWidth: 2, + flex: 1, + alignItems: "center", + paddingBottom: 8, + }, + inactive: { + color: "#777777", + fontSize: 24, + flex: 1, + alignItems: "center", + paddingBottom: 8, + }, +}); + +export default TabHeader; \ No newline at end of file diff --git a/frontend/components/TrendingUser.tsx b/frontend/components/TrendingUser.tsx new file mode 100644 index 00000000..22e3b526 --- /dev/null +++ b/frontend/components/TrendingUser.tsx @@ -0,0 +1,121 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +import React from 'react'; +import {Image, StyleSheet, Text, View} from 'react-native'; +import { Trending } from '../types/types'; +//import { FontAwesome } from '@expo/vector-icons'; + +type TrendingUserProps = { +trending: Trending; +} + +const TrendingUser: React.FC = ({ trending }: TrendingUserProps) => { + console.log(trending) + return ( + + {/* Column for image */} + + + + + + + + + + {trending.trending_user_reference.first_name} {trending.trending_user_reference.last_name} + + + Recent: Recent actions will display{"\n"} here + + + + + + + {/* { + trending.day_gain_pct > 0 ? + ( + + ) : ( + + ) + } */} + + {trending.day_gain_pct}% + + + + ); +}; + +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', + justifyContent: 'flex-end', + }, + 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, + }, + greenIcon: { + width: 20, + height: 20, + marginRight: 5, + color: 'green', // Adjust the color as needed + }, + redIcon: { + width: 20, + height: 20, + marginRight: 5, + color: 'red', // Adjust the color as needed + }, +}); + +export default TrendingUser; diff --git a/frontend/package.json b/frontend/package.json index c8fda7ca..b2583a70 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,11 @@ "@babel/runtime": "^7.20.6", "@clerk/clerk-expo": "^0.20.5", "@clerk/types": "^3.62.1", + "@emotion/styled": "^11.11.5", "@expo/ngrok": "^2.5.0", + "@expo/vector-icons": "^13.0.0", + "@mui/icons-material": "^5.15.15", + "@mui/material": "^5.15.15", "@react-native-community/slider": "^4.5.0", "@react-native/metro-config": "^0.73.5", "@react-navigation/bottom-tabs": "^6.5.11", diff --git a/frontend/pages/Leaderboard.tsx b/frontend/pages/Leaderboard.tsx new file mode 100644 index 00000000..c2b5dd16 --- /dev/null +++ b/frontend/pages/Leaderboard.tsx @@ -0,0 +1,81 @@ +import React, {useEffect, useState} from 'react'; +import { StyleSheet, View } from 'react-native'; +import { Text } from 'react-native-paper'; +import PopularLeaderboard from '../components/PopularLeaderboard'; +import PopularTrendingBoard from '../components/PopularTrendingBoard'; +import TabHeader from '../components/TabHeader'; +import {Leader, Trending} from "../types/types"; +import {getPopularLeaderboard, getPopularTrending} from "../services/leaderboard"; + +const Leaderboard: React.FC = () => { + //Fetch Leaders + const [leaderboard, setLeaderboard] = useState([]); + const [trendingboard, setTrendingBoard] = useState([]); + useEffect(() => { + const fetchLeaderboard = async () => { + try { + const leaders = await getPopularLeaderboard(); + setLeaderboard(leaders); + } catch (error) { + console.error('Error fetching leaderboard:', error); + } + }; + + const fetchTrendingBoard = async () => { + try { + const trending = await getPopularTrending(); + setTrendingBoard(trending); + } catch (error) { + console.error('Error fetching trendingboard') + } + + }; + + fetchLeaderboard(); + fetchTrendingBoard(); + }, []); + + + + //END ATTEMPT + const [tab, setTab] = useState("Popular Now"); + const allTabs = ["Popular Now", "All time Bests"]; + return ( + + Leaderboard + + + { + tab == 'Popular Now' ? + () : + () + } + + + ) //Can't figure out warnings, or style tab bar. Moving on. +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'flex-start', + justifyContent: 'flex-start', + padding: 20, + backgroundColor: '#f5f5f5', + }, + leaderboard: { + flex: 1, + justifyContent: 'flex-start', + borderColor: '#999999', + borderWidth: 2, + marginTop: 12, + padding: 0, + }, + tabBar: { + marginHorizontal: 80, + paddingHorizontal: 80, + } + +}); + +export default Leaderboard; \ No newline at end of file diff --git a/frontend/router/BottomNavBar.tsx b/frontend/router/BottomNavBar.tsx index a08cb15f..d5358b5a 100644 --- a/frontend/router/BottomNavBar.tsx +++ b/frontend/router/BottomNavBar.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; -import AuthPage from '../pages/AuthPage'; +//import AuthPage from '../pages/AuthPage'; import { Icon } from '@rneui/themed'; import { RouteProp } from '@react-navigation/native'; import Profile from '../pages/Profile'; import FeedPage from '../pages/FeedPage'; +import Leaderboard from '../pages/Leaderboard'; // import AuthNavigator from './AuthNavigation'; // import { useSession } from '@clerk/clerk-expo'; @@ -54,7 +55,7 @@ const BottomNavBar = () => { /> => { + const response: AxiosResponse = await axios.get( + `http://${API_LINK}/leaders/`, + ); + console.log(response.data); + return response.data; +} + +export const getPopularTrending = async (): Promise => { + const response: AxiosResponse = await axios.get( + `http://${API_LINK}/trending/`, + ); + console.log(response.data); + return response.data; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 2e245b05..f7dc7bca 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -4,5 +4,9 @@ "types": ["node"], // "jsx": "react", - } + }, + "include": [ + "frontend", // Assuming your source code is located in the "src" directory + "assets" // Add the "assets" directory to include + ] } diff --git a/frontend/types/types.d.ts b/frontend/types/types.d.ts index a2bfbae9..da8fdf35 100644 --- a/frontend/types/types.d.ts +++ b/frontend/types/types.d.ts @@ -6,6 +6,7 @@ export interface User { email: string; risk_tolerance: string; years_of_experience: number; + image_url: string; } export type FinancialGoal = { @@ -18,6 +19,16 @@ export interface Redirect { redirect_url: string; } +export type Leader = { + leader_user: User; + follower_count: number; +} + +export type Trending = { + trending_user_reference: User; + day_gain_pct: number; +} + export interface TokenStatus { status: string; } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 8b16f144..6048683b 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1259,6 +1259,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" + integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.0.0", "@babel/template@^7.22.15", "@babel/template@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" @@ -1421,6 +1428,13 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== +"@emotion/is-prop-valid@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337" + integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/memoize@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" @@ -1451,11 +1465,34 @@ "@emotion/utils" "^1.2.1" csstype "^3.0.2" +"@emotion/serialize@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.4.tgz#fc8f6d80c492cfa08801d544a05331d1cc7cd451" + integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + "@emotion/sheet@^1.2.2": version "1.2.2" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== +"@emotion/styled@^11.11.5": + version "11.11.5" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.5.tgz#0c5c8febef9d86e8a926e663b2e5488705545dfb" + integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/is-prop-valid" "^1.2.2" + "@emotion/serialize" "^1.1.4" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" @@ -1914,6 +1951,11 @@ dependencies: cross-spawn "^7.0.3" +"@expo/vector-icons@^13.0.0": + version "13.0.0" + resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-13.0.0.tgz#e2989b85e95a82bce216f88cf8fb583ab050ec95" + integrity sha512-TI+l71+5aSKnShYclFa14Kum+hQMZ86b95SH6tQUG3qZEmLTarvWpKwqtTwQKqvlJSJrpFiSFu3eCuZokY6zWA== + "@expo/vector-icons@^14.0.0": version "14.0.0" resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-14.0.0.tgz#48ce0aa5c05873b07c0c78bfe16c870388f4de9a" @@ -1944,7 +1986,7 @@ "@floating-ui/core" "^1.0.0" "@floating-ui/utils" "^0.2.0" -"@floating-ui/react-dom@^2.0.2": +"@floating-ui/react-dom@^2.0.2", "@floating-ui/react-dom@^2.0.8": version "2.0.8" resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.8.tgz#afc24f9756d1b433e1fe0d047c24bd4d9cefaa5d" integrity sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw== @@ -2134,6 +2176,97 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@mui/base@5.0.0-beta.40": + version "5.0.0-beta.40" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2" + integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ== + dependencies: + "@babel/runtime" "^7.23.9" + "@floating-ui/react-dom" "^2.0.8" + "@mui/types" "^7.2.14" + "@mui/utils" "^5.15.14" + "@popperjs/core" "^2.11.8" + clsx "^2.1.0" + prop-types "^15.8.1" + +"@mui/core-downloads-tracker@^5.15.15": + version "5.15.15" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz#2bc2bda50db66c12f10aefec907c48c8f669ef59" + integrity sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg== + +"@mui/icons-material@^5.15.15": + version "5.15.15" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.15.tgz#84ce08225a531d9f5dc5132009d91164b456a0ae" + integrity sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g== + dependencies: + "@babel/runtime" "^7.23.9" + +"@mui/material@^5.15.15": + version "5.15.15" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.15.tgz#e3ba35f50b510aa677cec3261abddc2db7b20b59" + integrity sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/base" "5.0.0-beta.40" + "@mui/core-downloads-tracker" "^5.15.15" + "@mui/system" "^5.15.15" + "@mui/types" "^7.2.14" + "@mui/utils" "^5.15.14" + "@types/react-transition-group" "^4.4.10" + clsx "^2.1.0" + csstype "^3.1.3" + prop-types "^15.8.1" + react-is "^18.2.0" + react-transition-group "^4.4.5" + +"@mui/private-theming@^5.15.14": + version "5.15.14" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.14.tgz#edd9a82948ed01586a01c842eb89f0e3f68970ee" + integrity sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/utils" "^5.15.14" + prop-types "^15.8.1" + +"@mui/styled-engine@^5.15.14": + version "5.15.14" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.14.tgz#168b154c4327fa4ccc1933a498331d53f61c0de2" + integrity sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw== + dependencies: + "@babel/runtime" "^7.23.9" + "@emotion/cache" "^11.11.0" + csstype "^3.1.3" + prop-types "^15.8.1" + +"@mui/system@^5.15.15": + version "5.15.15" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.15.tgz#658771b200ce3c4a0f28e58169f02e5e718d1c53" + integrity sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ== + dependencies: + "@babel/runtime" "^7.23.9" + "@mui/private-theming" "^5.15.14" + "@mui/styled-engine" "^5.15.14" + "@mui/types" "^7.2.14" + "@mui/utils" "^5.15.14" + clsx "^2.1.0" + csstype "^3.1.3" + prop-types "^15.8.1" + +"@mui/types@^7.2.14": + version "7.2.14" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.14.tgz#8a02ac129b70f3d82f2f9b76ded2c8d48e3fc8c9" + integrity sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ== + +"@mui/utils@^5.15.14": + version "5.15.14" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.14.tgz#e414d7efd5db00bfdc875273a40c0a89112ade3a" + integrity sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA== + dependencies: + "@babel/runtime" "^7.23.9" + "@types/prop-types" "^15.7.11" + prop-types "^15.8.1" + react-is "^18.2.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2181,6 +2314,11 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@react-native-community/cli-clean@12.3.2": version "12.3.2" resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-12.3.2.tgz#d4f1730c3d22d816b4d513d330d5f3896a3f5921" @@ -2870,6 +3008,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== +"@types/prop-types@^15.7.11": + version "15.7.12" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + "@types/react-native-vector-icons@^6.4.10": version "6.4.18" resolved "https://registry.yarnpkg.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz#18671c617b9d0958747bc959903470dde91a8c79" @@ -2899,6 +3042,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@^4.4.10": + version "4.4.10" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac" + integrity sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.2.46": version "18.2.59" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.59.tgz#14c7bcab22e2ce71d9eaa02f78d3d55067724d7f" @@ -3921,6 +4071,11 @@ clone@^2.1.2: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== +clsx@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" + integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== + color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -4213,7 +4368,7 @@ csstype@3.1.1: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== -csstype@^3.0.2: +csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -4413,6 +4568,14 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -7837,7 +8000,7 @@ prompts@^2.3.2, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -7970,7 +8133,7 @@ react-freeze@^1.0.0: resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.3.tgz#5e3ca90e682fed1d73a7cb50c2c7402b3e85618d" integrity sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.1.0: +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.1.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -8187,6 +8350,16 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"