Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

feature/integrate-pagination-offers #38

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions contexts/graph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ type GraphContextType = {
fetchMyMoonCats(): Promise<void>;
fetchAllOffers(
take: number,
skip: number
skip: number,
orderBy: string,
orderDirection: string
): Promise<AdoptionOffer[] | undefined>;
};

Expand Down Expand Up @@ -100,11 +102,13 @@ export const GraphProvider: React.FC = ({ children }) => {

const fetchAllOffers = async (
take: number,
skip: number
skip: number,
orderBy: string = null,
orderDirection: string = null
): Promise<AdoptionOffer[] | undefined> => {
setDataLoading(false);
if (!currentAddress) return;
const query = queryAllOffers(take, skip);
const query = queryAllOffers(take, skip, orderBy, orderDirection);
const subgraphURI = ENDPOINT_MOONCAT_PROD;
const response: {
offerPrices: AdoptionOffer[];
Expand Down
9 changes: 7 additions & 2 deletions contexts/graph/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,14 @@ export const queryAllRequests = (): string => {
}`;
};

export const queryAllOffers = (first: number, skip: number): string => {
export const queryAllOffers = (
first: number,
skip: number,
orderBy: string = null,
orderDirection: string = null
): string => {
return `{
offerPrices(where: {active_in: [true]}, first: ${first}, skip: ${skip}) {
offerPrices(where: {active_in: [true]}, first: ${first}, skip: ${skip}, orderBy: ${orderBy}, orderDirection: ${orderDirection}) {
id
price
to
Expand Down
179 changes: 110 additions & 69 deletions pages/offered/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import request from 'graphql-request';
import Head from 'next/head';
import React, { useState, useCallback, useContext, useMemo } from 'react';
import { ethers } from 'ethers';
import { calculatePrice, getDateTime } from '../../utils';
import { calculatePrice } from '../../utils';

import GraphContext from '../../contexts/graph/index';
import { Cat, AdoptionOffer } from '../../contexts/graph/types';
Expand All @@ -16,7 +16,6 @@ import Loader from '../../components/ui/loader';
import { queryAllOffers } from '../../contexts/graph/queries';
import {
ENDPOINT_MOONCAT_PROD,
FETCH_ALL_OFFERS_TAKE,
FETCH_EVERY_OFFERS_TAKE,
} from '../../lib/consts';
import { GetStaticProps } from 'next';
Expand All @@ -28,35 +27,28 @@ enum OffereSortType {
OLDEST = 'OLDEST',
}

enum NavigateType {
PREV = 'NAVIGATE_PREV',
NEXT = 'NAVIGATE_NEXT',
}

const sortTypes: Record<OffereSortType, string> = {
[OffereSortType.HIGH_PRICE]: 'HIGH PRICE',
[OffereSortType.CHEAPEST]: 'CHEAPEST',
[OffereSortType.RECENTLY_LISTED]: 'RECENTLY LISTED',
[OffereSortType.OLDEST]: 'OLDEST',
};

const sortFn: Record<
OffereSortType,
(offerA: AdoptionOffer, offerB: AdoptionOffer) => number
> = {
[OffereSortType.HIGH_PRICE]: (
offerA: AdoptionOffer,
offerB: AdoptionOffer
) => {
return parseInt(offerB.price, 10) - parseInt(offerA.price, 10);
},
[OffereSortType.CHEAPEST]: (offerA: AdoptionOffer, offerB: AdoptionOffer) => {
return parseInt(offerA.price, 10) - parseInt(offerB.price, 10);
},
[OffereSortType.RECENTLY_LISTED]: (
offerA: AdoptionOffer,
offerB: AdoptionOffer
) => {
return getDateTime(offerB.timestamp) - getDateTime(offerA.timestamp);
},
[OffereSortType.OLDEST]: (offerA: AdoptionOffer, offerB: AdoptionOffer) =>
getDateTime(offerA.timestamp) - getDateTime(offerB.timestamp),
};
const getOrderByParam = (sortType: OffereSortType) =>
sortType === OffereSortType.CHEAPEST || sortType === OffereSortType.HIGH_PRICE
? 'price'
: 'timestamp';

const getOrderDirectionParam = (sortType: OffereSortType) =>
sortType === OffereSortType.HIGH_PRICE ||
sortType === OffereSortType.RECENTLY_LISTED
? 'desc'
: 'asc';

const OfferedCats: React.FC<{ allOffers: AdoptionOffer[] }> = ({
allOffers: ssrOffers,
Expand All @@ -67,9 +59,7 @@ const OfferedCats: React.FC<{ allOffers: AdoptionOffer[] }> = ({

// State
const [allOffers, setAllOffers] = useState<AdoptionOffer[]>(ssrOffers);
const [currentSkipCount, setCurrentSkipCount] = useState<number>(
FETCH_ALL_OFFERS_TAKE
);
const [currentSkipCount, setCurrentSkipCount] = useState<number>(0);
const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isOpenModal, setOpenModal] = useState<boolean>(false);
Expand All @@ -80,27 +70,60 @@ const OfferedCats: React.FC<{ allOffers: AdoptionOffer[] }> = ({
const [isCopiedSuccessfully, setIsCopiedSuccessfully] = useState<boolean>(
false
);
const [disableNext, setDisableNext] = useState<boolean>(false);

// Memoized values
const sortedOffer = useMemo(() => {
return allOffers.sort(sortFn[currentSortType]);
}, [currentSortType, allOffers]);
const filteredOffers = useMemo(() => {
return (disableNext
? allOffers
: allOffers.slice(0, allOffers.length - 1)
).filter(
(offer: AdoptionOffer) =>
offer.to.toLowerCase() == ethers.constants.AddressZero
);
}, [disableNext, allOffers]);

// Callbacks
const handleFetchOffers = useCallback(async () => {
const handleFetchOffers = useCallback(
async (navigateType: NavigateType) => {
setIsLoading(true);
const offers = await fetchAllOffers(
FETCH_EVERY_OFFERS_TAKE + 1,
currentSkipCount +
(navigateType === NavigateType.NEXT ? 1 : -1) *
FETCH_EVERY_OFFERS_TAKE,
getOrderByParam(currentSortType),
getOrderDirectionParam(currentSortType)
);
setCurrentSkipCount(
(prevSkip) =>
prevSkip +
(navigateType === NavigateType.NEXT ? 1 : -1) *
FETCH_EVERY_OFFERS_TAKE
);
setAllOffers(() => [...offers]);
setIsLoading(false);
if (offers.length !== FETCH_EVERY_OFFERS_TAKE + 1) setDisableNext(true);
else setDisableNext(false);
},
[currentSkipCount, fetchAllOffers, currentSortType]
);

const handleSort = async (sortType: OffereSortType) => {
setIsLoading(true);
const yOffset = window.pageYOffset + 100;
const offers = await fetchAllOffers(
FETCH_EVERY_OFFERS_TAKE,
currentSkipCount
FETCH_EVERY_OFFERS_TAKE + 1,
0,
getOrderByParam(sortType),
getOrderDirectionParam(sortType)
);
setCurrentSkipCount((prevSkip) => prevSkip + FETCH_EVERY_OFFERS_TAKE);
setAllOffers((prevOffers) => [...prevOffers, ...offers]);

setCurrentSkipCount(0);
setAllOffers(() => [...offers]);
setIsLoading(false);
window.scrollTo(0, yOffset);
}, [currentSkipCount, fetchAllOffers]);
setCurrentSortType(sortType);
};

const handleSort = (sortType: OffereSortType) => setCurrentSortType(sortType);
const handleModalClose = useCallback(() => setOpenModal(false), []);
const handleModalOpen = useCallback((offer: AdoptionOffer) => {
setError(undefined);
Expand Down Expand Up @@ -155,38 +178,51 @@ const OfferedCats: React.FC<{ allOffers: AdoptionOffer[] }> = ({
</div>
</div>
</div>
<div className="content__row content__items">
{sortedOffer.map((offer) => {
const catId = offer.id.split('::')[0];
return (
<CatItem
key={offer.id}
cat={{
isWrapped: false,
rescueTimestamp: offer.catRescueTimestamp,
id: catId,
activeOffer: offer,
}}
hasRescuerIdx={true}
onClick={onCopyToClipboard}
>
<div className="nft__control">
<button
className="nft__button"
onClick={() => handleModalOpen(offer)}
>
Buy now
</button>
</div>
</CatItem>
);
})}
</div>
{!isLoading && (
<div className="content__row content__items">
{filteredOffers.map((offer) => {
const catId = offer.id.split('::')[0];
return (
<CatItem
key={offer.id}
cat={{
isWrapped: false,
rescueTimestamp: offer.catRescueTimestamp,
id: catId,
activeOffer: offer,
}}
hasRescuerIdx={true}
onClick={onCopyToClipboard}
>
<div className="nft__control">
<button
className="nft__button"
onClick={() => handleModalOpen(offer)}
>
Buy now
</button>
</div>
</CatItem>
);
})}
</div>
)}
{isCopiedSuccessfully && <CatNotifer />}
{!isLoading ? (
<div className="load-more">
<button className="nft__button" onClick={handleFetchOffers}>
Load more
<button
disabled={currentSkipCount === 0}
className="nft__button"
onClick={() => handleFetchOffers(NavigateType.PREV)}
>
Prev
</button>
<button
disabled={disableNext}
className="nft__button"
onClick={() => handleFetchOffers(NavigateType.NEXT)}
>
Next
</button>
</div>
) : (
Expand All @@ -212,7 +248,12 @@ const OfferedCats: React.FC<{ allOffers: AdoptionOffer[] }> = ({
};

export const getStaticProps: GetStaticProps = async () => {
const offeredQuery = queryAllOffers(FETCH_ALL_OFFERS_TAKE, 0);
const offeredQuery = queryAllOffers(
FETCH_EVERY_OFFERS_TAKE + 1,
0,
'timestamp',
'desc'
);
const { offerPrices: allOffers } = await request(
ENDPOINT_MOONCAT_PROD,
offeredQuery
Expand Down
6 changes: 6 additions & 0 deletions styles/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,12 @@
2px 2px 0 #8349be, 1px 1px 0 #8349be;
}

&:disabled {
background: gray;
box-shadow: 5px 5px 0 gray, 4px 4px 0 gray, 3px 3px 0 gray,
2px 2px 0 gray, 1px 1px 0 gray;
}

&__active {
background: #fc9706;
box-shadow: 5px 5px 0 #f58901, 4px 4px 0 #f58901, 3px 3px 0 #f58901,
Expand Down