diff --git a/backend/config/canvas.go b/backend/config/canvas.go index 31466fee..ee18f1bd 100644 --- a/backend/config/canvas.go +++ b/backend/config/canvas.go @@ -15,6 +15,7 @@ type CanvasConfig struct { Colors []string `json:"colors"` VotableColors []string `json:"votableColors"` ColorsBitWidth uint `json:"colorsBitwidth"` + Round uint `json:"round"` } var DefaultCanvasConfig = &CanvasConfig{ @@ -42,6 +43,7 @@ var DefaultCanvasConfig = &CanvasConfig{ "#00DDDD", }, ColorsBitWidth: 5, + Round: 2, } var DefaultCanvasConfigPath = "../configs/canvas.config.json" diff --git a/backend/routes/canvas.go b/backend/routes/canvas.go index a6d46858..41e22cfa 100644 --- a/backend/routes/canvas.go +++ b/backend/routes/canvas.go @@ -2,7 +2,9 @@ package routes import ( "context" + "fmt" "net/http" + "strconv" "github.com/keep-starknet-strange/art-peace/backend/core" routeutils "github.com/keep-starknet-strange/art-peace/backend/routes/utils" @@ -19,7 +21,10 @@ func initCanvas(w http.ResponseWriter, r *http.Request) { return } - if core.ArtPeaceBackend.Databases.Redis.Exists(context.Background(), "canvas").Val() == 0 { + roundNumber := core.ArtPeaceBackend.CanvasConfig.Round + canvasKey := fmt.Sprintf("canvas-%d", roundNumber) + + if core.ArtPeaceBackend.Databases.Redis.Exists(context.Background(), canvasKey).Val() == 0 { totalBitSize := core.ArtPeaceBackend.CanvasConfig.Canvas.Width * core.ArtPeaceBackend.CanvasConfig.Canvas.Height * core.ArtPeaceBackend.CanvasConfig.ColorsBitWidth totalByteSize := (totalBitSize / 8) if totalBitSize%8 != 0 { @@ -30,23 +35,31 @@ func initCanvas(w http.ResponseWriter, r *http.Request) { // Create canvas canvas := make([]byte, totalByteSize) ctx := context.Background() - err := core.ArtPeaceBackend.Databases.Redis.Set(ctx, "canvas", canvas, 0).Err() + err := core.ArtPeaceBackend.Databases.Redis.Set(ctx, canvasKey, canvas, 0).Err() if err != nil { routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to initialize canvas") return } - routeutils.WriteResultJson(w, "Canvas initialized") + routeutils.WriteResultJson(w, fmt.Sprintf("Canvas for round %d initialized", roundNumber)) } else { - routeutils.WriteErrorJson(w, http.StatusConflict, "Canvas already initialized") + routeutils.WriteErrorJson(w, http.StatusConflict, fmt.Sprintf("Canvas for round %d already initialized", roundNumber)) } } func getCanvas(w http.ResponseWriter, r *http.Request) { routeutils.SetupAccessHeaders(w) + // Get round number from query params, default to config round + roundNumber := r.URL.Query().Get("round") + if roundNumber == "" { + roundNumber = strconv.Itoa(int(core.ArtPeaceBackend.CanvasConfig.Round)) + } + + canvasKey := fmt.Sprintf("canvas-%s", roundNumber) + ctx := context.Background() - val, err := core.ArtPeaceBackend.Databases.Redis.Get(ctx, "canvas").Result() + val, err := core.ArtPeaceBackend.Databases.Redis.Get(ctx, canvasKey).Result() if err != nil { routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get canvas") return diff --git a/backend/routes/nft.go b/backend/routes/nft.go index 5c857eac..a08941fc 100644 --- a/backend/routes/nft.go +++ b/backend/routes/nft.go @@ -24,6 +24,7 @@ func InitNFTRoutes() { http.HandleFunc("/get-nft-pixel-data", getNftPixelData) // http.HandleFunc("/like-nft", LikeNFT) // http.HandleFunc("/unlike-nft", UnLikeNFT) + http.HandleFunc("/get-liked-nfts", getLikedNFTs) http.HandleFunc("/get-top-nfts", getTopNFTs) http.HandleFunc("/get-hot-nfts", getHotNFTs) if !core.ArtPeaceBackend.BackendConfig.Production { @@ -548,3 +549,57 @@ func getHotNFTs(w http.ResponseWriter, r *http.Request) { } routeutils.WriteDataJson(w, string(nfts)) } + +func getLikedNFTs(w http.ResponseWriter, r *http.Request) { + address := r.URL.Query().Get("address") + if address == "" || address == "0" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Valid address required for liked NFTs") + return + } + + 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, + true as liked + FROM + nfts + LEFT JOIN ( + SELECT + nftKey, + COUNT(*) AS like_count + FROM + nftlikes + GROUP BY + nftKey + ) nftlikes ON nfts.token_id = nftlikes.nftKey + WHERE + nfts.token_id IN ( + SELECT nftkey + FROM nftlikes + WHERE liker = $1 + ) + ORDER BY nfts.token_id DESC + LIMIT $2 OFFSET $3` + + nfts, err := core.PostgresQueryJson[NFTData](query, address, pageLength, offset) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve liked NFTs") + return + } + routeutils.WriteDataJson(w, string(nfts)) +} diff --git a/configs/canvas.config.json b/configs/canvas.config.json index 32ecf408..167eff44 100644 --- a/configs/canvas.config.json +++ b/configs/canvas.config.json @@ -47,5 +47,6 @@ "898D90", "D4D7D9" ], - "colorsBitwidth": 5 + "colorsBitwidth": 5, + "round": 2 } diff --git a/docker-compose.yml b/docker-compose.yml index b42dbe57..68a4309e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,7 @@ services: - POSTGRES_PASSWORD=password - ART_PEACE_END_TIME=3000000000 - ART_PEACE_HOST=0328ced46664355fc4b885ae7011af202313056a7e3d44827fb24c9d3206aaa0 - - ROUND_NUMBER=1 + - ROUND_NUMBER=2 volumes: - nfts:/app/nfts consumer: @@ -50,7 +50,7 @@ services: restart: always environment: - POSTGRES_PASSWORD=password - - ROUND_NUMBER=1 + - ROUND_NUMBER=2 volumes: - nfts:/app/nfts - worlds:/app/worlds @@ -143,7 +143,7 @@ services: - devnet environment: - REACT_APP_BASE_PIXEL_TIMER=30000 - - REACT_APP_ROUND_NUMBER=1 + - REACT_APP_ROUND_NUMBER=2 volumes: - ./frontend/package.json:/app/package.json - ./frontend/package-lock.json:/app/package-lock.json diff --git a/frontend/src/configs/canvas.config.json b/frontend/src/configs/canvas.config.json index 32ecf408..167eff44 100644 --- a/frontend/src/configs/canvas.config.json +++ b/frontend/src/configs/canvas.config.json @@ -47,5 +47,6 @@ "898D90", "D4D7D9" ], - "colorsBitwidth": 5 + "colorsBitwidth": 5, + "round": 2 } diff --git a/frontend/src/services/apiService.js b/frontend/src/services/apiService.js index 7b60f451..874f8148 100644 --- a/frontend/src/services/apiService.js +++ b/frontend/src/services/apiService.js @@ -181,3 +181,10 @@ export const getHotStencilsFn = async (params) => { `get-hot-stencils?address=${queryAddress}&page=${page}&pageLength=${pageLength}&worldId=${params.worldId}` ); }; + +export const getLikedNftsFn = async (params) => { + const { page, pageLength, queryAddress } = params; + return await fetchWrapper( + `get-liked-nfts?address=${queryAddress}&page=${page}&pageLength=${pageLength}` + ); +}; diff --git a/frontend/src/tabs/nfts/NFTs.js b/frontend/src/tabs/nfts/NFTs.js index a662c407..0edcc1c5 100644 --- a/frontend/src/tabs/nfts/NFTs.js +++ b/frontend/src/tabs/nfts/NFTs.js @@ -9,7 +9,8 @@ import { getNftsFn, getNewNftsFn, getTopNftsFn, - getHotNftsFn + getHotNftsFn, + getLikedNftsFn } from '../../services/apiService.js'; import { PaginationView } from '../../ui/pagination.js'; @@ -94,61 +95,76 @@ const NFTsMainSection = (props) => { }; const NFTsExpandedSection = (props) => { - const roundNumber = process.env.REACT_APP_ROUND_NUMBER || '0'; - const imageURL = `${nftUrl}/nft/round-${roundNumber}/images/`; - const metadataURL = `${nftUrl}/nft/round-${roundNumber}/metadata/`; + const maxRound = parseInt(process.env.REACT_APP_ROUND_NUMBER || '1'); + const [currentRound, setCurrentRound] = useState(maxRound); + const imageURL = `${nftUrl}/nft/round-${currentRound}/images/`; + const metadataURL = `${nftUrl}/nft/round-${currentRound}/metadata/`; + + const handleRoundChange = (direction) => { + if (direction === 'prev' && currentRound > 1) { + setCurrentRound((prev) => prev - 1); + props.setAllNftPagination((prev) => ({ ...prev, page: 1 })); + } else if (direction === 'next' && currentRound < maxRound) { + setCurrentRound((prev) => prev + 1); + props.setAllNftPagination((prev) => ({ ...prev, page: 1 })); + } + }; return (