From 9540b7f9206b220cbfe2afda6deb61f8f68f97b9 Mon Sep 17 00:00:00 2001 From: lfg2 Date: Fri, 21 Jun 2024 23:01:07 +0800 Subject: [PATCH 1/3] feat:CheckRainbowStatus (#239) * feat:CheckRainbowStatus * Patch rainbow quest status impl --------- Co-authored-by: Brandon Roberts --- backend/quests/status.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/backend/quests/status.go b/backend/quests/status.go index 096b58e1..34d80499 100644 --- a/backend/quests/status.go +++ b/backend/quests/status.go @@ -1,6 +1,8 @@ package quests -import "github.com/keep-starknet-strange/art-peace/backend/core" +import ( + "github.com/keep-starknet-strange/art-peace/backend/core" +) var QuestChecks = map[int]func(*Quest, string) (int, int){ AuthorityQuestType: CheckAuthorityStatus, @@ -97,9 +99,18 @@ func CheckFactionStatus(q *Quest, user string) (progress int, needed int) { return *count, 1 } +type RainbowStatus struct { + Used int `json:"used"` + Colors int `json:"colors"` +} + func CheckRainbowStatus(q *Quest, user string) (progress int, needed int) { - // TODO: Implement this - return 0, 1 + status, err := core.PostgresQueryOne[RainbowStatus]("SELECT COUNT(DISTINCT p.color) as used, (SELECT COUNT(*) FROM Colors) as colors FROM Pixels p WHERE p.address = $1", user) + if err != nil { + return 0, 1 + } + + return status.Used, status.Colors } func CheckTemplateStatus(q *Quest, user string) (progress int, needed int) { From 729c6143c1de3731da31909c56620611ee8fa6b9 Mon Sep 17 00:00:00 2001 From: lfg2 Date: Fri, 21 Jun 2024 23:07:42 +0800 Subject: [PATCH 2/3] feat:CheckHodlStatus (#237) * feat:CheckHodlStatus * fix * Add input type for hodl quest backend --------- Co-authored-by: Brandon Roberts --- backend/quests/inputs.go | 10 ++++++++++ backend/quests/status.go | 9 +++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/quests/inputs.go b/backend/quests/inputs.go index d5d0097a..4406d59b 100644 --- a/backend/quests/inputs.go +++ b/backend/quests/inputs.go @@ -12,6 +12,10 @@ type VoteQuestInputs struct { DayIndex uint32 } +type HodlQuestInputs struct { + Amount int +} + func NewPixelQuestInputs(encodedInputs []int) *PixelQuestInputs { return &PixelQuestInputs{ PixelsNeeded: uint32(encodedInputs[0]), @@ -27,3 +31,9 @@ func NewVoteQuestInputs(encodedInputs []int) *VoteQuestInputs { DayIndex: uint32(encodedInputs[0]), } } + +func NewHodlQuestInputs(encodedInputs []int) *HodlQuestInputs { + return &HodlQuestInputs{ + Amount: encodedInputs[0], + } +} diff --git a/backend/quests/status.go b/backend/quests/status.go index 34d80499..30a9d109 100644 --- a/backend/quests/status.go +++ b/backend/quests/status.go @@ -31,8 +31,13 @@ func CheckAuthorityStatus(q *Quest, user string) (progress int, needed int) { } func CheckHodlStatus(q *Quest, user string) (progress int, needed int) { - // TODO: Implement this - return 0, 1 + hodlQuestInputs := NewHodlQuestInputs(q.InputData) + available, err := core.PostgresQueryOne[int]("SELECT available FROM ExtraPixels WHERE address = $1", user) + + if err != nil { + return 0, hodlQuestInputs.Amount + } + return *available, hodlQuestInputs.Amount } func CheckNftStatus(q *Quest, user string) (progress int, needed int) { From 91819b4e39e749c7d381d4c180f4c2d6b4677b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Ag=C3=BCero?= <54730752+EmmanuelAR@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:27:39 -0600 Subject: [PATCH 3/3] Get hot nfts (#235) * Adding new function to get hot nfts getHotNFTs * Adding new call to get-hot-nfts nfts * Make the call to get hot nfts * Adding recieved address to validate if the user likes one hot nfts * Fix query to get hot nfts * Patch hot nfts query --------- Co-authored-by: Brandon Roberts --- backend/routes/nft.go | 66 +++++++++++++++++++++++++++-- frontend/src/services/apiService.js | 7 +++ frontend/src/tabs/nfts/NFTs.js | 10 ++++- postgres/init.sql | 3 +- 4 files changed, 79 insertions(+), 7 deletions(-) diff --git a/backend/routes/nft.go b/backend/routes/nft.go index ac6f0552..80ada5ec 100644 --- a/backend/routes/nft.go +++ b/backend/routes/nft.go @@ -20,6 +20,7 @@ func InitNFTRoutes() { http.HandleFunc("/like-nft", LikeNFT) http.HandleFunc("/unlike-nft", UnLikeNFT) http.HandleFunc("/get-top-nfts", getTopNFTs) + http.HandleFunc("/get-hot-nfts", getHotNFTs) if !core.ArtPeaceBackend.BackendConfig.Production { http.HandleFunc("/mint-nft-devnet", mintNFTDevnet) } @@ -69,7 +70,7 @@ func getMyNFTs(w http.ResponseWriter, r *http.Request) { SELECT nfts.*, COALESCE(like_count, 0) AS likes, - COALESCE((SELECT true FROM nftlikes WHERE liker = $1), false) as liked + COALESCE((SELECT true FROM nftlikes WHERE liker = $1 AND nftlikes.nftkey = nfts.token_id), false) as liked FROM nfts LEFT JOIN ( @@ -83,6 +84,7 @@ func getMyNFTs(w http.ResponseWriter, r *http.Request) { ) nftlikes ON nfts.token_id = nftlikes.nftKey WHERE nfts.owner = $1 + ORDER BY nfts.token_id DESC LIMIT $2 OFFSET $3` nfts, err := core.PostgresQueryJson[NFTData](query, address, pageLength, offset) if err != nil { @@ -127,7 +129,7 @@ func getNFTs(w http.ResponseWriter, r *http.Request) { SELECT nfts.*, COALESCE(like_count, 0) AS likes, - COALESCE((SELECT true FROM nftlikes WHERE liker = $1), false) as liked + COALESCE((SELECT true FROM nftlikes WHERE liker = $1 AND nftlikes.nftkey = nfts.token_id), false) as liked FROM nfts LEFT JOIN ( @@ -139,6 +141,7 @@ func getNFTs(w http.ResponseWriter, r *http.Request) { GROUP BY nftKey ) nftlikes ON nfts.token_id = nftlikes.nftKey + ORDER BY nfts.token_id DESC LIMIT $2 OFFSET $3` nfts, err := core.PostgresQueryJson[NFTData](query, address, pageLength, offset) if err != nil { @@ -170,7 +173,7 @@ func getNewNFTs(w http.ResponseWriter, r *http.Request) { SELECT nfts.*, COALESCE(like_count, 0) AS likes, - COALESCE((SELECT true FROM nftlikes WHERE liker = $1), false) as liked + COALESCE((SELECT true FROM nftlikes WHERE liker = $1 AND nftlikes.nftkey = nfts.token_id), false) as liked FROM nfts LEFT JOIN ( @@ -317,7 +320,7 @@ func getTopNFTs(w http.ResponseWriter, r *http.Request) { SELECT nfts.*, COALESCE(like_count, 0) AS likes, - COALESCE((SELECT true FROM nftlikes WHERE liker = $1), false) as liked + COALESCE((SELECT true FROM nftlikes WHERE liker = $1 AND nftlikes.nftkey = nfts.token_id), false) as liked FROM nfts LEFT JOIN ( @@ -339,3 +342,58 @@ func getTopNFTs(w http.ResponseWriter, r *http.Request) { } routeutils.WriteDataJson(w, string(nfts)) } + +func getHotNFTs(w http.ResponseWriter, r *http.Request) { + address := r.URL.Query().Get("address") + if address == "" { + address = "0" + } + // hot limit is the number of last likes to consider when calculating hotness + hotLimit, err := strconv.Atoi(r.URL.Query().Get("hotLimit")) + if err != nil || hotLimit <= 0 { + hotLimit = 100 + } + pageLength, err := strconv.Atoi(r.URL.Query().Get("pageLength")) + if err != nil || pageLength <= 0 { + pageLength = 25 + } + if pageLength > 50 { + pageLength = 50 + } + page, err := strconv.Atoi(r.URL.Query().Get("page")) + if err != nil || page <= 0 { + page = 1 + } + offset := (page - 1) * pageLength + + query := ` + SELECT + nfts.*, + COALESCE(like_count, 0) AS likes, + COALESCE((SELECT true FROM nftlikes WHERE liker = $1 AND nftlikes.nftkey = nfts.token_id), false) as liked + FROM + nfts + LEFT JOIN ( + SELECT + nftKey, + COUNT(*) AS like_count FROM nftlikes GROUP BY nftKey + ) nftlikes ON nfts.token_id = nftlikes.nftKey + LEFT JOIN ( + SELECT + latestlikes.nftKey, + COUNT(*) as rank + FROM ( + SELECT * FROM nftlikes + ORDER BY key DESC LIMIT $2 + ) latestlikes + GROUP BY nftkey + ) rank ON nfts.token_id = rank.nftkey + ORDER BY rank DESC + LIMIT $3 OFFSET $4;` + nfts, err := core.PostgresQueryJson[NFTData](query, address, hotLimit, pageLength, offset) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve Hot NFTs") + return + } + routeutils.WriteDataJson(w, string(nfts)) +} diff --git a/frontend/src/services/apiService.js b/frontend/src/services/apiService.js index ad76c3b5..01219b6f 100644 --- a/frontend/src/services/apiService.js +++ b/frontend/src/services/apiService.js @@ -94,3 +94,10 @@ export const getTopNftsFn = async (params) => { `get-top-nfts?address=${queryAddress}&page=${page}&pageLength=${pageLength}` ); }; + +export const getHotNftsFn = async (params) => { + const { page, pageLength, queryAddress } = params; + return await fetchWrapper( + `get-hot-nfts?address=${queryAddress}&page=${page}&pageLength=${pageLength}` + ); +}; diff --git a/frontend/src/tabs/nfts/NFTs.js b/frontend/src/tabs/nfts/NFTs.js index ea54ba89..a86b7e70 100644 --- a/frontend/src/tabs/nfts/NFTs.js +++ b/frontend/src/tabs/nfts/NFTs.js @@ -8,7 +8,8 @@ import { getMyNftsFn, getNftsFn, getNewNftsFn, - getTopNftsFn + getTopNftsFn, + getHotNftsFn } from '../../services/apiService.js'; import { PaginationView } from '../../ui/pagination.js'; @@ -174,7 +175,12 @@ const NFTs = (props) => { async function getNfts() { try { let result; - if (activeFilter === 'new') { + if (activeFilter === 'hot') { + result = await getHotNftsFn({ + page: allNftPagination.page, + pageLength: allNftPagination.pageLength + }); + } else if (activeFilter === 'new') { result = await getNewNftsFn({ page: allNftPagination.page, pageLength: allNftPagination.pageLength diff --git a/postgres/init.sql b/postgres/init.sql index 70886cf2..749b3e74 100644 --- a/postgres/init.sql +++ b/postgres/init.sql @@ -172,9 +172,10 @@ CREATE TABLE NFTs ( ); CREATE TABLE NFTLikes ( + key int PRIMARY KEY GENERATED ALWAYS AS IDENTITY, nftKey integer NOT NULL, liker char(64) NOT NULL, - PRIMARY KEY (nftKey, liker) + UNIQUE (nftKey, liker) ); CREATE INDEX nftLikes_nft_key_index ON NFTLikes (nftKey); CREATE INDEX nftLikes_liker_index ON NFTLikes (liker);