Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas and more filters #267

Merged
2 changes: 2 additions & 0 deletions backend/config/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -42,6 +43,7 @@ var DefaultCanvasConfig = &CanvasConfig{
"#00DDDD",
},
ColorsBitWidth: 5,
Round: 2,
}

var DefaultCanvasConfigPath = "../configs/canvas.config.json"
Expand Down
23 changes: 18 additions & 5 deletions backend/routes/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand All @@ -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
Expand Down
55 changes: 55 additions & 0 deletions backend/routes/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}
3 changes: 2 additions & 1 deletion configs/canvas.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@
"898D90",
"D4D7D9"
],
"colorsBitwidth": 5
"colorsBitwidth": 5,
"round": 2
}
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -50,7 +50,7 @@ services:
restart: always
environment:
- POSTGRES_PASSWORD=password
- ROUND_NUMBER=1
- ROUND_NUMBER=2
volumes:
- nfts:/app/nfts
- worlds:/app/worlds
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/configs/canvas.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@
"898D90",
"D4D7D9"
],
"colorsBitwidth": 5
"colorsBitwidth": 5,
"round": 2
}
7 changes: 7 additions & 0 deletions frontend/src/services/apiService.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
);
};
89 changes: 55 additions & 34 deletions frontend/src/tabs/nfts/NFTs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
getNftsFn,
getNewNftsFn,
getTopNftsFn,
getHotNftsFn
getHotNftsFn,
getLikedNftsFn
} from '../../services/apiService.js';
import { PaginationView } from '../../ui/pagination.js';

Expand Down Expand Up @@ -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 (
<div className='NFTs__all'>
<div className='NFTs__header'>
<h2 className='NFTs__heading'>Explore</h2>
<div className='NFTs__filters'>
{props.filters.map((filter, index) => {
return (
<div
key={index}
className={`NFTs__button NFTs__filter ${
props.activeFilter === filter ? 'NFTs__button--selected' : ''
}`}
onClick={() => props.setActiveFilter(filter)}
>
{filter}
</div>
);
})}
{props.filters.map((filter, index) => (
<div
key={index}
className={`NFTs__button NFTs__filter ${
props.activeFilter === filter ? 'NFTs__button--selected' : ''
}`}
onClick={() => props.setActiveFilter(filter)}
>
{filter}
</div>
))}
<div
className='NFTs__button NFTs__filter'
onClick={() => handleRoundChange('prev')}
>
{'<'}
</div>
<div className='NFTs__button NFTs__filter'>
{`Round ${currentRound}`}
</div>
<div
className='NFTs__button NFTs__filter'
onClick={() => handleRoundChange('next')}
>
{'>'}
</div>
</div>
</div>

<div className='NFTs__all__container'>
<div className='NFTs__all__grid'>
{props.allNfts.map((nft, index) => {
return (
{currentRound === maxRound &&
(props.allNfts.length > 0 || props.activeFilter !== 'liked') &&
props.allNfts.map((nft, index) => (
<NFTItem
key={index}
{...nft}
address={props.address}
account={props.account}
estimateInvokeFee={props.estimateInvokeFee}
artPeaceContract={props.artPeaceContract}
canvasNftContract={props.canvasNftContract}
tokenId={nft.tokenId}
position={nft.position}
image={imageURL + 'nft-' + nft.tokenId + '.png'}
metadata={metadataURL + 'nft-' + nft.tokenId + '.json'}
width={nft.width}
height={nft.height}
name={nft.name}
blockNumber={nft.blockNumber}
likes={nft.likes}
liked={nft.liked}
minter={nft.minter}
queryAddress={props.queryAddress}
updateLikes={props.updateLikes}
setTemplateOverlayMode={props.setTemplateOverlayMode}
setOverlayTemplate={props.setOverlayTemplate}
setActiveTab={props.setActiveTab}
/>
);
})}
))}
</div>
<PaginationView
data={props.allNfts}
Expand Down Expand Up @@ -178,7 +194,6 @@ const NFTs = (props) => {
const response = await fetchWrapper(getNFTByTokenId, { mode: 'cors' });
if (response.data) {
let newNFTs = [response.data, ...myNFTs];
// Remove duplicate tokenIds
let uniqueNFTs = newNFTs.filter(
(nft, index, self) =>
index === self.findIndex((t) => t.tokenId === nft.tokenId)
Expand Down Expand Up @@ -244,7 +259,7 @@ const NFTs = (props) => {
}, [props.queryAddress, myNftPagination.page, myNftPagination.pageLength]);

const [expanded, setExpanded] = useState(false);
const filters = ['hot', 'new', 'top'];
const filters = ['hot', 'new', 'top', 'liked'];
const [activeFilter, setActiveFilter] = useState(filters[0]);

useEffect(() => {
Expand Down Expand Up @@ -272,6 +287,12 @@ const NFTs = (props) => {
pageLength: allNftPagination.pageLength,
queryAddress: props.queryAddress
});
} else if (activeFilter === 'liked') {
result = await getLikedNftsFn({
page: allNftPagination.page,
pageLength: allNftPagination.pageLength,
queryAddress: props.queryAddress
});
} else {
result = await getNftsFn({
page: allNftPagination.page,
Expand All @@ -298,7 +319,7 @@ const NFTs = (props) => {
}
}
getNfts();
}, [props.queryAddress, expanded, allNftPagination]);
}, [props.queryAddress, expanded, allNftPagination, activeFilter]);

const resetPagination = () => {
setAllNftPagination((prev) => ({
Expand Down
6 changes: 3 additions & 3 deletions main-docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ services:
- POSTGRES_PASSWORD=password
- ART_PEACE_END_TIME=3000000000
- ART_PEACE_HOST=0328ced46664355fc4b885ae7011af202313056a7e3d44827fb24c9d3206aaa0
- ROUND_NUMBER=1
- ROUND_NUMBER=2
consumer:
build:
dockerfile: backend/Dockerfile.consumer
Expand All @@ -48,7 +48,7 @@ services:
restart: always
environment:
- POSTGRES_PASSWORD=password
- ROUND_NUMBER=1
- ROUND_NUMBER=2
volumes:
- nfts:/app/nfts
devnet:
Expand Down Expand Up @@ -140,7 +140,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
Expand Down
Loading