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);