From cd101e64fef2971c4eb5356935ee184a1b681d13 Mon Sep 17 00:00:00 2001 From: AlefrankM Date: Tue, 10 Jan 2023 13:27:01 -0400 Subject: [PATCH 1/5] refactor: updating account store --- .../create-page/nft-collection.component.tsx | 7 ++- src/components/item-details/title.tsx | 3 +- src/components/item/index.tsx | 5 +- .../items-stats/items-stats.components.tsx | 3 +- .../{index.tsx => AuthorIntroArea.tsx} | 33 +++++-------- .../{index.tsx => AuthorProfileArea.tsx} | 11 ++--- src/containers/collections/index.tsx | 9 ++-- src/containers/create-new/index.tsx | 9 ++-- src/data/AuthorData.ts | 10 ++++ src/data/author.json | 10 ---- src/features/account/store/account.slice.ts | 39 +++++++-------- .../collections/store/collections.actions.ts | 47 +++++-------------- .../collections/store/collections.slice.ts | 4 +- src/features/leda-nft/store/leda-nft.slice.ts | 2 - .../marketplace/store/marketplace.actions.ts | 22 ++++----- .../store/marketplace.slice.spec.ts | 42 +++-------------- .../marketplace/store/marketplace.slice.ts | 22 +++------ src/pages/author.tsx | 8 ++-- src/pages/collections/index.tsx | 7 +-- 19 files changed, 101 insertions(+), 192 deletions(-) rename src/containers/author-intro/{index.tsx => AuthorIntroArea.tsx} (75%) rename src/containers/author-profile/{index.tsx => AuthorProfileArea.tsx} (94%) create mode 100644 src/data/AuthorData.ts delete mode 100644 src/data/author.json diff --git a/src/components/create-page/nft-collection.component.tsx b/src/components/create-page/nft-collection.component.tsx index 1c98be7..ade3dba 100644 --- a/src/components/create-page/nft-collection.component.tsx +++ b/src/components/create-page/nft-collection.component.tsx @@ -3,7 +3,6 @@ import { useEffect, useRef, useState } from 'react'; import Modal from 'react-bootstrap/Modal'; import { AiOutlinePlus } from 'react-icons/ai'; import { findUserCollectionsWithoutItems } from '../../features/account/store/account.actions'; -import { selectUserCollectionsWithoutItems } from '../../features/account/store/account.slice'; import useMetamask from '../../features/auth/hooks/useMetamask'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; @@ -19,7 +18,7 @@ const NftCollectionComponent = () => { const dispatch = useAppDispatch(); const { address } = useMetamask(); const [open, setOpen] = useState(false); - const userCollections = useAppSelector(selectUserCollectionsWithoutItems); + const myCollections = useAppSelector((state) => state.account.collections); const [dropdownCollection, setDropdownCollection] = useState(''); const [collectionModalOpen, setCollectionModalOpen] = useState(false); const [selectedCollectionImage, setSelectedCollectionImage] = useState(null); @@ -54,7 +53,7 @@ const NftCollectionComponent = () => { handleDropdown(); }; - const existOnUserCollections = userCollections.find((col) => col.name === collectionInput.name); + const existOnUserCollections = myCollections.find((col) => col.name === collectionInput.name); const handleSaveCollection = () => { if (collectionInput.name.length <= 3) setCollectionError(collectionsErrors.ShortString); @@ -123,7 +122,7 @@ const NftCollectionComponent = () => { > Default Collection - {userCollections + {myCollections .filter((col) => col.name !== 'LedaNFT') .map((userCollection) => (
  • { const handleLikeItem = () => { dispatch(withAuthProtection(likeItem(itemId))); }; - const likedItems = useAppSelector(selectLikedItems); + const { likedItems } = useAppSelector((state) => state.account); const isLiked = useMemo( () => Boolean(likedItems.find((likedItem) => likedItem.itemId === itemId)), diff --git a/src/components/item/index.tsx b/src/components/item/index.tsx index 8dc930f..1adaa2f 100644 --- a/src/components/item/index.tsx +++ b/src/components/item/index.tsx @@ -9,12 +9,11 @@ import CountdownTimer from '@ui/countdown/count-down-timer'; import { getFormattedName } from '@utils/getFormattedName'; import clsx from 'clsx'; import { useMemo } from 'react'; -import { selectLikedItems } from '../../features/account/store/account.slice'; +import appConfig from '../../common/configuration/app.config'; import { selectAuthState } from '../../features/auth/store/auth.slice'; import { setIsModalOpen } from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; -import appConfig from '../../common/configuration/app.config'; type Props = { overlay?: boolean; @@ -66,7 +65,7 @@ const Product = ({ }: Props) => { const dispatch = useAppDispatch(); const { isModalOpen } = useAppSelector((state) => state.marketplace); - const likedItems = useAppSelector(selectLikedItems); + const { likedItems } = useAppSelector((state) => state.account); const handleBuyModal = () => { dispatch(setIsModalOpen(!isModalOpen)); }; diff --git a/src/components/items-stats/items-stats.components.tsx b/src/components/items-stats/items-stats.components.tsx index 8391994..bd432a5 100644 --- a/src/components/items-stats/items-stats.components.tsx +++ b/src/components/items-stats/items-stats.components.tsx @@ -8,7 +8,6 @@ import { useCallback, useEffect, useMemo } from 'react'; import { FaEthereum, FaRegHeart } from 'react-icons/fa'; import { IoMdHeart } from 'react-icons/io'; import appConfig from '../../common/configuration/app.config'; -import { selectLikedItems } from '../../features/account/store/account.slice'; import { withAuthProtection } from '../../features/auth/store/auth.actions'; import { findPagedCollectionsNfts } from '../../features/collections/store/collections.actions'; import { likeItem } from '../../features/marketplace/store/marketplace.actions'; @@ -18,7 +17,7 @@ import { Item } from '../../types/item'; const LikeRender = ({ likes, itemId }: { likes: number; itemId: string }) => { const dispatch = useAppDispatch(); - const likedItems = useAppSelector(selectLikedItems); + const { likedItems } = useAppSelector((state) => state.account); const isLiked = useMemo( () => Boolean(likedItems.find((likedItem) => likedItem.itemId === itemId)), diff --git a/src/containers/author-intro/index.tsx b/src/containers/author-intro/AuthorIntroArea.tsx similarity index 75% rename from src/containers/author-intro/index.tsx rename to src/containers/author-intro/AuthorIntroArea.tsx index 8b91075..fa7e82b 100644 --- a/src/containers/author-intro/index.tsx +++ b/src/containers/author-intro/AuthorIntroArea.tsx @@ -1,28 +1,23 @@ -import { useMemo, useState } from 'react'; -import clsx from 'clsx'; -import Image from 'next/image'; import ShareModal from '@components/modals/share-modal'; -import { Author } from '@types'; -import { selectAccountState, selectLikedItems } from '../../features/account/store/account.slice'; -import useAppSelector from '../../store/hooks/useAppSelector'; -import ReportModal from '../../components/modals/report-modal/index'; +import Image from 'next/image'; +import { useMemo, useState } from 'react'; import ItemStatus from '../../common/minting/enums/item-status.enum'; +import ReportModal from '../../components/modals/report-modal/index'; +import { AuthorData } from '../../data/AuthorData'; +import useAppSelector from '../../store/hooks/useAppSelector'; type Props = { - className?: string; - space?: number; - data: Author; address: string; }; -const AuthorIntroArea = ({ className, space = 1, data, address }: Props) => { +export const AuthorIntroArea = ({ address }: Props) => { const [isShareModalOpen, setIsShareModalOpen] = useState(false); const [showReportModal, setShowReportModal] = useState(false); - const { imageNumber } = useAppSelector(selectAccountState); + const { imageNumber } = useAppSelector((state) => state.account); const shareModalHandler = () => setIsShareModalOpen((prev) => !prev); const handleReportModal = () => setShowReportModal((prev) => !prev); - const likedItems = useAppSelector(selectLikedItems); + const likedItems = useAppSelector((state) => state.account.likedItems); const likedItemsToShow = useMemo( () => @@ -46,17 +41,17 @@ const AuthorIntroArea = ({ className, space = 1, data, address }: Props) => { priority /> -
    +
    - {data?.image?.src && ( + {AuthorData?.image?.src && (
    {data.image?.alt { )}
    -

    - {address} -

    +

    {address}

    @@ -85,5 +78,3 @@ const AuthorIntroArea = ({ className, space = 1, data, address }: Props) => { ); }; - -export default AuthorIntroArea; diff --git a/src/containers/author-profile/index.tsx b/src/containers/author-profile/AuthorProfileArea.tsx similarity index 94% rename from src/containers/author-profile/index.tsx rename to src/containers/author-profile/AuthorProfileArea.tsx index 2595094..e4f4a61 100644 --- a/src/containers/author-profile/index.tsx +++ b/src/containers/author-profile/AuthorProfileArea.tsx @@ -1,6 +1,5 @@ import Product from '@components/item'; import { Item } from '@types'; -import clsx from 'clsx'; import { useMemo } from 'react'; import Nav from 'react-bootstrap/Nav'; import TabContainer from 'react-bootstrap/TabContainer'; @@ -8,20 +7,18 @@ import TabContent from 'react-bootstrap/TabContent'; import TabPane from 'react-bootstrap/TabPane'; import { selectCreatedItems, - selectLikedItems, selectOnSaleItems, selectOwnedItems, } from '../../features/account/store/account.slice'; import useAppSelector from '../../store/hooks/useAppSelector'; type Props = { - className?: string; address: string; }; -const AuthorProfileArea = ({ className, address }: Props) => { +export const AuthorProfileArea = ({ address }: Props) => { const createdItems = useAppSelector((state) => selectCreatedItems(state, address)); - const likedItems = useAppSelector(selectLikedItems); + const { likedItems } = useAppSelector((state) => state.account); const onSaleItems = useAppSelector((state) => selectOnSaleItems(state, address)); const ownedItems = useAppSelector((state) => selectOwnedItems(state, address)); @@ -31,7 +28,7 @@ const AuthorProfileArea = ({ className, address }: Props) => { ); return ( -

    +
    @@ -144,5 +141,3 @@ const AuthorProfileArea = ({ className, address }: Props) => {
    ); }; - -export default AuthorProfileArea; diff --git a/src/containers/collections/index.tsx b/src/containers/collections/index.tsx index 91d277f..fb85444 100644 --- a/src/containers/collections/index.tsx +++ b/src/containers/collections/index.tsx @@ -20,7 +20,6 @@ import { selectNftState } from '../../features/leda-nft/store/leda-nft.slice'; import useMetamask from '../../features/auth/hooks/useMetamask'; import { ItemProperty } from '../../common/types/ipfs-types'; import { findUserCollectionsWithoutItems } from '../../features/account/store/account.actions'; -import { selectUserCollectionsWithoutItems } from '../../features/account/store/account.slice'; import { CollectionCreateType } from '../../types/collection-type'; const tagsErrorMessages = { @@ -55,7 +54,7 @@ const collectionsErrors = { }; const CreateNewArea = () => { - const userCollections = useAppSelector(selectUserCollectionsWithoutItems); + const { collectionsWithoutItems } = useAppSelector((state) => state.account); const [properties, setProperties] = useState([]); const [propertiesModalMessage, setPropertiesModalMessage] = useState(''); const [propsModalOpen, setPropsModalOpen] = useState(false); @@ -112,7 +111,9 @@ const CreateNewArea = () => { onClose(); }; - const existOnUserCollections = userCollections.find((col) => col.name === collectionInput.name); + const existOnUserCollections = collectionsWithoutItems.find( + (col) => col.name === collectionInput.name + ); const handleSaveCollection = () => { if (collectionInput.name.length <= 3) setCollectionError(collectionsErrors.ShortString); @@ -419,7 +420,7 @@ const CreateNewArea = () => { > Default Collection
  • - {userCollections + {collectionsWithoutItems .filter((col) => col.name !== 'LedaNFT') .map((userCollection) => (
  • { - const userCollections = useAppSelector(selectUserCollectionsWithoutItems); + const { collectionsWithoutItems } = useAppSelector((state) => state.account); const [properties, setProperties] = useState([]); const [propertiesModalMessage, setPropertiesModalMessage] = useState(''); const [propsModalOpen, setPropsModalOpen] = useState(false); @@ -124,7 +123,9 @@ const CreateNewArea = () => { onClose(); }; - const existOnUserCollections = userCollections.find((col) => col.name === collectionInput.name); + const existOnUserCollections = collectionsWithoutItems.find( + (col) => col.name === collectionInput.name + ); const handleDefaultCollection = () => { setCollection(defaultCollection); @@ -454,7 +455,7 @@ const CreateNewArea = () => { > Default Collection
  • - {userCollections + {collectionsWithoutItems .filter((col) => col.name !== 'LedaNFT') .map((userCollection) => (
  • { builder.addCase(findUserCollectionsWithoutItems.pending, (state) => { - state.loadingUserCollection = true; + state.isLoadingCollection = true; }); builder.addCase(findUserCollectionsWithoutItems.fulfilled, (state, { payload }) => { - state.loadingUserCollection = false; - state.userCollectionsWithoutItems = payload; + state.isLoadingCollection = false; + state.collectionsWithoutItems = payload; }); builder.addCase(findUserCollectionsWithoutItems.rejected, (state) => { - state.loadingUserCollection = false; + state.isLoadingCollection = false; }); builder.addCase(findUserCollections.pending, (state) => { - state.loadingUserCollection = true; + state.isLoadingCollection = true; }); builder.addCase(findUserCollections.fulfilled, (state, { payload }) => { - state.userCollections = payload; - state.loadingUserCollection = false; + state.collections = payload; + state.isLoadingCollection = false; }); builder.addCase(findUserCollections.rejected, (state) => { - state.loadingUserCollection = false; + state.isLoadingCollection = false; }); builder.addCase(findItemsByAccount.pending, (state) => { state.isLoading = true; @@ -99,8 +99,6 @@ export const selectCreatedItems = createSelector( (items: Item[], address: string) => items.filter((item) => item.author.address === address) ); -export const selectLikedItems = (state: RootState) => state.account.likedItems; - export const selectOnSaleItems = createSelector( selectItems, (_: unknown, address: string) => address, @@ -108,11 +106,6 @@ export const selectOnSaleItems = createSelector( items.filter((item) => item.owner.address === address && item.status === ItemStatus.Listed) ); -export const selectUserCollections = (state: RootState) => state.account.userCollections; - -export const selectUserCollectionsWithoutItems = (state: RootState) => - state.account.userCollectionsWithoutItems; - export const selectOwnedItems = createSelector( selectItems, (_: unknown, address: string) => address, diff --git a/src/features/collections/store/collections.actions.ts b/src/features/collections/store/collections.actions.ts index 57908c4..c617b99 100644 --- a/src/features/collections/store/collections.actions.ts +++ b/src/features/collections/store/collections.actions.ts @@ -1,71 +1,50 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; +import { FilterType } from '../../../types/item-filter-types'; import { collectionsService } from '../services/collections.service'; import { CollectionsFiltersTypes } from '../types/CollectionsFiltersTypes'; -import type { RootState } from '../../../store/types'; -import { openToastError } from '../../../store/ui/ui.slice'; -import { FilterType } from '../../../types/item-filter-types'; -const findCollectionById = createAsyncThunk('collections/findById', async (collectionId: string) => - collectionsService.findById(collectionId) +export const findCollectionById = createAsyncThunk( + 'collections/findById', + async (collectionId: string) => collectionsService.findById(collectionId) ); -const findAllCollections = createAsyncThunk('collections/findAll', async () => +export const findAllCollections = createAsyncThunk('collections/findAll', async () => collectionsService.findAll() ); -const getNewestCollections = createAsyncThunk('collections/getNewest', async (qty: number) => +export const getNewestCollections = createAsyncThunk('collections/getNewest', async (qty: number) => collectionsService.findNewest(qty) ); -const findPagedCollections = createAsyncThunk( +export const findPagedCollections = createAsyncThunk( 'collections/findPagedCollections', async (filters: CollectionsFiltersTypes) => collectionsService.findPagedCollections(filters) ); -const findFilteredCollections = createAsyncThunk( +export const findFilteredCollections = createAsyncThunk( 'collections/findFilteredCollections', - async (filters: CollectionsFiltersTypes, { getState, dispatch }) => { - const { collections } = getState() as RootState; - const payload = await collectionsService.findPagedCollections(filters); - if (!payload.totalCount) { - dispatch(openToastError('No collections found.')); - return collections.collectionPagination; - } - return payload; - } + async (filters: CollectionsFiltersTypes) => collectionsService.findPagedCollections(filters) ); -const findFilteredCollectionItems = createAsyncThunk( +export const findFilteredCollectionItems = createAsyncThunk( 'collections/findFilteredCollectionItems', async ({ collectionId, filters }: { collectionId: string; filters: FilterType }) => collectionsService.findPagedCollectionItems(collectionId, filters) ); -const findPagedCollectionsNfts = createAsyncThunk( +export const findPagedCollectionsNfts = createAsyncThunk( 'collections/findFilteredCollectionsNfts', async ({ collectionId, page }: { collectionId: string; page: number }) => collectionsService.findPagedCollectionsNfts(collectionId, page) ); -const findPriceRange = createAsyncThunk( +export const findPriceRange = createAsyncThunk( 'collections/findPriceRange', async (collectionId: string) => collectionsService.findPriceRangeCollectionItems(collectionId) ); -const findPagedCollectionItems = createAsyncThunk( +export const findPagedCollectionItems = createAsyncThunk( 'collections/findPagedCollectionItems', async ({ collectionId, filters }: { collectionId: string; filters: FilterType }) => collectionsService.findPagedCollectionItems(collectionId, filters) ); - -export { - findCollectionById, - findAllCollections, - getNewestCollections, - findPagedCollections, - findFilteredCollections, - findPagedCollectionsNfts, - findPriceRange, - findPagedCollectionItems, - findFilteredCollectionItems, -}; diff --git a/src/features/collections/store/collections.slice.ts b/src/features/collections/store/collections.slice.ts index 51a7d2a..a9135c3 100644 --- a/src/features/collections/store/collections.slice.ts +++ b/src/features/collections/store/collections.slice.ts @@ -57,7 +57,7 @@ const initialState: CollectionsState = { cheapest: '', mostExpensive: '', page: 1, - limit: 8, + limit: 3, } as FilterType, itemsPagination: { items: [], totalCount: 0 }, isCollectionNftsLoading: false, @@ -70,7 +70,7 @@ const initialState: CollectionsState = { creationOrder: '', mintType: '', page: 1, - limit: 15, + limit: 3, }, collectionPagination: { collections: [], totalCount: 0 }, isPagingLoading: false, diff --git a/src/features/leda-nft/store/leda-nft.slice.ts b/src/features/leda-nft/store/leda-nft.slice.ts index 880fe5d..9a43b86 100644 --- a/src/features/leda-nft/store/leda-nft.slice.ts +++ b/src/features/leda-nft/store/leda-nft.slice.ts @@ -76,8 +76,6 @@ export const selectNftState = (state: RootState) => state.ledaNft; export const selectAllItems = (state: RootState) => state.ledaNft.items; -export const selectNewest = (state: RootState) => state.ledaNft.items.slice(0, 5); - export const selectById = (state: RootState, itemId: string) => state.ledaNft.items.find((item) => item.itemId === itemId); diff --git a/src/features/marketplace/store/marketplace.actions.ts b/src/features/marketplace/store/marketplace.actions.ts index fe10d4f..67dd925 100644 --- a/src/features/marketplace/store/marketplace.actions.ts +++ b/src/features/marketplace/store/marketplace.actions.ts @@ -1,19 +1,18 @@ import { createAsyncThunk, Dispatch } from '@reduxjs/toolkit'; import Router from 'next/router'; -import { getContracts } from '../../../utils/getContracts'; -import { FilterType, FilterTypeBase } from '../../../types/item-filter-types'; -import { openToastError, openToastSuccess } from '../../../store/ui/ui.slice'; import BusinessError from '../../../common/exceptions/business-error'; import CollectionType from '../../../common/minting/enums/collection-type.enum'; -import ContractEvent from '../process/enums/contract-event.enum'; -import ItemService, { itemService } from '../../leda-nft/services/item.service'; import ItemStatus from '../../../common/minting/enums/item-status.enum'; -import MarketplaceClientProcessor from '../process/clients/marketplace-client-processor'; -import MarketplaceService from '../services/marketplace.service'; -import MarketplaceState from '../process/types/marketplace-state'; -import type { RootState } from '../../../store/types'; import { LazyProcessType } from '../../../common/minting/enums/lazy-process-type.enum'; +import type { RootState } from '../../../store/types'; +import { openToastError, openToastSuccess } from '../../../store/ui/ui.slice'; import { Item } from '../../../types/item'; +import { FilterType, FilterTypeBase } from '../../../types/item-filter-types'; +import { getContracts } from '../../../utils/getContracts'; +import ItemService, { itemService } from '../../leda-nft/services/item.service'; +import MarketplaceClientProcessor from '../process/clients/marketplace-client-processor'; +import ContractEvent from '../process/enums/contract-event.enum'; +import MarketplaceState from '../process/types/marketplace-state'; const { LedaAddress } = getContracts(); @@ -35,11 +34,6 @@ export const findPriceRange = createAsyncThunk('marketplace/findPriceRange', asy itemService.findPriceRange() ); -export const getOwner = createAsyncThunk('marketplace/getNftList', async () => { - const service = new MarketplaceService(); - return service.getOwner(); -}); - export const listItem = createAsyncThunk( 'marketplace/listItem', async ( diff --git a/src/features/marketplace/store/marketplace.slice.spec.ts b/src/features/marketplace/store/marketplace.slice.spec.ts index ae24919..25f304f 100644 --- a/src/features/marketplace/store/marketplace.slice.spec.ts +++ b/src/features/marketplace/store/marketplace.slice.spec.ts @@ -1,19 +1,11 @@ import { AnyAction } from '@reduxjs/toolkit'; -import store from '../../../store'; -import { FilterType } from '../../../types/item-filter-types'; import { Item } from '../../../types/item'; -import { - findFilteredItems, - findPagedItems, - getOwner, - listItem, - getNewest, -} from './marketplace.actions'; +import { FilterType } from '../../../types/item-filter-types'; +import { findFilteredItems, findPagedItems, getNewest, listItem } from './marketplace.actions'; import { marketplaceReducer, MarketplaceState, resetMarketplaceFilters, - selectOwner, setMarketplaceFilters, } from './marketplace.slice'; @@ -22,12 +14,12 @@ describe('Marketplace slice', () => { beforeEach(() => { initialState = { - owner: '', + items: [], isLoading: false, isPagingLoading: false, isLoadingHistory: false, newestItems: [], - loadingNewest: false, + isLoadingNewest: false, marketplaceFilters: { likesDirection: '', cheapest: '', @@ -88,17 +80,17 @@ describe('Marketplace slice', () => { it('the loadingNewest should retrieve false if it is fulfilled', () => { const expected = false; const actual = marketplaceReducer(undefined, getNewest.fulfilled); - expect(actual.loadingNewest).toEqual(expected); + expect(actual.isLoadingNewest).toEqual(expected); }); it('the loadingNewest should retrieve true if it is pending', () => { const expected = true; const actual = marketplaceReducer(undefined, getNewest.pending); - expect(actual.loadingNewest).toEqual(expected); + expect(actual.isLoadingNewest).toEqual(expected); }); it('the loadingNewest should retrieve false if it is rejected', () => { const expected = false; const actual = marketplaceReducer(undefined, getNewest.rejected); - expect(actual.loadingNewest).toEqual(expected); + expect(actual.isLoadingNewest).toEqual(expected); }); }); @@ -152,26 +144,6 @@ describe('Marketplace slice', () => { }); }); - describe('When getOwner function is called', () => { - it('should assign an owner successfully', () => { - const expected = 'Jane Doe'; - - const actual = marketplaceReducer(undefined, getOwner.fulfilled(expected, '')); - - expect(actual.owner).toEqual(expected); - }); - }); - - describe('When selectOwner is called', () => { - it('should return the owner from the state', () => { - const expected = ''; - - const actual = selectOwner(store.getState()); - - expect(actual).toEqual(expected); - }); - }); - describe('When isListed is called', () => { it('should return true when isListed is succesfull', () => { const expected = true; diff --git a/src/features/marketplace/store/marketplace.slice.ts b/src/features/marketplace/store/marketplace.slice.ts index a3e671c..244656f 100644 --- a/src/features/marketplace/store/marketplace.slice.ts +++ b/src/features/marketplace/store/marketplace.slice.ts @@ -12,7 +12,6 @@ import { findPriceRange, findAllHistory, findHistoryByItemId, - getOwner, listItem, buyItem, likeItem, @@ -21,11 +20,11 @@ import { } from './marketplace.actions'; export type MarketplaceState = { - owner: string | undefined; + items: Item[]; marketplaceFilters: FilterType; itemPagination: ItemPagination; newestItems: Item[]; - loadingNewest: boolean; + isLoadingNewest: boolean; isLoading: boolean; isPagingLoading: boolean; isLoadingHistory: boolean; @@ -51,7 +50,7 @@ export const initialFormState = { }; const initialState: MarketplaceState = { - owner: '', + items: [], isLoading: false, isPagingLoading: false, isLoadingHistory: false, @@ -70,7 +69,7 @@ const initialState: MarketplaceState = { } as FilterType, selectedItem: {} as Item, newestItems: [], - loadingNewest: false, + isLoadingNewest: false, history: { data: [], count: 0, @@ -114,14 +113,14 @@ const marketplaceSlice = createSlice({ state.selectedItem = item; }); builder.addCase(getNewest.pending, (state) => { - state.loadingNewest = true; + state.isLoadingNewest = true; }); builder.addCase(getNewest.fulfilled, (state, { payload }) => { state.newestItems = payload; - state.loadingNewest = false; + state.isLoadingNewest = false; }); builder.addCase(getNewest.rejected, (state) => { - state.loadingNewest = false; + state.isLoadingNewest = false; }); builder.addCase(listItem.rejected, (state) => { state.isLoading = false; @@ -166,9 +165,6 @@ const marketplaceSlice = createSlice({ state.isLoading = false; state.isModalOpen = false; }); - builder.addCase(getOwner.fulfilled, (state, { payload }) => { - state.owner = payload; - }); builder.addCase(findFilteredItems.pending, (state) => { state.isLoading = true; }); @@ -233,8 +229,6 @@ const marketplaceSlice = createSlice({ }, }); -export const selectOwner = (state: RootState) => state.marketplace.owner; - export const selectNFTsMarketplace = (state: RootState) => state.marketplace; export const selectCanIList = (state: RootState) => { @@ -277,8 +271,6 @@ export const selectIsOwner = (state: RootState) => { return address === selectedItem?.owner?.address; }; -export const selectNewest = (state: RootState) => state.marketplace.newestItems.slice(0, 2); - export const selectMarketplaceState = (state: RootState) => state.marketplace; export const selectIsLoadingWhileBuy = (state: RootState) => { diff --git a/src/pages/author.tsx b/src/pages/author.tsx index dc51395..d25fbd7 100644 --- a/src/pages/author.tsx +++ b/src/pages/author.tsx @@ -1,9 +1,9 @@ import { useEffect } from 'react'; -import AuthorIntroArea from '@containers/author-intro'; -import AuthorProfileArea from '@containers/author-profile'; +import { AuthorIntroArea } from '@containers/author-intro/AuthorIntroArea'; +import { AuthorProfileArea } from '@containers/author-profile/AuthorProfileArea'; import SEO from '@components/seo'; import withAuth from '@components/auth/withAuth'; -import authorData from '../data/author.json'; + import { findItemsByAccount, findLikedItemsByAccount, @@ -26,7 +26,7 @@ const Author = () => { <>
    - +
    diff --git a/src/pages/collections/index.tsx b/src/pages/collections/index.tsx index bfcf84f..fb7a759 100644 --- a/src/pages/collections/index.tsx +++ b/src/pages/collections/index.tsx @@ -3,16 +3,13 @@ import SEO from '@components/seo'; import CollectionsArea from '@containers/collections/collections.container'; import { useEffect } from 'react'; import { findFilteredCollections } from '../../features/collections/store/collections.actions'; -import { - resetCollectionsFilters, - selectCollectionsState, -} from '../../features/collections/store/collections.slice'; +import { resetCollectionsFilters } from '../../features/collections/store/collections.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; const CollectionsPage = () => { const dispatch = useAppDispatch(); - const { collectionsFilters } = useAppSelector(selectCollectionsState); + const { collectionsFilters } = useAppSelector((state) => state.collections); useEffect(() => { dispatch(resetCollectionsFilters()); From ad98a29659fea45a10aa4537490fb77c2eab6dda Mon Sep 17 00:00:00 2001 From: AlefrankM Date: Tue, 10 Jan 2023 19:39:37 -0400 Subject: [PATCH 2/5] refactor: adding collection to marketplace --- .../collection-introduction.component.tsx | 5 +- .../collection-items.component.tsx | 25 +- .../collections-filter.component.tsx | 7 +- .../collections/collections-rendered.tsx | 9 +- .../items-collection-filter.component.tsx | 31 +- .../create-page/nft-collection.component.tsx | 2 +- .../bid-tab/delisting-tab-content.tsx | 6 +- .../bid-tab/listing-tab-content.tsx | 8 +- src/components/item-details/title.tsx | 2 +- src/components/item-filter/index.tsx | 2 +- src/components/item/index.tsx | 3 +- .../items-stats/items-stats.components.tsx | 10 +- .../author-intro/AuthorIntroArea.tsx | 2 +- .../author-profile/AuthorProfileArea.tsx | 2 +- .../collection-details.container.tsx | 18 +- .../collection-items.container.tsx | 14 +- .../collections/collections.container.tsx | 13 +- src/containers/collections/index.tsx | 2 +- .../newest-collections.container.tsx | 3 +- src/containers/create-new/index.tsx | 2 +- .../marketplace/MarketplaceArea.tsx | 16 +- .../account/services/account.service.ts | 2 + src/features/account/store/account.actions.ts | 38 +-- src/features/account/store/account.slice.ts | 72 +---- .../services/collections.service.ts | 4 +- .../collections/store/collections.actions.ts | 12 +- .../collections/store/collections.slice.ts | 212 +------------ .../types/CollectionsFiltersTypes.ts | 14 +- .../store/marketplace.slice.spec.ts | 68 ++-- .../marketplace/store/marketplace.slice.ts | 300 +++++++++++++++--- src/layouts/wrapper.tsx | 2 - src/pages/collections/[collectionId].tsx | 13 +- src/pages/collections/index.tsx | 4 +- src/pages/marketplace.tsx | 15 +- src/types/ICollection.ts | 8 - src/types/item-filter-types.ts | 7 - 36 files changed, 412 insertions(+), 541 deletions(-) diff --git a/src/components/collections/collection-introduction.component.tsx b/src/components/collections/collection-introduction.component.tsx index 3424a25..b406b5c 100644 --- a/src/components/collections/collection-introduction.component.tsx +++ b/src/components/collections/collection-introduction.component.tsx @@ -1,13 +1,12 @@ import Image from 'next/image'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import Tooltip from 'react-bootstrap/Tooltip'; -import { selectCollectionsState } from '../../features/collections/store/collections.slice'; +import appConfig from '../../common/configuration/app.config'; import useAppSelector from '../../store/hooks/useAppSelector'; import { formattedAddress } from '../../utils/getFormattedAddress'; -import appConfig from '../../common/configuration/app.config'; const CollectionIntroductionComponent = () => { - const { selectedCollection } = useAppSelector(selectCollectionsState); + const { selectedCollection } = useAppSelector((state) => state.marketplace); const lastUpdateDate = new Date(selectedCollection.updatedAt).toLocaleDateString(); diff --git a/src/components/collections/collection-items.component.tsx b/src/components/collections/collection-items.component.tsx index 116a044..191aa6c 100644 --- a/src/components/collections/collection-items.component.tsx +++ b/src/components/collections/collection-items.component.tsx @@ -8,31 +8,28 @@ import { Item as ItemType } from '../../types/item'; const CollectionProductsComponent = () => { const dispatch = useAppDispatch(); - const { - selectedCollection, - collectionItemsFiltering: { - itemsPagination: { items, totalCount }, - itemsFilters, - isCollectionNftsLoading, - }, - } = useAppSelector((state) => state.collections); + const { items, itemsCount, selectedCollection, isPagingLoading, filters } = useAppSelector( + (state) => state.marketplace + ); - const hasMore = items.length < totalCount; + const hasMore = items.length < itemsCount; const handleNext = useCallback(() => { if (hasMore) { - const newPage = Math.floor(items.length / itemsFilters.limit + 1); - const filters = { ...itemsFilters, page: newPage }; - dispatch(findPagedCollectionItems({ collectionId: selectedCollection.id, filters })); + const newPage = Math.floor(items.length / filters.limit + 1); + const newFilters = { ...filters, page: newPage }; + dispatch( + findPagedCollectionItems({ collectionId: selectedCollection.id, filters: newFilters }) + ); } - }, [dispatch, hasMore, itemsFilters, items, selectedCollection.id]); + }, [dispatch, filters, hasMore, items.length, selectedCollection.id]); const infiniteScrollSettings = { style: { overflow: 'inherit' }, dataLength: items.length, handleNext, hasMore, - loading: isCollectionNftsLoading, + loading: isPagingLoading, endMessageDisplay: 'Looking for more NFTs?', endMessageLink: '/create', endMessageLinkDetails: 'Create one!', diff --git a/src/components/collections/collections-filter.component.tsx b/src/components/collections/collections-filter.component.tsx index ec1176c..3ee5be3 100644 --- a/src/components/collections/collections-filter.component.tsx +++ b/src/components/collections/collections-filter.component.tsx @@ -1,16 +1,13 @@ import NiceSelect from '@ui/nice-select'; import clsx from 'clsx'; import { useState } from 'react'; -import { - selectCollectionsState, - setCollectionsFilters, -} from '../../features/collections/store/collections.slice'; +import { setCollectionsFilters } from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; const CollectionsFilter = () => { const dispatch = useAppDispatch(); - const { collectionsFilters } = useAppSelector(selectCollectionsState); + const { collectionsFilters } = useAppSelector((state) => state.marketplace); const [isOpen, setIsOpen] = useState(false); const [localSearch, setLocalSearch] = useState(''); const handleTriggerButton = () => setIsOpen((prev) => !prev); diff --git a/src/components/collections/collections-rendered.tsx b/src/components/collections/collections-rendered.tsx index 3f07255..7993f10 100644 --- a/src/components/collections/collections-rendered.tsx +++ b/src/components/collections/collections-rendered.tsx @@ -1,7 +1,6 @@ import InfiniteScroll from '@components/common/InfiniteScroll'; import { useCallback } from 'react'; import { findPagedCollections } from '../../features/collections/store/collections.actions'; -import { selectCollectionsState } from '../../features/collections/store/collections.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; import { ICollection } from '../../types/ICollection'; @@ -9,11 +8,11 @@ import CollectionComponent from './collection.component'; const CollectionRendered = () => { const dispatch = useAppDispatch(); - const { collectionsFilters, collectionPagination, isPagingLoading } = - useAppSelector(selectCollectionsState); - const { collections, totalCount } = collectionPagination; + const { collectionsFilters, isPagingLoading, collections, collectionsCount } = useAppSelector( + (state) => state.marketplace + ); - const hasMore = collections.length < totalCount; + const hasMore = collections.length < collectionsCount; const handleNext = useCallback(() => { if (hasMore) { diff --git a/src/components/collections/items-collection-filter.component.tsx b/src/components/collections/items-collection-filter.component.tsx index 1c7a881..8a84714 100644 --- a/src/components/collections/items-collection-filter.component.tsx +++ b/src/components/collections/items-collection-filter.component.tsx @@ -5,8 +5,8 @@ import Sticky from '@ui/sticky'; import { useEffect, useMemo, useState } from 'react'; import { Range } from 'react-range'; import { IRenderTrackParams } from 'react-range/lib/types'; -import { findPriceRange } from '../../features/collections/store/collections.actions'; -import { setCollectionsNftsFilters } from '../../features/collections/store/collections.slice'; +import { findCollectionsByPriceRange } from '../../features/collections/store/collections.actions'; +import { setCollectionsNftsFilters } from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; import { selectUiReducer } from '../../store/ui/ui.slice'; @@ -30,10 +30,9 @@ const ItemCollectionFilter = () => { }); const [valuesRange, setValuesRange] = useState([]); const [step, setStep] = useState(DEFAULT_STEP); - const { - selectedCollection, - collectionItemsFiltering: { itemsFilters, itemsPagination }, - } = useAppSelector((state) => state.collections); + const { items, itemsCount, selectedCollection, filters } = useAppSelector( + (state) => state.marketplace + ); const stickyPadding = useMemo( () => (isNetworkAdviceOpen ? '150px' : '100px'), @@ -41,10 +40,10 @@ const ItemCollectionFilter = () => { ); useEffect(() => { - if (itemsPagination.items.length && itemsPagination.totalCount) { - const itemsWithPrice = itemsPagination.items.filter((item) => item.price !== null); + if (items.length && itemsCount) { + const itemsWithPrice = items.filter((item) => item.price !== null); if (itemsWithPrice.length) { - dispatch(findPriceRange(selectedCollection.id)).then(({ payload }) => { + dispatch(findCollectionsByPriceRange(selectedCollection.id)).then(({ payload }) => { const { from, to } = payload as { from: number; to: number }; setPriceRange({ cheapest: from, @@ -53,12 +52,12 @@ const ItemCollectionFilter = () => { }); } } - }, [dispatch, itemsPagination.items, itemsPagination.totalCount, selectedCollection.id]); + }, [dispatch, items, itemsCount, selectedCollection.id]); useEffect(() => { - if (Number(itemsFilters.mostExpensive) > 0 && Number(itemsFilters.cheapest) > 0) - setValuesRange([+itemsFilters.cheapest, +itemsFilters.mostExpensive]); - }, [itemsFilters.mostExpensive, itemsFilters.cheapest]); + if (Number(filters.mostExpensive) > 0 && Number(filters.cheapest) > 0) + setValuesRange([+filters.cheapest, +filters.mostExpensive]); + }, [filters.mostExpensive, filters.cheapest]); useEffect(() => { if (cheapest >= 0 && mostExpensive >= 0) { @@ -71,7 +70,7 @@ const ItemCollectionFilter = () => { }, [cheapest, mostExpensive]); const handleLikesChange = (likesDirection: string) => { - dispatch(setCollectionsNftsFilters({ ...itemsFilters, likesDirection })); + dispatch(setCollectionsNftsFilters({ ...filters, likesDirection })); }; const renderTrack = (props: IRenderTrackParams) => ( @@ -84,7 +83,7 @@ const ItemCollectionFilter = () => { const handleSearch = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - dispatch(setCollectionsNftsFilters({ ...itemsFilters, search: localSearch })); + dispatch(setCollectionsNftsFilters({ ...filters, search: localSearch })); } }; @@ -95,7 +94,7 @@ const ItemCollectionFilter = () => { const handlePriceRangeFinalChange = ([from, to]: number[]) => { dispatch( setCollectionsNftsFilters({ - ...itemsFilters, + ...filters, priceRange: { from, to }, }) ); diff --git a/src/components/create-page/nft-collection.component.tsx b/src/components/create-page/nft-collection.component.tsx index ade3dba..208e0b3 100644 --- a/src/components/create-page/nft-collection.component.tsx +++ b/src/components/create-page/nft-collection.component.tsx @@ -18,7 +18,7 @@ const NftCollectionComponent = () => { const dispatch = useAppDispatch(); const { address } = useMetamask(); const [open, setOpen] = useState(false); - const myCollections = useAppSelector((state) => state.account.collections); + const myCollections = useAppSelector((state) => state.marketplace.collections); const [dropdownCollection, setDropdownCollection] = useState(''); const [collectionModalOpen, setCollectionModalOpen] = useState(false); const [selectedCollectionImage, setSelectedCollectionImage] = useState(null); diff --git a/src/components/item-details/bid-tab/delisting-tab-content.tsx b/src/components/item-details/bid-tab/delisting-tab-content.tsx index 52b4b4d..f321c0c 100644 --- a/src/components/item-details/bid-tab/delisting-tab-content.tsx +++ b/src/components/item-details/bid-tab/delisting-tab-content.tsx @@ -8,7 +8,7 @@ import useAppSelector from '../../../store/hooks/useAppSelector'; import ActionLoaderComponent from '../../action-loader/action-loader.component'; export const DelistingTabContent = () => { - const { isLoading, selectedItem, isModalOpen } = useAppSelector((state) => state.marketplace); + const { isDelisting, selectedItem, isModalOpen } = useAppSelector((state) => state.marketplace); const dispatch = useAppDispatch(); const { address } = useMetamask(); @@ -36,7 +36,7 @@ export const DelistingTabContent = () => {
    {
    { const { address } = useMetamask(); const [isValid, setIsValid] = useState(false); const [price, setPrice] = useState(''); - const { isLoading, selectedItem, isModalOpen } = useAppSelector((state) => state.marketplace); + const { isListing, selectedItem, isModalOpen } = useAppSelector((state) => state.marketplace); const dispatch = useAppDispatch(); const { register, @@ -102,7 +102,7 @@ export const ListingTabContent = () => {
    {

    Once you list this NFT, it will be shown on the marketplace

    - {isLoading && ( + {isListing && ( This transaction may take a few seconds )}
    @@ -148,7 +148,7 @@ export const ListingTabContent = () => {
    { const dispatch = useAppDispatch(); const { selectedItem: { name: title, likes: likeCount, itemId, collection, isHidden }, + likedItems, } = useAppSelector((state) => state.marketplace); const isOwner = useAppSelector(selectIsOwner); @@ -24,7 +25,6 @@ const ProductTitle = ({ className }: Props) => { const handleLikeItem = () => { dispatch(withAuthProtection(likeItem(itemId))); }; - const { likedItems } = useAppSelector((state) => state.account); const isLiked = useMemo( () => Boolean(likedItems.find((likedItem) => likedItem.itemId === itemId)), diff --git a/src/components/item-filter/index.tsx b/src/components/item-filter/index.tsx index 78dcf64..9973a6e 100644 --- a/src/components/item-filter/index.tsx +++ b/src/components/item-filter/index.tsx @@ -23,7 +23,7 @@ const STEP_PRECISION = 3; const ItemFilter = ({ cheapest, mostExpensive }: Props) => { const dispatch = useAppDispatch(); - const { marketplaceFilters } = useAppSelector(selectNFTsMarketplace); + const { filters: marketplaceFilters } = useAppSelector(selectNFTsMarketplace); const [isOpen, setIsOpen] = useState(false); const [valuesRange, setValuesRange] = useState([] as number[]); const [step, setStep] = useState(DEFAULT_STEP); diff --git a/src/components/item/index.tsx b/src/components/item/index.tsx index 1adaa2f..ee02d8c 100644 --- a/src/components/item/index.tsx +++ b/src/components/item/index.tsx @@ -64,8 +64,7 @@ const Product = ({ isLazy, }: Props) => { const dispatch = useAppDispatch(); - const { isModalOpen } = useAppSelector((state) => state.marketplace); - const { likedItems } = useAppSelector((state) => state.account); + const { isModalOpen, likedItems } = useAppSelector((state) => state.marketplace); const handleBuyModal = () => { dispatch(setIsModalOpen(!isModalOpen)); }; diff --git a/src/components/items-stats/items-stats.components.tsx b/src/components/items-stats/items-stats.components.tsx index bd432a5..d4f351f 100644 --- a/src/components/items-stats/items-stats.components.tsx +++ b/src/components/items-stats/items-stats.components.tsx @@ -17,7 +17,7 @@ import { Item } from '../../types/item'; const LikeRender = ({ likes, itemId }: { likes: number; itemId: string }) => { const dispatch = useAppDispatch(); - const { likedItems } = useAppSelector((state) => state.account); + const { likedItems } = useAppSelector((state) => state.marketplace); const isLiked = useMemo( () => Boolean(likedItems.find((likedItem) => likedItem.itemId === itemId)), @@ -41,10 +41,12 @@ const ItemStatsComponent = () => { const dispatch = useAppDispatch(); const { selectedCollection, - itemsStats: { page, items, totalCount }, - } = useAppSelector((state) => state.collections); + items, + itemsCount, + filters: { page }, + } = useAppSelector((state) => state.marketplace); - const hasMore = items.length < totalCount; + const hasMore = items.length < itemsCount; const getMore = useCallback( (pg = 1) => { diff --git a/src/containers/author-intro/AuthorIntroArea.tsx b/src/containers/author-intro/AuthorIntroArea.tsx index fa7e82b..78b38d7 100644 --- a/src/containers/author-intro/AuthorIntroArea.tsx +++ b/src/containers/author-intro/AuthorIntroArea.tsx @@ -17,7 +17,7 @@ export const AuthorIntroArea = ({ address }: Props) => { const shareModalHandler = () => setIsShareModalOpen((prev) => !prev); const handleReportModal = () => setShowReportModal((prev) => !prev); - const likedItems = useAppSelector((state) => state.account.likedItems); + const { likedItems } = useAppSelector((state) => state.marketplace); const likedItemsToShow = useMemo( () => diff --git a/src/containers/author-profile/AuthorProfileArea.tsx b/src/containers/author-profile/AuthorProfileArea.tsx index e4f4a61..1278a8b 100644 --- a/src/containers/author-profile/AuthorProfileArea.tsx +++ b/src/containers/author-profile/AuthorProfileArea.tsx @@ -18,7 +18,7 @@ type Props = { export const AuthorProfileArea = ({ address }: Props) => { const createdItems = useAppSelector((state) => selectCreatedItems(state, address)); - const { likedItems } = useAppSelector((state) => state.account); + const { likedItems } = useAppSelector((state) => state.marketplace); const onSaleItems = useAppSelector((state) => selectOnSaleItems(state, address)); const ownedItems = useAppSelector((state) => selectOwnedItems(state, address)); diff --git a/src/containers/collection-details/collection-details.container.tsx b/src/containers/collection-details/collection-details.container.tsx index a7dca8e..244f055 100644 --- a/src/containers/collection-details/collection-details.container.tsx +++ b/src/containers/collection-details/collection-details.container.tsx @@ -15,7 +15,7 @@ const NotFound = () => ( const CollectionDetailsContainer = () => { const [isStatsVisible, setIsStatsVisible] = useState(false); - const { selectedCollection } = useAppSelector((state) => state.collections); + const { selectedCollection } = useAppSelector((state) => state.marketplace); if (!Object.entries(selectedCollection).length) return ; @@ -26,28 +26,20 @@ const CollectionDetailsContainer = () => {
    diff --git a/src/containers/collection-details/collection-items.container.tsx b/src/containers/collection-details/collection-items.container.tsx index d57f94b..a719ece 100644 --- a/src/containers/collection-details/collection-items.container.tsx +++ b/src/containers/collection-details/collection-items.container.tsx @@ -6,17 +6,15 @@ import { useMemo } from 'react'; import useAppSelector from '../../store/hooks/useAppSelector'; const CollectionItemsContainer = () => { - const { itemsPagination, isCollectionNftsLoading } = useAppSelector( - (state) => state.collections.collectionItemsFiltering - ); + const { isPagingLoading, items } = useAppSelector((state) => state.marketplace); const renderedComponent = useMemo(() => { - if (itemsPagination.items.length) return ; + if (items.length) return ; - if (!isCollectionNftsLoading) return ; + if (!isPagingLoading) return ; return null; - }, [itemsPagination.items.length, isCollectionNftsLoading]); + }, [items.length, isPagingLoading]); return (
    @@ -28,9 +26,7 @@ const CollectionItemsContainer = () => {
    - - {renderedComponent} - + {renderedComponent}
    diff --git a/src/containers/collections/collections.container.tsx b/src/containers/collections/collections.container.tsx index 6919180..590ef2e 100644 --- a/src/containers/collections/collections.container.tsx +++ b/src/containers/collections/collections.container.tsx @@ -3,15 +3,16 @@ import CollectionRendered from '@components/collections/collections-rendered'; import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import Link from 'next/link'; import { useMemo } from 'react'; -import { selectCollectionsState } from '../../features/collections/store/collections.slice'; import useAppSelector from '../../store/hooks/useAppSelector'; const CollectionsContainer = () => { - const { collectionPagination, isLoadingCollections } = useAppSelector(selectCollectionsState); + const { collections, collectionsCount, isLoadingCollections } = useAppSelector( + (state) => state.marketplace + ); const renderedComponent = useMemo(() => { - if (collectionPagination.collections.length) return ; - if (collectionPagination.totalCount === 0) + if (collections.length) return ; + if (collectionsCount === 0) return (

    @@ -24,11 +25,11 @@ const CollectionsContainer = () => { ); return null; - }, [collectionPagination.collections.length, collectionPagination.totalCount]); + }, [collections.length, collectionsCount]); return (

    -
    {!!collectionPagination.totalCount && }
    +
    {!!collectionsCount && }
    {renderedComponent}
    ); diff --git a/src/containers/collections/index.tsx b/src/containers/collections/index.tsx index fb85444..7d167b3 100644 --- a/src/containers/collections/index.tsx +++ b/src/containers/collections/index.tsx @@ -54,7 +54,7 @@ const collectionsErrors = { }; const CreateNewArea = () => { - const { collectionsWithoutItems } = useAppSelector((state) => state.account); + const { collectionsWithoutItems } = useAppSelector((state) => state.marketplace); const [properties, setProperties] = useState([]); const [propertiesModalMessage, setPropertiesModalMessage] = useState(''); const [propsModalOpen, setPropsModalOpen] = useState(false); diff --git a/src/containers/collections/newest-collections.container.tsx b/src/containers/collections/newest-collections.container.tsx index df7d669..e2ec70d 100644 --- a/src/containers/collections/newest-collections.container.tsx +++ b/src/containers/collections/newest-collections.container.tsx @@ -4,7 +4,6 @@ import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import { useEffect, useMemo } from 'react'; import ClipLoader from 'react-spinners/ClipLoader'; import { getNewestCollections } from '../../features/collections/store/collections.actions'; -import { selectCollectionsState } from '../../features/collections/store/collections.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; import { ICollection } from '../../types/ICollection'; @@ -17,7 +16,7 @@ const NotFound = () => ( const NewestCollectionArea = () => { const dispatch = useAppDispatch(); - const { newestCollections, isLoadingCollections } = useAppSelector(selectCollectionsState); + const { newestCollections, isLoadingCollections } = useAppSelector((state) => state.marketplace); const qtyItemsToFetch = 4; diff --git a/src/containers/create-new/index.tsx b/src/containers/create-new/index.tsx index b29dc4a..e3ccee2 100644 --- a/src/containers/create-new/index.tsx +++ b/src/containers/create-new/index.tsx @@ -66,7 +66,7 @@ const defaultCollection = { } as CollectionCreateType; const CreateNewArea = () => { - const { collectionsWithoutItems } = useAppSelector((state) => state.account); + const { collectionsWithoutItems } = useAppSelector((state) => state.marketplace); const [properties, setProperties] = useState([]); const [propertiesModalMessage, setPropertiesModalMessage] = useState(''); const [propsModalOpen, setPropsModalOpen] = useState(false); diff --git a/src/containers/marketplace/MarketplaceArea.tsx b/src/containers/marketplace/MarketplaceArea.tsx index a8d72ca..84bdc2a 100644 --- a/src/containers/marketplace/MarketplaceArea.tsx +++ b/src/containers/marketplace/MarketplaceArea.tsx @@ -1,19 +1,21 @@ -import { Item as ItemType } from '@types'; -import { useCallback } from 'react'; import InfiniteScroll from '@components/common/InfiniteScroll'; import Item from '@components/item'; -import { selectNFTsMarketplace } from '../../features/marketplace/store/marketplace.slice'; +import { Item as ItemType } from '@types'; +import { useCallback } from 'react'; import { findPagedItems } from '../../features/marketplace/store/marketplace.actions'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; export const MarketplaceArea = () => { const dispatch = useAppDispatch(); - const { marketplaceFilters, itemPagination, isPagingLoading } = - useAppSelector(selectNFTsMarketplace); - const { items, totalCount } = itemPagination; + const { + filters: marketplaceFilters, + items, + itemsCount: count, + isPagingLoading, + } = useAppSelector((state) => state.marketplace); - const hasMore = items.length < totalCount; + const hasMore = items.length < count; const handleNext = useCallback(() => { if (hasMore) { diff --git a/src/features/account/services/account.service.ts b/src/features/account/services/account.service.ts index 2493033..0672968 100644 --- a/src/features/account/services/account.service.ts +++ b/src/features/account/services/account.service.ts @@ -34,3 +34,5 @@ export default class AccountService extends HttpService { return data; } } + +export const accountService = new AccountService(); diff --git a/src/features/account/store/account.actions.ts b/src/features/account/store/account.actions.ts index ba0f195..87dfdc2 100644 --- a/src/features/account/store/account.actions.ts +++ b/src/features/account/store/account.actions.ts @@ -1,43 +1,25 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { Item } from '@types'; import { ICollection } from '../../../types/ICollection'; -import AccountService from '../services/account.service'; +import { accountService } from '../services/account.service'; -const findItemsByAccount = createAsyncThunk( +export const findItemsByAccount = createAsyncThunk( 'account/findItemsByAccount', - async (address: string): Promise => { - const accountService = new AccountService(); - return accountService.findItemsByAccount(address); - } + async (address: string): Promise => accountService.findItemsByAccount(address) ); -const findLikedItemsByAccount = createAsyncThunk( +export const findLikedItemsByAccount = createAsyncThunk( 'account/findLikedItemsByAccount', - async (address: string): Promise => { - const accountService = new AccountService(); - return accountService.findLikedItemsByAccount(address); - } + async (address: string): Promise => accountService.findLikedItemsByAccount(address) ); -const findUserCollections = createAsyncThunk( +export const findUserCollections = createAsyncThunk( 'account/findUserCollections', - async (address: string): Promise => { - const accountService = new AccountService(); - return accountService.findUserCollections(address); - } + async (address: string): Promise => accountService.findUserCollections(address) ); -const findUserCollectionsWithoutItems = createAsyncThunk( +export const findUserCollectionsWithoutItems = createAsyncThunk( 'account/findUserCollectionsWithoutItems', - async (address: string): Promise => { - const accountService = new AccountService(); - return accountService.findUserCollectionsWithoutItems(address); - } + async (address: string): Promise => + accountService.findUserCollectionsWithoutItems(address) ); - -export { - findItemsByAccount, - findLikedItemsByAccount, - findUserCollections, - findUserCollectionsWithoutItems, -}; diff --git a/src/features/account/store/account.slice.ts b/src/features/account/store/account.slice.ts index f6324a8..f2c018d 100644 --- a/src/features/account/store/account.slice.ts +++ b/src/features/account/store/account.slice.ts @@ -2,31 +2,13 @@ import { createSelector, createSlice } from '@reduxjs/toolkit'; import { Item } from '@types'; import ItemStatus from '../../../common/minting/enums/item-status.enum'; import type { RootState } from '../../../store/types'; -import { ICollection, ICollectionWithoutItems } from '../../../types/ICollection'; -import { likeItem } from '../../marketplace/store/marketplace.actions'; -import { - findItemsByAccount, - findLikedItemsByAccount, - findUserCollections, - findUserCollectionsWithoutItems, -} from './account.actions'; type AccountState = { - items: Item[]; - likedItems: Item[]; isLoading: boolean; - collections: ICollection[]; - collectionsWithoutItems: ICollectionWithoutItems[]; - isLoadingCollection: boolean; imageNumber: number; }; const initialState: AccountState = { - items: [], - likedItems: [], - collections: [], - collectionsWithoutItems: [], - isLoadingCollection: false, isLoading: false, imageNumber: 1, }; @@ -39,75 +21,27 @@ const accountSlice = createSlice({ state.imageNumber = payload; }, }, - extraReducers: (builder) => { - builder.addCase(findUserCollectionsWithoutItems.pending, (state) => { - state.isLoadingCollection = true; - }); - builder.addCase(findUserCollectionsWithoutItems.fulfilled, (state, { payload }) => { - state.isLoadingCollection = false; - state.collectionsWithoutItems = payload; - }); - builder.addCase(findUserCollectionsWithoutItems.rejected, (state) => { - state.isLoadingCollection = false; - }); - builder.addCase(findUserCollections.pending, (state) => { - state.isLoadingCollection = true; - }); - builder.addCase(findUserCollections.fulfilled, (state, { payload }) => { - state.collections = payload; - state.isLoadingCollection = false; - }); - builder.addCase(findUserCollections.rejected, (state) => { - state.isLoadingCollection = false; - }); - builder.addCase(findItemsByAccount.pending, (state) => { - state.isLoading = true; - }); - builder.addCase(findItemsByAccount.fulfilled, (state, { payload }) => { - state.items = payload; - }); - builder.addCase(findLikedItemsByAccount.pending, (state) => { - state.isLoading = true; - }); - builder.addCase(findLikedItemsByAccount.fulfilled, (state, { payload }) => { - state.likedItems = payload; - }); - builder.addCase(likeItem.fulfilled, (state, { payload }) => { - const index = state.items.findIndex((i) => i.itemId === payload.itemId); - state.items[index] = payload; - - const likedIndex = state.likedItems.findIndex((i) => i.itemId === payload.itemId); - - if (likedIndex !== -1) state.likedItems.splice(likedIndex, 1); - else state.likedItems.push(payload); - }); - }, }); export const selectAccountState = (state: RootState) => state.account; export const { setProfileImage } = accountSlice.actions; -export const selectItems = createSelector( - (state: RootState) => state.account.items, - (items: Item[]) => items -); - export const selectCreatedItems = createSelector( - selectItems, + (state: RootState) => state.marketplace.items, (_: unknown, address: string) => address, (items: Item[], address: string) => items.filter((item) => item.author.address === address) ); export const selectOnSaleItems = createSelector( - selectItems, + (state: RootState) => state.marketplace.items, (_: unknown, address: string) => address, (items: Item[], address: string) => items.filter((item) => item.owner.address === address && item.status === ItemStatus.Listed) ); export const selectOwnedItems = createSelector( - selectItems, + (state: RootState) => state.marketplace.items, (_: unknown, address: string) => address, (items: Item[], address: string) => items.filter((item) => item.owner.address === address) ); diff --git a/src/features/collections/services/collections.service.ts b/src/features/collections/services/collections.service.ts index eb9bf60..19d1eb8 100644 --- a/src/features/collections/services/collections.service.ts +++ b/src/features/collections/services/collections.service.ts @@ -3,7 +3,7 @@ import { ICollection } from '../../../types/ICollection'; import { Item } from '../../../types/item'; import { FilterType, PriceRangeType } from '../../../types/item-filter-types'; import ICollectionService from '../interfaces/collections-service.interface'; -import { CollectionsFiltersTypes } from '../types/CollectionsFiltersTypes'; +import { CollectionFilterType } from '../types/CollectionsFiltersTypes'; export default class CollectionsService extends HttpService implements ICollectionService { private readonly endpoint: string; @@ -29,7 +29,7 @@ export default class CollectionsService extends HttpService implements ICollecti } async findPagedCollections( - filters: CollectionsFiltersTypes + filters: CollectionFilterType ): Promise<{ collections: ICollection[]; totalCount: number }> { const { limit, page, search, popularityOrder, creationOrder, mintType } = filters; const { data } = await this.instance.get<{ collections: ICollection[]; totalCount: number }>( diff --git a/src/features/collections/store/collections.actions.ts b/src/features/collections/store/collections.actions.ts index c617b99..3405eca 100644 --- a/src/features/collections/store/collections.actions.ts +++ b/src/features/collections/store/collections.actions.ts @@ -1,29 +1,25 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { FilterType } from '../../../types/item-filter-types'; import { collectionsService } from '../services/collections.service'; -import { CollectionsFiltersTypes } from '../types/CollectionsFiltersTypes'; +import { CollectionFilterType } from '../types/CollectionsFiltersTypes'; export const findCollectionById = createAsyncThunk( 'collections/findById', async (collectionId: string) => collectionsService.findById(collectionId) ); -export const findAllCollections = createAsyncThunk('collections/findAll', async () => - collectionsService.findAll() -); - export const getNewestCollections = createAsyncThunk('collections/getNewest', async (qty: number) => collectionsService.findNewest(qty) ); export const findPagedCollections = createAsyncThunk( 'collections/findPagedCollections', - async (filters: CollectionsFiltersTypes) => collectionsService.findPagedCollections(filters) + async (filters: CollectionFilterType) => collectionsService.findPagedCollections(filters) ); export const findFilteredCollections = createAsyncThunk( 'collections/findFilteredCollections', - async (filters: CollectionsFiltersTypes) => collectionsService.findPagedCollections(filters) + async (filters: CollectionFilterType) => collectionsService.findPagedCollections(filters) ); export const findFilteredCollectionItems = createAsyncThunk( @@ -38,7 +34,7 @@ export const findPagedCollectionsNfts = createAsyncThunk( collectionsService.findPagedCollectionsNfts(collectionId, page) ); -export const findPriceRange = createAsyncThunk( +export const findCollectionsByPriceRange = createAsyncThunk( 'collections/findPriceRange', async (collectionId: string) => collectionsService.findPriceRangeCollectionItems(collectionId) ); diff --git a/src/features/collections/store/collections.slice.ts b/src/features/collections/store/collections.slice.ts index a9135c3..87f7500 100644 --- a/src/features/collections/store/collections.slice.ts +++ b/src/features/collections/store/collections.slice.ts @@ -1,229 +1,21 @@ import { createSlice } from '@reduxjs/toolkit'; -import { ICollection } from '../../../types/ICollection'; -import { - findCollectionById, - getNewestCollections, - findPagedCollections, - findFilteredCollections, - findPagedCollectionsNfts, - findPriceRange, - findFilteredCollectionItems, - findPagedCollectionItems, -} from './collections.actions'; import type { RootState } from '../../../store/types'; -import { CollectionPagination, CollectionsFiltersTypes } from '../types/CollectionsFiltersTypes'; -import { FilterType, ItemPagination } from '../../../types/item-filter-types'; -import { Item } from '../../../types/item'; -import { likeItem } from '../../marketplace/store/marketplace.actions'; type CollectionsState = { - selectedCollection: ICollection; - itemsStats: { - items: Item[]; - totalCount: number; - page: number; - limit: number; - isLoadingItemsStats: boolean; - }; - collectionItemsFiltering: { - itemsFilters: FilterType; - itemsPagination: ItemPagination; - isCollectionNftsLoading: boolean; - }; - newestCollections: ICollection[]; - isLoadingCollections: boolean; - collectionsFilters: CollectionsFiltersTypes; - collectionPagination: CollectionPagination; isPagingLoading: boolean; }; const initialState: CollectionsState = { - newestCollections: [] as ICollection[], - itemsStats: { - items: [], - totalCount: 0, - page: 1, - limit: 3, - isLoadingItemsStats: false, - }, - collectionItemsFiltering: { - itemsFilters: { - likesDirection: '', - search: '', - priceRange: { - from: '', - to: '', - }, - cheapest: '', - mostExpensive: '', - page: 1, - limit: 3, - } as FilterType, - itemsPagination: { items: [], totalCount: 0 }, - isCollectionNftsLoading: false, - }, - selectedCollection: {} as ICollection, - isLoadingCollections: false, - collectionsFilters: { - search: '', - popularityOrder: '', - creationOrder: '', - mintType: '', - page: 1, - limit: 3, - }, - collectionPagination: { collections: [], totalCount: 0 }, isPagingLoading: false, }; const collectionsSlice = createSlice({ name: 'collections', initialState, - reducers: { - setCollectionsFilters: (state, { payload }) => { - state.collectionsFilters = payload; - }, - resetCollectionsFilters: (state) => { - state.collectionsFilters = initialState.collectionsFilters; - }, - resetSelectedCollectionStats: (state) => { - state.itemsStats = initialState.itemsStats; - }, - setCollectionsNftsFilters: (state, { payload }) => { - state.collectionItemsFiltering.itemsFilters = payload; - }, - resetCollectionsNftFilters: (state) => { - state.collectionItemsFiltering.itemsFilters = - initialState.collectionItemsFiltering.itemsFilters; - }, - setSelectedCollection: (state, { payload }) => { - state.selectedCollection = payload; - }, - }, - extraReducers: (builder) => { - builder.addCase(findPagedCollectionItems.pending, (state) => { - state.collectionItemsFiltering.isCollectionNftsLoading = true; - }); - builder.addCase(findPagedCollectionItems.fulfilled, (state, { payload }) => { - state.collectionItemsFiltering.itemsPagination.items = [ - ...state.collectionItemsFiltering.itemsPagination.items, - ...payload.items, - ]; - state.collectionItemsFiltering.isCollectionNftsLoading = false; - }); - builder.addCase(findPagedCollectionItems.rejected, (state) => { - state.collectionItemsFiltering.isCollectionNftsLoading = false; - }); - // find filtered collection items - builder.addCase(findFilteredCollectionItems.pending, (state) => { - state.collectionItemsFiltering.isCollectionNftsLoading = true; - }); - builder.addCase(findFilteredCollectionItems.fulfilled, (state, { payload }) => { - state.collectionItemsFiltering.itemsPagination = payload; - state.collectionItemsFiltering.isCollectionNftsLoading = false; - }); - builder.addCase(findFilteredCollectionItems.rejected, (state) => { - state.collectionItemsFiltering.isCollectionNftsLoading = false; - }); - // find price range with items inside a collection - builder.addCase(findPriceRange.pending, (state) => { - state.collectionItemsFiltering.isCollectionNftsLoading = true; - }); - builder.addCase(findPriceRange.fulfilled, (state, { payload }) => { - state.collectionItemsFiltering.itemsFilters.cheapest = payload.from; - state.collectionItemsFiltering.itemsFilters.mostExpensive = payload.to; - state.collectionItemsFiltering.isCollectionNftsLoading = false; - }); - builder.addCase(findPriceRange.rejected, (state) => { - state.collectionItemsFiltering.isCollectionNftsLoading = false; - }); - // find nfts from a collections - builder.addCase(findPagedCollectionsNfts.pending, (state) => { - state.itemsStats.isLoadingItemsStats = true; - }); - builder.addCase(findPagedCollectionsNfts.fulfilled, (state, { payload }) => { - state.itemsStats.limit = payload.limit; - state.itemsStats.page = payload.page; - state.itemsStats.totalCount = payload.totalCount; - state.itemsStats.items = [...state.itemsStats.items, ...payload.items]; - state.itemsStats.isLoadingItemsStats = false; - }); - builder.addCase(findPagedCollectionsNfts.rejected, (state) => { - state.itemsStats.isLoadingItemsStats = false; - }); - // find filtered collections - builder.addCase(findFilteredCollections.pending, (state) => { - state.isLoadingCollections = true; - }); - builder.addCase(findFilteredCollections.fulfilled, (state, { payload }) => { - state.collectionPagination = payload; - state.isLoadingCollections = false; - }); - builder.addCase(findFilteredCollections.rejected, (state) => { - state.isLoadingCollections = false; - }); - // find paginated collections - builder.addCase(findPagedCollections.pending, (state) => { - state.isPagingLoading = true; - }); - builder.addCase(findPagedCollections.fulfilled, (state, { payload }) => { - state.collectionPagination.collections = [ - ...state.collectionPagination.collections, - ...payload.collections, - ]; - state.collectionPagination.totalCount = payload.totalCount; - state.isLoadingCollections = false; - }); - builder.addCase(findPagedCollections.rejected, (state) => { - state.isLoadingCollections = false; - }); - // get latets collections - builder.addCase(getNewestCollections.pending, (state) => { - state.isLoadingCollections = true; - }); - builder.addCase(getNewestCollections.fulfilled, (state, { payload }) => { - state.newestCollections = payload; - state.isLoadingCollections = false; - }); - builder.addCase(getNewestCollections.rejected, (state) => { - state.isLoadingCollections = false; - }); - // get collection by id - builder.addCase(findCollectionById.pending, (state) => { - state.isLoadingCollections = true; - }); - builder.addCase(findCollectionById.fulfilled, (state, { payload }) => { - state.selectedCollection = payload; - state.isLoadingCollections = false; - }); - builder.addCase(findCollectionById.rejected, (state) => { - state.isLoadingCollections = false; - }); - builder.addCase(likeItem.fulfilled, (state, { payload }) => { - const indexItemsStats = state.itemsStats.items.findIndex((i) => i.itemId === payload.itemId); - const indexItemsFiltering = state.collectionItemsFiltering.itemsPagination.items.findIndex( - (i) => i.itemId === payload.itemId - ); - state.itemsStats.items[indexItemsStats] = payload; - state.collectionItemsFiltering.itemsPagination.items[indexItemsFiltering] = payload; - }); - }, + reducers: {}, + extraReducers: (builder) => {}, }); export const collectionsReducer = collectionsSlice.reducer; -export const { - resetCollectionsFilters, - setCollectionsNftsFilters, - setCollectionsFilters, - resetSelectedCollectionStats, - resetCollectionsNftFilters, - setSelectedCollection, -} = collectionsSlice.actions; - export const selectCollectionsState = (state: RootState) => state.collections; - -export const selectCurrentSelection = (state: RootState) => state.collections.selectedCollection; - -export const selectCurrentSelectionItemsFiltering = (state: RootState) => - state.collections.collectionItemsFiltering; diff --git a/src/features/collections/types/CollectionsFiltersTypes.ts b/src/features/collections/types/CollectionsFiltersTypes.ts index fbd1bf5..ae1326e 100644 --- a/src/features/collections/types/CollectionsFiltersTypes.ts +++ b/src/features/collections/types/CollectionsFiltersTypes.ts @@ -1,15 +1,7 @@ -import { ICollection } from '../../../types/ICollection'; +import { FilterTypeBase } from '../../../types/item-filter-types'; -export type CollectionsFiltersTypes = { - search: string; +export type CollectionFilterType = { popularityOrder: string | 'asc' | 'desc'; creationOrder: string | 'asc' | 'desc'; mintType: string | 'lazy' | 'normal'; - limit: number; - page: number; -}; - -export type CollectionPagination = { - collections: ICollection[]; - totalCount: number; -}; +} & FilterTypeBase; diff --git a/src/features/marketplace/store/marketplace.slice.spec.ts b/src/features/marketplace/store/marketplace.slice.spec.ts index 25f304f..7d8ae60 100644 --- a/src/features/marketplace/store/marketplace.slice.spec.ts +++ b/src/features/marketplace/store/marketplace.slice.spec.ts @@ -1,4 +1,5 @@ import { AnyAction } from '@reduxjs/toolkit'; +import { ICollection } from '../../../types/ICollection'; import { Item } from '../../../types/item'; import { FilterType } from '../../../types/item-filter-types'; import { findFilteredItems, findPagedItems, getNewest, listItem } from './marketplace.actions'; @@ -15,31 +16,49 @@ describe('Marketplace slice', () => { beforeEach(() => { initialState = { items: [], - isLoading: false, - isPagingLoading: false, - isLoadingHistory: false, - newestItems: [], - isLoadingNewest: false, - marketplaceFilters: { + itemsCount: 0, + likedItems: [], + collections: [], + collectionsCount: 0, + collectionsWithoutItems: [], + filters: { likesDirection: '', - cheapest: '', - mostExpensive: '', search: '', priceRange: { from: '', to: '', }, + cheapest: '', + mostExpensive: '', page: 1, - limit: 15, + limit: 3, } as FilterType, - itemPagination: { items: [], totalCount: 0 }, selectedItem: {} as Item, + newestItems: [], history: { data: [], count: 0, limit: 3, page: 1, }, + newestCollections: [] as ICollection[], + selectedCollection: {} as ICollection, + isLoadingCollections: false, + collectionsFilters: { + search: '', + popularityOrder: '', + creationOrder: '', + mintType: '', + page: 1, + limit: 3, + }, + isLoading: false, + isDelisting: false, + isListing: false, + isPagingLoading: false, + isLoadingHistory: false, + isLoadingNewest: false, + isLoadingCollection: false, isModalOpen: false, isCompleted: false, isOpenPreviewProductModal: false, @@ -119,28 +138,15 @@ describe('Marketplace slice', () => { const actual = marketplaceReducer(undefined, setMarketplaceFilters(expected)); - expect(actual.marketplaceFilters).toEqual(expected); + expect(actual.filters).toEqual(expected); }); }); describe('When resetMarketplaceFilters reducer is called', () => { it('should assign the marketplace filters initial state correctly', () => { - const expected = { - likesDirection: '', - cheapest: '', - mostExpensive: '', - search: '', - priceRange: { - from: '', - to: '', - }, - limit: 15, - page: 1, - } as FilterType; - const actual = marketplaceReducer(undefined, resetMarketplaceFilters()); - expect(actual.marketplaceFilters).toEqual(expected); + expect(actual.filters).toEqual(initialState.filters); }); }); @@ -168,8 +174,8 @@ describe('Marketplace slice', () => { findFilteredItems.pending('', {} as FilterType) ); - expect(actual.itemPagination.items.length).toEqual(0); - expect(actual.itemPagination.totalCount).toEqual(0); + expect(actual.items.length).toEqual(0); + expect(actual.itemsCount).toEqual(0); expect(actual.isLoading).toEqual(true); }); }); @@ -185,8 +191,8 @@ describe('Marketplace slice', () => { findFilteredItems.fulfilled({ items: expectedItems, totalCount: 2 }, '', {} as FilterType) ); - expect(actual.itemPagination.items).toEqual(expectedItems); - expect(actual.itemPagination.totalCount).toEqual(expectedItems.length); + expect(actual.items).toEqual(expectedItems); + expect(actual.itemsCount).toEqual(expectedItems.length); expect(actual.isLoading).toEqual(false); }); }); @@ -223,8 +229,8 @@ describe('Marketplace slice', () => { findPagedItems.fulfilled({ items: expectedItems, totalCount: 2 }, '', {} as FilterType) ); - expect(actual.itemPagination.items).toEqual(expectedItems); - expect(actual.itemPagination.totalCount).toEqual(expectedItems.length); + expect(actual.items).toEqual(expectedItems); + expect(actual.itemsCount).toEqual(expectedItems.length); expect(actual.isPagingLoading).toEqual(false); }); }); diff --git a/src/features/marketplace/store/marketplace.slice.ts b/src/features/marketplace/store/marketplace.slice.ts index 244656f..236a2ef 100644 --- a/src/features/marketplace/store/marketplace.slice.ts +++ b/src/features/marketplace/store/marketplace.slice.ts @@ -1,33 +1,53 @@ import { createSlice } from '@reduxjs/toolkit'; +import ItemStatus from '../../../common/minting/enums/item-status.enum'; import type { RootState } from '../../../store/types'; import { History } from '../../../types/history'; +import { ICollection } from '../../../types/ICollection'; import { Item } from '../../../types/item'; -import { FilterType, ItemPagination } from '../../../types/item-filter-types'; -import ItemStatus from '../../../common/minting/enums/item-status.enum'; +import { FilterType } from '../../../types/item-filter-types'; +import { + findItemsByAccount, + findLikedItemsByAccount, + findUserCollections, + findUserCollectionsWithoutItems, +} from '../../account/store/account.actions'; +import { + findCollectionById, + findFilteredCollectionItems, + findFilteredCollections, + findPagedCollectionItems, + findPagedCollections, + findPagedCollectionsNfts, + findCollectionsByPriceRange, + getNewestCollections, +} from '../../collections/store/collections.actions'; + +import { CollectionFilterType } from '../../collections/types/CollectionsFiltersTypes'; import { + buyItem, changePriceItem, delistItem, + findAllHistory, findFilteredItems, + findHistoryByItemId, findPagedItems, findPriceRange, - findAllHistory, - findHistoryByItemId, - listItem, - buyItem, - likeItem, getNewest, hideItem, + likeItem, + listItem, } from './marketplace.actions'; export type MarketplaceState = { items: Item[]; - marketplaceFilters: FilterType; - itemPagination: ItemPagination; + likedItems: Item[]; + collections: ICollection[]; + collectionsCount: number; + collectionsWithoutItems: ICollection[]; + itemsCount: number; + filters: FilterType; + collectionsFilters: CollectionFilterType; newestItems: Item[]; - isLoadingNewest: boolean; - isLoading: boolean; - isPagingLoading: boolean; - isLoadingHistory: boolean; selectedItem: Item; history: { data: History[]; @@ -35,6 +55,16 @@ export type MarketplaceState = { limit: number; page: number; }; + selectedCollection: ICollection; + newestCollections: ICollection[]; + isLoadingCollections: boolean; + isLoadingNewest: boolean; + isLoading: boolean; + isPagingLoading: boolean; + isLoadingHistory: boolean; + isDelisting: boolean; + isLoadingCollection: boolean; + isListing: boolean; isModalOpen: boolean; isCompleted: boolean; isOpenPreviewProductModal: boolean; @@ -51,11 +81,14 @@ export const initialFormState = { const initialState: MarketplaceState = { items: [], - isLoading: false, - isPagingLoading: false, - isLoadingHistory: false, - itemPagination: { items: [], totalCount: 0 }, - marketplaceFilters: { + itemsCount: 0, + likedItems: [], + collections: [], + collectionsCount: 0, + collectionsWithoutItems: [], + selectedItem: {} as Item, + newestItems: [], + filters: { likesDirection: '', search: '', priceRange: { @@ -65,17 +98,32 @@ const initialState: MarketplaceState = { cheapest: '', mostExpensive: '', page: 1, - limit: 15, - } as FilterType, - selectedItem: {} as Item, - newestItems: [], - isLoadingNewest: false, + limit: 3, + }, history: { data: [], count: 0, limit: 3, page: 1, }, + newestCollections: [] as ICollection[], + selectedCollection: {} as ICollection, + isLoadingCollections: false, + collectionsFilters: { + search: '', + popularityOrder: '', + creationOrder: '', + mintType: '', + page: 1, + limit: 3, + }, + isLoading: false, + isDelisting: false, + isListing: false, + isPagingLoading: false, + isLoadingHistory: false, + isLoadingNewest: false, + isLoadingCollection: false, isModalOpen: false, isCompleted: false, isOpenPreviewProductModal: false, @@ -86,10 +134,10 @@ const marketplaceSlice = createSlice({ initialState, reducers: { setMarketplaceFilters: (state, { payload }) => { - state.marketplaceFilters = payload; + state.filters = payload; }, resetMarketplaceFilters: (state) => { - state.marketplaceFilters = initialState.marketplaceFilters; + state.filters = initialState.filters; }, setSelectedItem: (state, { payload }) => { state.selectedItem = payload; @@ -100,18 +148,37 @@ const marketplaceSlice = createSlice({ setIsOpenPreviewProductModal: (state, { payload }) => { state.isOpenPreviewProductModal = payload; }, + setCollectionsFilters: (state, { payload }) => { + state.collectionsFilters = payload; + }, + resetCollectionsFilters: (state) => { + state.collectionsFilters = initialState.collectionsFilters; + }, + setCollectionsNftsFilters: (state, { payload }) => { + state.filters = payload; + }, + resetCollectionsNftFilters: (state) => { + state.filters = initialState.filters; + }, + setSelectedCollection: (state, { payload }) => { + state.selectedCollection = payload; + }, }, extraReducers: (builder) => { // List Item builder.addCase(listItem.pending, (state) => { - state.isLoading = true; + state.isListing = true; }); builder.addCase(listItem.fulfilled, (state, { payload: item }) => { - state.isLoading = false; + state.isListing = false; state.isCompleted = true; state.isModalOpen = false; state.selectedItem = item; }); + builder.addCase(listItem.rejected, (state) => { + state.isListing = false; + }); + // getNewestItems builder.addCase(getNewest.pending, (state) => { state.isLoadingNewest = true; }); @@ -122,21 +189,18 @@ const marketplaceSlice = createSlice({ builder.addCase(getNewest.rejected, (state) => { state.isLoadingNewest = false; }); - builder.addCase(listItem.rejected, (state) => { - state.isLoading = false; - }); // Delist Item builder.addCase(delistItem.pending, (state) => { - state.isLoading = true; + state.isDelisting = true; }); builder.addCase(delistItem.fulfilled, (state, { payload: item }) => { - state.isLoading = false; + state.isDelisting = false; state.isCompleted = true; state.isModalOpen = false; state.selectedItem = item; }); builder.addCase(delistItem.rejected, (state) => { - state.isLoading = false; + state.isDelisting = false; }); // Change Price builder.addCase(changePriceItem.pending, (state) => { @@ -165,11 +229,57 @@ const marketplaceSlice = createSlice({ state.isLoading = false; state.isModalOpen = false; }); + builder.addCase(findItemsByAccount.pending, (state) => { + state.isLoading = true; + }); + builder.addCase(findItemsByAccount.fulfilled, (state, { payload }) => { + state.items = payload; + }); + builder.addCase(findLikedItemsByAccount.pending, (state) => { + state.isLoading = true; + }); + builder.addCase(findLikedItemsByAccount.fulfilled, (state, { payload }) => { + state.likedItems = payload; + }); + builder.addCase(findUserCollectionsWithoutItems.pending, (state) => { + state.isLoadingCollection = true; + }); + builder.addCase(findUserCollectionsWithoutItems.fulfilled, (state, { payload }) => { + state.isLoadingCollection = false; + state.collectionsWithoutItems = payload; + }); + builder.addCase(findUserCollectionsWithoutItems.rejected, (state) => { + state.isLoadingCollection = false; + }); + builder.addCase(findUserCollections.pending, (state) => { + state.isLoadingCollection = true; + }); + builder.addCase(findUserCollections.fulfilled, (state, { payload }) => { + state.collections = payload; + state.isLoadingCollection = false; + }); + builder.addCase(findUserCollections.rejected, (state) => { + state.isLoadingCollection = false; + }); + builder.addCase(likeItem.fulfilled, (state, { payload }) => { + const index = state.items.findIndex((i) => i.itemId === payload.itemId); + state.items[index] = payload; + + const likedIndex = state.likedItems.findIndex((i) => i.itemId === payload.itemId); + + if (likedIndex !== -1) state.likedItems.splice(likedIndex, 1); + else state.likedItems.push(payload); + + if (state.selectedItem.itemId === payload.itemId) state.selectedItem = payload; + const indexNewest = state.newestItems.findIndex((i) => i.itemId === payload.itemId); + state.newestItems[indexNewest] = payload; + }); builder.addCase(findFilteredItems.pending, (state) => { state.isLoading = true; }); builder.addCase(findFilteredItems.fulfilled, (state, { payload }) => { - state.itemPagination = payload; + state.items = payload.items; + state.itemsCount = payload.totalCount; state.isLoading = false; }); builder.addCase(findFilteredItems.rejected, (state) => { @@ -179,16 +289,16 @@ const marketplaceSlice = createSlice({ state.isPagingLoading = true; }); builder.addCase(findPagedItems.fulfilled, (state, { payload }) => { - state.itemPagination.items = [...state.itemPagination.items, ...payload.items]; - state.itemPagination.totalCount = payload.totalCount; + state.items = [...state.items, ...payload.items]; + state.itemsCount = payload.totalCount; state.isPagingLoading = false; }); builder.addCase(findPagedItems.rejected, (state) => { state.isPagingLoading = false; }); builder.addCase(findPriceRange.fulfilled, (state, { payload }) => { - state.marketplaceFilters.cheapest = payload.from; - state.marketplaceFilters.mostExpensive = payload.to; + state.filters.cheapest = payload.from; + state.filters.mostExpensive = payload.to; }); builder.addCase(findHistoryByItemId.pending, (state) => { state.isLoadingHistory = true; @@ -210,22 +320,109 @@ const marketplaceSlice = createSlice({ state.history.data = [...state.history.data, ...payload.history]; state.history.count = payload.count; }); - builder.addCase(likeItem.fulfilled, (state, { payload }) => { - const indexPagination = state.itemPagination.items.findIndex( - (i) => i.itemId === payload.itemId - ); - state.itemPagination.items[indexPagination] = payload; - if (state.selectedItem.itemId === payload.itemId) state.selectedItem = payload; - const indexNewest = state.newestItems.findIndex((i) => i.itemId === payload.itemId); - state.newestItems[indexNewest] = payload; - }); builder.addCase(hideItem.fulfilled, (state, { payload }) => { - const index = state.itemPagination.items.findIndex((i) => i.itemId === payload.itemId); - state.itemPagination.items[index] = payload; + const index = state.items.findIndex((i) => i.itemId === payload.itemId); + state.items[index] = payload; if (state.selectedItem.itemId === payload.itemId) state.selectedItem = payload; }); + + // FIND PAGED COLLECTIONS + builder.addCase(findPagedCollectionItems.pending, (state) => { + state.isPagingLoading = true; + }); + builder.addCase(findPagedCollectionItems.fulfilled, (state, { payload }) => { + state.items = [...state.items, ...payload.items]; + state.isPagingLoading = false; + }); + builder.addCase(findPagedCollectionItems.rejected, (state) => { + state.isPagingLoading = false; + }); + // find filtered collection items + builder.addCase(findFilteredCollectionItems.pending, (state) => { + state.isPagingLoading = true; + }); + builder.addCase(findFilteredCollectionItems.fulfilled, (state, { payload }) => { + state.items = payload.items; + state.itemsCount = payload.totalCount; + state.isPagingLoading = false; + }); + builder.addCase(findFilteredCollectionItems.rejected, (state) => { + state.isPagingLoading = false; + }); + // find price range with items inside a collection + builder.addCase(findCollectionsByPriceRange.pending, (state) => { + state.isPagingLoading = true; + }); + builder.addCase(findCollectionsByPriceRange.fulfilled, (state, { payload }) => { + state.filters.cheapest = payload.from; + state.filters.mostExpensive = payload.to; + state.isPagingLoading = false; + }); + builder.addCase(findCollectionsByPriceRange.rejected, (state) => { + state.isPagingLoading = false; + }); + // find nfts from a collections + builder.addCase(findPagedCollectionsNfts.pending, (state) => { + state.isPagingLoading = true; + }); + builder.addCase(findPagedCollectionsNfts.fulfilled, (state, { payload }) => { + state.filters.limit = payload.limit; + state.filters.page = payload.page; + state.itemsCount = payload.totalCount; + state.items = [...state.items, ...payload.items]; + state.isPagingLoading = false; + }); + builder.addCase(findPagedCollectionsNfts.rejected, (state) => { + state.isPagingLoading = false; + }); + // find filtered collections + builder.addCase(findFilteredCollections.pending, (state) => { + state.isLoadingCollections = true; + }); + builder.addCase(findFilteredCollections.fulfilled, (state, { payload }) => { + state.collections = payload.collections; + state.collectionsCount = payload.totalCount; + state.isLoadingCollections = false; + }); + builder.addCase(findFilteredCollections.rejected, (state) => { + state.isLoadingCollections = false; + }); + // find paginated collections + builder.addCase(findPagedCollections.pending, (state) => { + state.isPagingLoading = true; + }); + builder.addCase(findPagedCollections.fulfilled, (state, { payload }) => { + state.collections = [...state.collections, ...payload.collections]; + state.collectionsCount = payload.totalCount; + state.isLoadingCollections = false; + }); + builder.addCase(findPagedCollections.rejected, (state) => { + state.isLoadingCollections = false; + }); + // get latets collections + builder.addCase(getNewestCollections.pending, (state) => { + state.isLoadingCollections = true; + }); + builder.addCase(getNewestCollections.fulfilled, (state, { payload }) => { + state.newestCollections = payload; + state.isLoadingCollections = false; + }); + builder.addCase(getNewestCollections.rejected, (state) => { + state.isLoadingCollections = false; + }); + // get collection by id + builder.addCase(findCollectionById.pending, (state) => { + state.isLoadingCollections = true; + }); + builder.addCase(findCollectionById.fulfilled, (state, { payload }) => { + state.selectedCollection = payload; + state.isLoadingCollections = false; + }); + builder.addCase(findCollectionById.rejected, (state) => { + state.isLoadingCollections = false; + }); }, }); @@ -289,6 +486,11 @@ export const { setSelectedItem, setIsModalOpen, setIsOpenPreviewProductModal, + resetCollectionsFilters, + setCollectionsNftsFilters, + setCollectionsFilters, + resetCollectionsNftFilters, + setSelectedCollection, } = marketplaceSlice.actions; export const marketplaceReducer = marketplaceSlice.reducer; diff --git a/src/layouts/wrapper.tsx b/src/layouts/wrapper.tsx index c04e4be..088a5c6 100644 --- a/src/layouts/wrapper.tsx +++ b/src/layouts/wrapper.tsx @@ -9,7 +9,6 @@ import useAppDispatch from '../store/hooks/useAppDispatch'; import { findLikedItemsByAccount } from '../features/account/store/account.actions'; import useAppSelector from '../store/hooks/useAppSelector'; import { setIsNetworkAdviceOpen } from '../store/ui/ui.slice'; -import { resetSelectedCollectionStats } from '../features/collections/store/collections.slice'; import { NetworkNames } from '../common/enums/network-names.enum'; import useMetamask from '../features/auth/hooks/useMetamask'; import NetworkRequestModal from '../components/modals/network-request-modal/network-request.modal'; @@ -26,7 +25,6 @@ const Wrapper = ({ children }: Props) => { useEffect(() => { if (isAuthenticated) dispatch(findLikedItemsByAccount(address)); - dispatch(resetSelectedCollectionStats()); dispatch(setIsNetworkAdviceOpen(true)); }, [dispatch, isAuthenticated, address]); diff --git a/src/pages/collections/[collectionId].tsx b/src/pages/collections/[collectionId].tsx index 8d5f9e2..6118d6c 100644 --- a/src/pages/collections/[collectionId].tsx +++ b/src/pages/collections/[collectionId].tsx @@ -7,9 +7,8 @@ import { useEffect } from 'react'; import { findFilteredCollectionItems } from '../../features/collections/store/collections.actions'; import { resetCollectionsNftFilters, - resetSelectedCollectionStats, setSelectedCollection, -} from '../../features/collections/store/collections.slice'; +} from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; import { ICollection } from '../../types/ICollection'; @@ -21,27 +20,23 @@ type PropsType = { const CollectionDetailsPage = ({ collection }: PropsType) => { const router = useRouter(); const dispatch = useAppDispatch(); - const { - selectedCollection, - collectionItemsFiltering: { itemsFilters }, - } = useAppSelector((state) => state.collections); + const { selectedCollection, filters } = useAppSelector((state) => state.marketplace); const collectionExist = Object.entries(collection).length; const collectionIsDifferent = collection.id !== selectedCollection.id; useEffect(() => { if (collectionIsDifferent) { - dispatch(resetSelectedCollectionStats()); dispatch(resetCollectionsNftFilters()); dispatch(setSelectedCollection(collection)); } dispatch( findFilteredCollectionItems({ collectionId: collection.id, - filters: itemsFilters, + filters, }) ); - }, [collection, collectionIsDifferent, dispatch, itemsFilters]); + }, [collection, collectionIsDifferent, dispatch, filters]); useEffect(() => { const exitingFunction = () => dispatch(resetCollectionsNftFilters()); diff --git a/src/pages/collections/index.tsx b/src/pages/collections/index.tsx index fb7a759..3c8c0f2 100644 --- a/src/pages/collections/index.tsx +++ b/src/pages/collections/index.tsx @@ -3,13 +3,13 @@ import SEO from '@components/seo'; import CollectionsArea from '@containers/collections/collections.container'; import { useEffect } from 'react'; import { findFilteredCollections } from '../../features/collections/store/collections.actions'; -import { resetCollectionsFilters } from '../../features/collections/store/collections.slice'; +import { resetCollectionsFilters } from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; const CollectionsPage = () => { const dispatch = useAppDispatch(); - const { collectionsFilters } = useAppSelector((state) => state.collections); + const { collectionsFilters } = useAppSelector((state) => state.marketplace); useEffect(() => { dispatch(resetCollectionsFilters()); diff --git a/src/pages/marketplace.tsx b/src/pages/marketplace.tsx index c829a2d..37b87a9 100644 --- a/src/pages/marketplace.tsx +++ b/src/pages/marketplace.tsx @@ -18,17 +18,22 @@ import useAppSelector from '../store/hooks/useAppSelector'; const Marketplace = () => { const dispatch = useAppDispatch(); - const { marketplaceFilters, isLoading, itemPagination } = useAppSelector(selectNFTsMarketplace); + const { + filters: marketplaceFilters, + isLoading, + itemsCount: count, + items, + } = useAppSelector(selectNFTsMarketplace); useEffect(() => { dispatch(resetMarketplaceFilters()); }, [dispatch]); useEffect(() => { - if (itemPagination.totalCount) { + if (count) { dispatch(findPriceRange()); } - }, [dispatch, itemPagination.totalCount]); + }, [dispatch, count]); useEffect(() => { dispatch(findFilteredItems(marketplaceFilters)); @@ -42,12 +47,12 @@ const Marketplace = () => { }, [marketplaceFilters.cheapest, marketplaceFilters.mostExpensive]); const renderedComponent = useMemo(() => { - if (itemPagination.items.length) return ; + if (items.length) return ; if (!isLoading) return ; return null; - }, [itemPagination.items.length, isLoading]); + }, [items.length, isLoading]); return ( <> diff --git a/src/types/ICollection.ts b/src/types/ICollection.ts index a0ab45f..cdc72bc 100644 --- a/src/types/ICollection.ts +++ b/src/types/ICollection.ts @@ -13,11 +13,3 @@ export interface ICollection { createdAt: Date; updatedAt: Date; } - -export interface ICollectionWithoutItems { - id: string; - description: string; - name: string; - createdAt: Date; - updatedAt: Date; -} diff --git a/src/types/item-filter-types.ts b/src/types/item-filter-types.ts index 4227a38..fe86d40 100644 --- a/src/types/item-filter-types.ts +++ b/src/types/item-filter-types.ts @@ -1,5 +1,3 @@ -import { Item } from './item'; - export type PriceRangeType = { from: number | string; to: number | string; @@ -38,8 +36,3 @@ export type LikesHandleType = { text: string; direction: string; }; - -export type ItemPagination = { - items: Item[]; - totalCount: number; -}; From 929cb3d4976fa25478bdf923b6ad25a10739e3a8 Mon Sep 17 00:00:00 2001 From: AlefrankM Date: Tue, 10 Jan 2023 22:48:44 -0400 Subject: [PATCH 3/5] refactor: more clean components --- jest.config.js | 12 +++ .../common/activate-item-command.spec.ts | 8 ++ .../commands/common/activate-item-command.ts | 2 +- .../commands/common/mint-nft-command.spec.ts | 4 +- .../common/store-draft-item-command.ts | 7 +- .../common/store-ipfs-object-command.ts | 7 +- .../commands/lazy/generate-voucher-command.ts | 2 +- .../commands/lazy/get-voucher-command.ts | 2 +- .../commands/lazy/store-voucher-command.ts | 2 +- .../minting/commands/lazy/transfer-command.ts | 2 +- src/components/auth/withAuth.tsx | 2 +- .../collection-introduction.component.tsx | 2 +- ...ems.component.tsx => collection-items.tsx} | 6 +- .../collections-filter.component.tsx | 2 +- .../collections/collections-rendered.tsx | 2 +- .../items-collection-filter.component.tsx | 10 +- .../create-page/nft-collection.component.tsx | 4 +- .../create-page/nft-properties.component.tsx | 4 +- .../create-page/nft-tags.component.tsx | 2 +- .../item-details/bid-tab/bid-tab.tsx | 2 +- .../bid-tab/delisting-tab-content.tsx | 4 +- .../bid-tab/history-tab-content.tsx | 4 +- .../bid-tab/listing-tab-content.tsx | 4 +- .../{buy-nft-component.tsx => buy-nft.tsx} | 19 +--- .../item-details/hide-item-button.tsx | 4 +- src/components/item-details/title.tsx | 4 +- src/components/item-filter/index.tsx | 23 ++--- src/components/item/index.tsx | 4 +- .../items-stats/items-stats.components.tsx | 4 +- src/components/modals/buy-modal/buy-modal.tsx | 4 +- .../network-request.modal.tsx | 6 +- .../PreviewProductModal.tsx | 4 +- src/components/modals/share-modal/index.tsx | 2 +- src/components/product-bid/index.tsx | 2 +- .../signature-area/SignatureArea.tsx | 6 +- src/components/user-dropdown/index.tsx | 2 +- src/containers/activity/activity-area.tsx | 28 +++--- .../author-intro/AuthorIntroArea.tsx | 2 +- .../author-profile/AuthorProfileArea.tsx | 2 +- .../collection-details.container.tsx | 4 +- ...ems.container.tsx => collection-items.tsx} | 12 +-- .../collections/collections.container.tsx | 2 +- src/containers/collections/index.tsx | 4 +- .../newest-collections.container.tsx | 4 +- src/containers/connect/index.tsx | 2 +- src/containers/create-new/index.tsx | 4 +- src/containers/hero/hero.tsx | 2 +- src/containers/item-details/item-details.tsx | 8 +- .../marketplace/MarketplaceArea.tsx | 4 +- src/features/account/store/account.slice.ts | 2 +- src/features/auth/hooks/useMetamask.ts | 16 ++-- src/features/auth/store/auth.actions.ts | 28 +++--- src/features/auth/store/auth.slice.spec.ts | 20 ++-- src/features/auth/store/auth.slice.ts | 11 ++- .../collections/store/collections.actions.ts | 15 +-- .../collections/store/collections.slice.ts | 21 ----- .../leda-nft/store/leda-nft.actions.ts | 2 +- src/features/leda-nft/store/leda-nft.slice.ts | 2 +- .../commands/buy-item/buy-item-command.ts | 2 +- .../buy-item/store-buy-item-command.ts | 2 +- .../change-price-item-command.spec.ts | 2 +- .../change-price-item-command.ts | 2 +- .../store-change-price-item-command.ts | 2 +- .../generate-jup-voucher-command.ts | 2 +- .../common-lazy/generate-voucher-command.ts | 2 +- .../common-lazy/store-voucher-command.ts | 2 +- .../change-status-item-command.spec.ts | 2 +- .../delist-item/store-delist-item-command.ts | 2 +- .../commands/list-item/approve-command.ts | 2 +- .../list-item/list-item-command.spec.ts | 2 +- .../commands/list-item/list-item-command.ts | 2 +- .../list-item/store-list-item-command.ts | 2 +- .../marketplace/store/marketplace.actions.ts | 93 +++++++++---------- .../store/marketplace.slice.spec.ts | 34 ++++--- .../marketplace/store/marketplace.slice.ts | 54 ++++------- src/layouts/header/NetworkNotice.tsx | 8 +- src/layouts/header/index.tsx | 2 +- src/layouts/wrapper.tsx | 8 +- src/pages/_app.tsx | 1 + src/pages/_document.tsx | 2 + src/pages/author.tsx | 3 +- src/pages/collections/[collectionId].tsx | 10 +- src/pages/collections/index.tsx | 6 +- src/pages/index.tsx | 4 +- src/pages/item/[itemId].tsx | 2 +- src/pages/marketplace.tsx | 13 +-- src/store/error/error-handler.ts | 3 + src/store/index.ts | 3 +- src/types/item-slider.types.ts | 2 + src/utils/{methods.js => methods.ts} | 58 +++++------- tsconfig.json | 1 + 91 files changed, 335 insertions(+), 375 deletions(-) rename src/components/collections/{collection-items.component.tsx => collection-items.tsx} (96%) rename src/components/item-details/{buy-nft-component.tsx => buy-nft.tsx} (65%) rename src/containers/collection-details/{collection-items.container.tsx => collection-items.tsx} (73%) delete mode 100644 src/features/collections/store/collections.slice.ts rename src/utils/{methods.js => methods.ts} (77%) diff --git a/jest.config.js b/jest.config.js index 584655d..3bc8cc1 100644 --- a/jest.config.js +++ b/jest.config.js @@ -13,6 +13,18 @@ const customJestConfig = { // setupFilesAfterEnv: ['/jest.setup.js'], // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work moduleDirectories: ['node_modules', '/'], + moduleNameMapper: { + '@store/*': ['/src/store'], + '@assets/*': ['/assets/'], + '@ui/*': ['/components/ui/'], + '@components/*': ['/components/'], + '@widgets/*': ['/components/widgets/'], + '@containers/*': ['/containers/'], + '@layout/*': ['/layouts/'], + '@utils/*': ['/utils/'], + '@hooks': ['/hooks'], + '@types': ['/types'], + }, testEnvironment: 'jest-environment-jsdom', preset: 'ts-jest', testEnvironment: 'node', diff --git a/src/common/minting/commands/common/activate-item-command.spec.ts b/src/common/minting/commands/common/activate-item-command.spec.ts index 5852f8a..2745fbb 100644 --- a/src/common/minting/commands/common/activate-item-command.spec.ts +++ b/src/common/minting/commands/common/activate-item-command.spec.ts @@ -132,6 +132,14 @@ describe('ActivateItemCommand', () => { cid: '123', imageUrl: 'url', item: { itemId: '123' } as Item, + collection: { + name: 'ab', + description: 'ab', + image: { + url: 'url', + cid: 'cid', + }, + }, tokenId: 123, } as MintState; diff --git a/src/common/minting/commands/common/activate-item-command.ts b/src/common/minting/commands/common/activate-item-command.ts index d59803a..137b17c 100644 --- a/src/common/minting/commands/common/activate-item-command.ts +++ b/src/common/minting/commands/common/activate-item-command.ts @@ -1,9 +1,9 @@ +import { rejectWithHttp } from '../../../../store/error/error-handler'; import ActivateItemRequest from '../../../types/activate-item-request'; import ICommand from '../../interfaces/command.interface'; import MintError from '../../enums/mint-error.enum'; import MintState from '../../types/mint-state'; import IItemService from '../../../../features/leda-nft/interfaces/item-service.interface'; -import { rejectWithHttp } from '../../../../store/error/error-handler'; export default class ActivateItemCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/common/minting/commands/common/mint-nft-command.spec.ts b/src/common/minting/commands/common/mint-nft-command.spec.ts index 283caa1..c18dfc7 100644 --- a/src/common/minting/commands/common/mint-nft-command.spec.ts +++ b/src/common/minting/commands/common/mint-nft-command.spec.ts @@ -1,9 +1,9 @@ import { ContractReceipt, Event } from 'ethers'; +import * as ErrorHandler from '../../../../store/error/error-handler'; import MintError from '../../enums/mint-error.enum'; import ICommand from '../../interfaces/command.interface'; import MintState from '../../types/mint-state'; import MintNftCommand from './mint-nft-command'; -import * as errorHandler from '../../../../store/error/error-handler'; const nftServiceMock = { init: jest.fn(), @@ -213,7 +213,7 @@ describe('MintNftCommand', () => { const expected = { ...state, error: MintError.MintNftFailure }; - jest.spyOn(errorHandler, 'rejectWithMetamask').mockResolvedValue(expected); + jest.spyOn(ErrorHandler, 'rejectWithMetamask').mockResolvedValue(expected); nftServiceMock.mint.mockRejectedValue('something went wrong.'); diff --git a/src/common/minting/commands/common/store-draft-item-command.ts b/src/common/minting/commands/common/store-draft-item-command.ts index 3211625..3824c7a 100644 --- a/src/common/minting/commands/common/store-draft-item-command.ts +++ b/src/common/minting/commands/common/store-draft-item-command.ts @@ -1,9 +1,9 @@ +import * as ErrorHandler from '../../../../store/error/error-handler'; import ICommand from '../../interfaces/command.interface'; import MintError from '../../enums/mint-error.enum'; import MintState from '../../types/mint-state'; import IItemService from '../../../../features/leda-nft/interfaces/item-service.interface'; import DraftItemRequest from '../../../types/draft-item-request'; -import { rejectWithHttp } from '../../../../store/error/error-handler'; export default class StoreDraftItemCommand implements ICommand { private readonly itemService: IItemService; @@ -36,7 +36,10 @@ export default class StoreDraftItemCommand implements ICommand { state.item = await this.itemService.create(item); } catch (ex) { - return rejectWithHttp(ex, () => ({ ...state, error: MintError.StoreDraftItemFailure })); + return ErrorHandler.rejectWithHttp(ex, () => ({ + ...state, + error: MintError.StoreDraftItemFailure, + })); } return state; diff --git a/src/common/minting/commands/common/store-ipfs-object-command.ts b/src/common/minting/commands/common/store-ipfs-object-command.ts index 35d7be4..2c581c4 100644 --- a/src/common/minting/commands/common/store-ipfs-object-command.ts +++ b/src/common/minting/commands/common/store-ipfs-object-command.ts @@ -1,8 +1,8 @@ +import { rejectWithHttp } from '../../../../store/error/error-handler'; import ICommand from '../../interfaces/command.interface'; import MintError from '../../enums/mint-error.enum'; import MintState from '../../types/mint-state'; import IImageService from '../../../../features/leda-nft/interfaces/image-service.interface'; -import { rejectWithHttp } from '../../../../store/error/error-handler'; export default class StoreIpfsObjectCommand implements ICommand { private readonly imageService: IImageService; @@ -35,7 +35,10 @@ export default class StoreIpfsObjectCommand implements ICommand { state.collection.image.cid = cidResponse; } } catch (ex) { - return rejectWithHttp(ex, () => ({ ...state, error: MintError.IpfsStoreFailure })); + return rejectWithHttp(ex, () => ({ + ...state, + error: MintError.IpfsStoreFailure, + })); } return state; diff --git a/src/common/minting/commands/lazy/generate-voucher-command.ts b/src/common/minting/commands/lazy/generate-voucher-command.ts index 572773d..3335aa5 100644 --- a/src/common/minting/commands/lazy/generate-voucher-command.ts +++ b/src/common/minting/commands/lazy/generate-voucher-command.ts @@ -1,9 +1,9 @@ import { ethers } from 'ethers'; +import { rejectWithMetamask } from '../../../../store/error/error-handler'; import ICommand from '../../interfaces/command.interface'; import MintError from '../../enums/mint-error.enum'; import MintState from '../../types/mint-state'; import ILazyMintService from '../../../../features/leda-nft/interfaces/lazy-mint-service.interface'; -import { rejectWithMetamask } from '../../../../store/error/error-handler'; import IImageService from '../../../../features/leda-nft/interfaces/image-service.interface'; export default class GenerateVoucherCommand implements ICommand { diff --git a/src/common/minting/commands/lazy/get-voucher-command.ts b/src/common/minting/commands/lazy/get-voucher-command.ts index a0943aa..7054f28 100644 --- a/src/common/minting/commands/lazy/get-voucher-command.ts +++ b/src/common/minting/commands/lazy/get-voucher-command.ts @@ -1,5 +1,5 @@ -import IItemService from '../../../../features/leda-nft/interfaces/item-service.interface'; import { rejectWithHttp } from '../../../../store/error/error-handler'; +import IItemService from '../../../../features/leda-nft/interfaces/item-service.interface'; import MintError from '../../enums/mint-error.enum'; import ICommand from '../../interfaces/command.interface'; import MintState from '../../types/mint-state'; diff --git a/src/common/minting/commands/lazy/store-voucher-command.ts b/src/common/minting/commands/lazy/store-voucher-command.ts index 4ee3d95..6cb48cb 100644 --- a/src/common/minting/commands/lazy/store-voucher-command.ts +++ b/src/common/minting/commands/lazy/store-voucher-command.ts @@ -1,9 +1,9 @@ +import { rejectWithHttp } from '../../../../store/error/error-handler'; import ICommand from '../../interfaces/command.interface'; import MintError from '../../enums/mint-error.enum'; import MintState from '../../types/mint-state'; import IItemService from '../../../../features/leda-nft/interfaces/item-service.interface'; import ProcessLazyItemRequest from '../../../types/process-lazy-item-request'; -import { rejectWithHttp } from '../../../../store/error/error-handler'; export default class StoreVoucherCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/common/minting/commands/lazy/transfer-command.ts b/src/common/minting/commands/lazy/transfer-command.ts index ad76a11..c51594b 100644 --- a/src/common/minting/commands/lazy/transfer-command.ts +++ b/src/common/minting/commands/lazy/transfer-command.ts @@ -1,8 +1,8 @@ +import { rejectWithHttp } from '../../../../store/error/error-handler'; import IItemService from '../../../../features/leda-nft/interfaces/item-service.interface'; import MintError from '../../enums/mint-error.enum'; import ICommand from '../../interfaces/command.interface'; import MintState from '../../types/mint-state'; -import { rejectWithHttp } from '../../../../store/error/error-handler'; export default class TransferCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/components/auth/withAuth.tsx b/src/components/auth/withAuth.tsx index 849e77a..f6c90e7 100644 --- a/src/components/auth/withAuth.tsx +++ b/src/components/auth/withAuth.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from 'react'; import { useRouter } from 'next/router'; -import { selectAuthState } from '../../features/auth/store/auth.slice'; import useAppSelector from '../../store/hooks/useAppSelector'; +import { selectAuthState } from '../../features/auth/store/auth.slice'; const withAuth = (WrappedConmponent: React.FunctionComponent) => { const Component = (props: object) => { diff --git a/src/components/collections/collection-introduction.component.tsx b/src/components/collections/collection-introduction.component.tsx index b406b5c..0361221 100644 --- a/src/components/collections/collection-introduction.component.tsx +++ b/src/components/collections/collection-introduction.component.tsx @@ -1,8 +1,8 @@ import Image from 'next/image'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import Tooltip from 'react-bootstrap/Tooltip'; -import appConfig from '../../common/configuration/app.config'; import useAppSelector from '../../store/hooks/useAppSelector'; +import appConfig from '../../common/configuration/app.config'; import { formattedAddress } from '../../utils/getFormattedAddress'; const CollectionIntroductionComponent = () => { diff --git a/src/components/collections/collection-items.component.tsx b/src/components/collections/collection-items.tsx similarity index 96% rename from src/components/collections/collection-items.component.tsx rename to src/components/collections/collection-items.tsx index 191aa6c..156f833 100644 --- a/src/components/collections/collection-items.component.tsx +++ b/src/components/collections/collection-items.tsx @@ -1,12 +1,12 @@ import InfiniteScroll from '@components/common/InfiniteScroll'; import Item from '@components/item'; import { useCallback } from 'react'; -import { findPagedCollectionItems } from '../../features/collections/store/collections.actions'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; +import { findPagedCollectionItems } from '../../features/collections/store/collections.actions'; import { Item as ItemType } from '../../types/item'; -const CollectionProductsComponent = () => { +export const CollectionItemsArea = () => { const dispatch = useAppDispatch(); const { items, itemsCount, selectedCollection, isPagingLoading, filters } = useAppSelector( (state) => state.marketplace @@ -61,5 +61,3 @@ const CollectionProductsComponent = () => {
    ); }; - -export default CollectionProductsComponent; diff --git a/src/components/collections/collections-filter.component.tsx b/src/components/collections/collections-filter.component.tsx index 3ee5be3..01e4aab 100644 --- a/src/components/collections/collections-filter.component.tsx +++ b/src/components/collections/collections-filter.component.tsx @@ -1,9 +1,9 @@ import NiceSelect from '@ui/nice-select'; import clsx from 'clsx'; import { useState } from 'react'; -import { setCollectionsFilters } from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; +import { setCollectionsFilters } from '../../features/marketplace/store/marketplace.slice'; const CollectionsFilter = () => { const dispatch = useAppDispatch(); diff --git a/src/components/collections/collections-rendered.tsx b/src/components/collections/collections-rendered.tsx index 7993f10..e499e97 100644 --- a/src/components/collections/collections-rendered.tsx +++ b/src/components/collections/collections-rendered.tsx @@ -1,8 +1,8 @@ import InfiniteScroll from '@components/common/InfiniteScroll'; import { useCallback } from 'react'; -import { findPagedCollections } from '../../features/collections/store/collections.actions'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; +import { findPagedCollections } from '../../features/collections/store/collections.actions'; import { ICollection } from '../../types/ICollection'; import CollectionComponent from './collection.component'; diff --git a/src/components/collections/items-collection-filter.component.tsx b/src/components/collections/items-collection-filter.component.tsx index 8a84714..647c65b 100644 --- a/src/components/collections/items-collection-filter.component.tsx +++ b/src/components/collections/items-collection-filter.component.tsx @@ -5,11 +5,11 @@ import Sticky from '@ui/sticky'; import { useEffect, useMemo, useState } from 'react'; import { Range } from 'react-range'; import { IRenderTrackParams } from 'react-range/lib/types'; -import { findCollectionsByPriceRange } from '../../features/collections/store/collections.actions'; -import { setCollectionsNftsFilters } from '../../features/marketplace/store/marketplace.slice'; import useAppDispatch from '../../store/hooks/useAppDispatch'; import useAppSelector from '../../store/hooks/useAppSelector'; import { selectUiReducer } from '../../store/ui/ui.slice'; +import { setFilters } from '../../features/marketplace/store/marketplace.slice'; +import { findCollectionsByPriceRange } from '../../features/collections/store/collections.actions'; type PriceRange = { cheapest: number; @@ -70,7 +70,7 @@ const ItemCollectionFilter = () => { }, [cheapest, mostExpensive]); const handleLikesChange = (likesDirection: string) => { - dispatch(setCollectionsNftsFilters({ ...filters, likesDirection })); + dispatch(setFilters({ ...filters, likesDirection })); }; const renderTrack = (props: IRenderTrackParams) => ( @@ -83,7 +83,7 @@ const ItemCollectionFilter = () => { const handleSearch = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - dispatch(setCollectionsNftsFilters({ ...filters, search: localSearch })); + dispatch(setFilters({ ...filters, search: localSearch })); } }; @@ -93,7 +93,7 @@ const ItemCollectionFilter = () => { const handlePriceRangeFinalChange = ([from, to]: number[]) => { dispatch( - setCollectionsNftsFilters({ + setFilters({ ...filters, priceRange: { from, to }, }) diff --git a/src/components/create-page/nft-collection.component.tsx b/src/components/create-page/nft-collection.component.tsx index 208e0b3..ad1752b 100644 --- a/src/components/create-page/nft-collection.component.tsx +++ b/src/components/create-page/nft-collection.component.tsx @@ -2,10 +2,10 @@ import clsx from 'clsx'; import { useEffect, useRef, useState } from 'react'; import Modal from 'react-bootstrap/Modal'; import { AiOutlinePlus } from 'react-icons/ai'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { findUserCollectionsWithoutItems } from '../../features/account/store/account.actions'; import useMetamask from '../../features/auth/hooks/useMetamask'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { CollectionCreateType } from '../../types/collection-type'; const collectionsErrors = { diff --git a/src/components/create-page/nft-properties.component.tsx b/src/components/create-page/nft-properties.component.tsx index b35a6e4..b5bea47 100644 --- a/src/components/create-page/nft-properties.component.tsx +++ b/src/components/create-page/nft-properties.component.tsx @@ -1,11 +1,11 @@ import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import { useEffect, useRef, useState } from 'react'; import Modal from 'react-bootstrap/Modal'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { ItemProperty } from '../../common/types/ipfs-types'; import { selectNftState } from '../../features/leda-nft/store/leda-nft.slice'; import { selectMarketplaceState } from '../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; const propertiesModalMessages = { NotRepeteadAllowed: 'You can not enter items with same key', diff --git a/src/components/create-page/nft-tags.component.tsx b/src/components/create-page/nft-tags.component.tsx index ea1b376..355fc50 100644 --- a/src/components/create-page/nft-tags.component.tsx +++ b/src/components/create-page/nft-tags.component.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import TagsInput from 'react-tagsinput'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; +import useAppDispatch from '@store/hooks/useAppDispatch'; const tagsErrorMessages = { CantMore: 'You can not enter more than 8 tags', diff --git a/src/components/item-details/bid-tab/bid-tab.tsx b/src/components/item-details/bid-tab/bid-tab.tsx index d51d47c..8c3d5e5 100644 --- a/src/components/item-details/bid-tab/bid-tab.tsx +++ b/src/components/item-details/bid-tab/bid-tab.tsx @@ -4,12 +4,12 @@ import Nav from 'react-bootstrap/Nav'; import TabContainer from 'react-bootstrap/TabContainer'; import TabContent from 'react-bootstrap/TabContent'; import TabPane from 'react-bootstrap/TabPane'; +import useAppSelector from '@store/hooks/useAppSelector'; import { TabsDetails } from '../../../common/enums/nft-details-tabs.enum'; import { selectCanIDelist, selectCanIList, } from '../../../features/marketplace/store/marketplace.slice'; -import useAppSelector from '../../../store/hooks/useAppSelector'; import { DelistingTabContent } from './delisting-tab-content'; import DetailsTabContent from './details-tab-content'; import { HistoryTabContent } from './history-tab-content'; diff --git a/src/components/item-details/bid-tab/delisting-tab-content.tsx b/src/components/item-details/bid-tab/delisting-tab-content.tsx index f321c0c..a34fa49 100644 --- a/src/components/item-details/bid-tab/delisting-tab-content.tsx +++ b/src/components/item-details/bid-tab/delisting-tab-content.tsx @@ -1,10 +1,10 @@ import Modal from 'react-bootstrap/Modal'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import useMetamask from '../../../features/auth/hooks/useMetamask'; import { withAuthProtection } from '../../../features/auth/store/auth.actions'; import { delistItem } from '../../../features/marketplace/store/marketplace.actions'; import { setIsModalOpen } from '../../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; -import useAppSelector from '../../../store/hooks/useAppSelector'; import ActionLoaderComponent from '../../action-loader/action-loader.component'; export const DelistingTabContent = () => { diff --git a/src/components/item-details/bid-tab/history-tab-content.tsx b/src/components/item-details/bid-tab/history-tab-content.tsx index 92b941f..1fe814e 100644 --- a/src/components/item-details/bid-tab/history-tab-content.tsx +++ b/src/components/item-details/bid-tab/history-tab-content.tsx @@ -6,10 +6,10 @@ import { useEffect } from 'react'; import ClipLoader from 'react-spinners/ClipLoader'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { TransactionType } from '../../../common/enums/transaction-types.enum'; import { findHistoryByItemId } from '../../../features/marketplace/store/marketplace.actions'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; -import useAppSelector from '../../../store/hooks/useAppSelector'; import appConfig from '../../../common/configuration/app.config'; dayjs.extend(utc); diff --git a/src/components/item-details/bid-tab/listing-tab-content.tsx b/src/components/item-details/bid-tab/listing-tab-content.tsx index 157f532..5ceb257 100644 --- a/src/components/item-details/bid-tab/listing-tab-content.tsx +++ b/src/components/item-details/bid-tab/listing-tab-content.tsx @@ -2,13 +2,13 @@ import ErrorText from '@ui/error-text'; import { useState } from 'react'; import Modal from 'react-bootstrap/Modal'; import { useForm } from 'react-hook-form'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { TransactionType } from '../../../common/enums/transaction-types.enum'; import useMetamask from '../../../features/auth/hooks/useMetamask'; import { withAuthProtection } from '../../../features/auth/store/auth.actions'; import { changePriceItem, listItem } from '../../../features/marketplace/store/marketplace.actions'; import { setIsModalOpen } from '../../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; -import useAppSelector from '../../../store/hooks/useAppSelector'; import { decimalCount } from '../../../utils/getDecimalsCount'; import ActionLoaderComponent from '../../action-loader/action-loader.component'; diff --git a/src/components/item-details/buy-nft-component.tsx b/src/components/item-details/buy-nft.tsx similarity index 65% rename from src/components/item-details/buy-nft-component.tsx rename to src/components/item-details/buy-nft.tsx index 8262d7c..2f53354 100644 --- a/src/components/item-details/buy-nft-component.tsx +++ b/src/components/item-details/buy-nft.tsx @@ -1,20 +1,11 @@ import ActionLoaderComponent from '@components/action-loader/action-loader.component'; import { BuyModal } from '@components/modals/buy-modal/buy-modal'; -import { HighestBid } from '@types'; -import clsx from 'clsx'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { withAuthProtection } from '../../features/auth/store/auth.actions'; import { setIsModalOpen } from '../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; -type Props = { - highestBid?: HighestBid; - actionDate?: string; - btnColor?: 'primary' | 'primary-alta'; - className?: string; -}; - -const BuyNftComponent = ({ highestBid, actionDate, btnColor, className }: Props) => { +export const BuyNft = () => { const dispatch = useAppDispatch(); const { isModalOpen } = useAppSelector((state) => state.marketplace); const { isLoading } = useAppSelector((state) => state.ledaNft); @@ -25,7 +16,7 @@ const BuyNftComponent = ({ highestBid, actionDate, btnColor, className }: Props) return ( <> -
    +
    ); }; - -export default BuyNftComponent; diff --git a/src/components/item-details/hide-item-button.tsx b/src/components/item-details/hide-item-button.tsx index 0194f8f..f6c098e 100644 --- a/src/components/item-details/hide-item-button.tsx +++ b/src/components/item-details/hide-item-button.tsx @@ -2,10 +2,10 @@ import ActionLoaderComponent from '@components/action-loader/action-loader.compo import clsx from 'clsx'; import { useState } from 'react'; import Modal from 'react-bootstrap/Modal'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { withAuthProtection } from '../../features/auth/store/auth.actions'; import { hideItem } from '../../features/marketplace/store/marketplace.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; export const HideItemButton = () => { const dispatch = useAppDispatch(); diff --git a/src/components/item-details/title.tsx b/src/components/item-details/title.tsx index c6cb449..f7b9d3d 100644 --- a/src/components/item-details/title.tsx +++ b/src/components/item-details/title.tsx @@ -1,11 +1,11 @@ import clsx from 'clsx'; import Link from 'next/link'; import { useMemo } from 'react'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { withAuthProtection } from '../../features/auth/store/auth.actions'; import { likeItem } from '../../features/marketplace/store/marketplace.actions'; import { selectIsOwner } from '../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import ShareDropdown from '../share-dropdown'; import { HideItemButton } from './hide-item-button'; diff --git a/src/components/item-filter/index.tsx b/src/components/item-filter/index.tsx index 9973a6e..3bf33c3 100644 --- a/src/components/item-filter/index.tsx +++ b/src/components/item-filter/index.tsx @@ -1,16 +1,13 @@ -import { IRenderTrackParams } from 'react-range/lib/types'; -import { Range } from 'react-range'; -import clsx from 'clsx'; import NiceSelect from '@ui/nice-select'; +import clsx from 'clsx'; import React, { useEffect, useState } from 'react'; +import { Range } from 'react-range'; +import { IRenderTrackParams } from 'react-range/lib/types'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; +import { setFilters } from '../../features/marketplace/store/marketplace.slice'; import SliderThumb from '../ui/input-range/slider-thumb'; import SliderTrack from '../ui/input-range/slider-track'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; -import { - selectNFTsMarketplace, - setMarketplaceFilters, -} from '../../features/marketplace/store/marketplace.slice'; type Props = { cheapest: number; @@ -23,7 +20,7 @@ const STEP_PRECISION = 3; const ItemFilter = ({ cheapest, mostExpensive }: Props) => { const dispatch = useAppDispatch(); - const { filters: marketplaceFilters } = useAppSelector(selectNFTsMarketplace); + const { filters: marketplaceFilters } = useAppSelector((state) => state.marketplace); const [isOpen, setIsOpen] = useState(false); const [valuesRange, setValuesRange] = useState([] as number[]); const [step, setStep] = useState(DEFAULT_STEP); @@ -54,7 +51,7 @@ const ItemFilter = ({ cheapest, mostExpensive }: Props) => { }; const handleLikesChange = (order: string) => { - dispatch(setMarketplaceFilters({ ...marketplaceFilters, likesDirection: order })); + dispatch(setFilters({ ...marketplaceFilters, likesDirection: order })); }; const handleSearchChange = (e: React.ChangeEvent) => { @@ -63,7 +60,7 @@ const ItemFilter = ({ cheapest, mostExpensive }: Props) => { const handleSearch = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - dispatch(setMarketplaceFilters({ ...marketplaceFilters, search: localSearch })); + dispatch(setFilters({ ...marketplaceFilters, search: localSearch })); } }; @@ -74,7 +71,7 @@ const ItemFilter = ({ cheapest, mostExpensive }: Props) => { const handlePriceRangeFinalChange = (vals: number[]) => { setValuesRange(vals); dispatch( - setMarketplaceFilters({ + setFilters({ ...marketplaceFilters, priceRange: { from: valuesRange[0], to: valuesRange[1] }, }) diff --git a/src/components/item/index.tsx b/src/components/item/index.tsx index ee02d8c..3d92cf8 100644 --- a/src/components/item/index.tsx +++ b/src/components/item/index.tsx @@ -9,11 +9,11 @@ import CountdownTimer from '@ui/countdown/count-down-timer'; import { getFormattedName } from '@utils/getFormattedName'; import clsx from 'clsx'; import { useMemo } from 'react'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import appConfig from '../../common/configuration/app.config'; import { selectAuthState } from '../../features/auth/store/auth.slice'; import { setIsModalOpen } from '../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; type Props = { overlay?: boolean; diff --git a/src/components/items-stats/items-stats.components.tsx b/src/components/items-stats/items-stats.components.tsx index d4f351f..7ca2527 100644 --- a/src/components/items-stats/items-stats.components.tsx +++ b/src/components/items-stats/items-stats.components.tsx @@ -7,12 +7,12 @@ import Image from 'next/image'; import { useCallback, useEffect, useMemo } from 'react'; import { FaEthereum, FaRegHeart } from 'react-icons/fa'; import { IoMdHeart } from 'react-icons/io'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import appConfig from '../../common/configuration/app.config'; import { withAuthProtection } from '../../features/auth/store/auth.actions'; import { findPagedCollectionsNfts } from '../../features/collections/store/collections.actions'; import { likeItem } from '../../features/marketplace/store/marketplace.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { Item } from '../../types/item'; const LikeRender = ({ likes, itemId }: { likes: number; itemId: string }) => { diff --git a/src/components/modals/buy-modal/buy-modal.tsx b/src/components/modals/buy-modal/buy-modal.tsx index aa8e749..3507de4 100644 --- a/src/components/modals/buy-modal/buy-modal.tsx +++ b/src/components/modals/buy-modal/buy-modal.tsx @@ -1,12 +1,12 @@ import ActionLoaderComponent from '@components/action-loader/action-loader.component'; import Modal from 'react-bootstrap/Modal'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import useMetamask from '../../../features/auth/hooks/useMetamask'; import { withAuthProtection } from '../../../features/auth/store/auth.actions'; import { redeemVoucher } from '../../../features/leda-nft/store/leda-nft.actions'; import { buyItem } from '../../../features/marketplace/store/marketplace.actions'; import { selectIsLoadingWhileBuy } from '../../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; -import useAppSelector from '../../../store/hooks/useAppSelector'; type Props = { handleModal: () => void; diff --git a/src/components/modals/network-request-modal/network-request.modal.tsx b/src/components/modals/network-request-modal/network-request.modal.tsx index 7603e06..b86329b 100644 --- a/src/components/modals/network-request-modal/network-request.modal.tsx +++ b/src/components/modals/network-request-modal/network-request.modal.tsx @@ -1,8 +1,8 @@ import Button from '@ui/button'; import Modal from 'react-bootstrap/Modal'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; -import useAppSelector from '../../../store/hooks/useAppSelector'; -import { selectUiReducer, setIsMainnetModalOpen } from '../../../store/ui/ui.slice'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; +import { selectUiReducer, setIsMainnetModalOpen } from '@store/ui/ui.slice'; const NetworkRequestModal = () => { const dispatch = useAppDispatch(); diff --git a/src/components/modals/preview-product-modal/PreviewProductModal.tsx b/src/components/modals/preview-product-modal/PreviewProductModal.tsx index f38ebc6..c5393f2 100644 --- a/src/components/modals/preview-product-modal/PreviewProductModal.tsx +++ b/src/components/modals/preview-product-modal/PreviewProductModal.tsx @@ -2,9 +2,9 @@ import { ItemRequest } from '@types'; import { getFormattedName } from '@utils/getFormattedName'; import clsx from 'clsx'; import Modal from 'react-bootstrap/Modal'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { setIsOpenPreviewProductModal } from '../../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; -import useAppSelector from '../../../store/hooks/useAppSelector'; type Props = { item: ItemRequest; diff --git a/src/components/modals/share-modal/index.tsx b/src/components/modals/share-modal/index.tsx index 2a1e056..eda53be 100644 --- a/src/components/modals/share-modal/index.tsx +++ b/src/components/modals/share-modal/index.tsx @@ -1,8 +1,8 @@ import { useRouter } from 'next/router'; import Modal from 'react-bootstrap/Modal'; import { BsWhatsapp } from 'react-icons/bs'; +import useAppSelector from '@store/hooks/useAppSelector'; import { selectById } from '../../../features/leda-nft/store/leda-nft.slice'; -import useAppSelector from '../../../store/hooks/useAppSelector'; type Props = { show: boolean; diff --git a/src/components/product-bid/index.tsx b/src/components/product-bid/index.tsx index 37648ce..0626fba 100644 --- a/src/components/product-bid/index.tsx +++ b/src/components/product-bid/index.tsx @@ -2,10 +2,10 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ import { Price } from '@types'; +import useAppDispatch from '@store/hooks/useAppDispatch'; import { withAuthProtection } from '../../features/auth/store/auth.actions'; import ItemStatus from '../../common/minting/enums/item-status.enum'; import { likeItem } from '../../features/marketplace/store/marketplace.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; type Props = { itemId: string; diff --git a/src/components/signature-area/SignatureArea.tsx b/src/components/signature-area/SignatureArea.tsx index de4d8da..6a3571e 100644 --- a/src/components/signature-area/SignatureArea.tsx +++ b/src/components/signature-area/SignatureArea.tsx @@ -1,10 +1,10 @@ import { useRouter } from 'next/router'; import Button from '@ui/button'; import React, { useEffect, useState } from 'react'; +import { openToastError } from '@store/ui/ui.slice'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { selectAuthState } from '../../features/auth/store/auth.slice'; -import { openToastError } from '../../store/ui/ui.slice'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import useMetamask from '../../features/auth/hooks/useMetamask'; type Props = { diff --git a/src/components/user-dropdown/index.tsx b/src/components/user-dropdown/index.tsx index 4219241..0b0ba29 100644 --- a/src/components/user-dropdown/index.tsx +++ b/src/components/user-dropdown/index.tsx @@ -4,8 +4,8 @@ import Anchor from '@ui/anchor'; import Modal from 'react-bootstrap/Modal'; import Button from '@ui/button'; import { formattedAddress } from '@utils/getFormattedAddress'; +import useAppSelector from '@store/hooks/useAppSelector'; import constants from '../../common/configuration/constants'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { selectAuthState } from '../../features/auth/store/auth.slice'; import useMetamask from '../../features/auth/hooks/useMetamask'; import { selectAccountState } from '../../features/account/store/account.slice'; diff --git a/src/containers/activity/activity-area.tsx b/src/containers/activity/activity-area.tsx index 9dcbfc3..14c0cc9 100644 --- a/src/containers/activity/activity-area.tsx +++ b/src/containers/activity/activity-area.tsx @@ -4,36 +4,40 @@ import { getTimeAgo } from '@utils/getTimeAgo'; import clsx from 'clsx'; import Image from 'next/image'; import { useCallback, useEffect } from 'react'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import appConfig from '../../common/configuration/app.config'; import { findAllHistory } from '../../features/marketplace/store/marketplace.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; +import { resetFilters } from '../../features/marketplace/store/marketplace.slice'; export const ActivityArea = () => { const dispatch = useAppDispatch(); - const { history, isLoadingHistory } = useAppSelector((state) => state.marketplace); + const { history, isLoadingHistory, historyCount, filters } = useAppSelector( + (state) => state.marketplace + ); - const hasMore = history.data.length < history.count; + const hasMore = history.length < historyCount; useEffect(() => { + dispatch(resetFilters()); dispatch( findAllHistory({ - limit: history.limit, - page: history.page, + limit: filters.limit, + page: filters.page, }) ); - }, [dispatch, history.limit, history.page]); + }, [dispatch, filters.limit, filters.page]); const handleNext = useCallback(() => { if (hasMore) { - const newPage = Math.floor(history.data.length / history.limit + 1); - dispatch(findAllHistory({ limit: history.limit, page: newPage })); + const newPage = Math.floor(history.length / filters.limit + 1); + dispatch(findAllHistory({ limit: filters.limit, page: newPage })); } - }, [hasMore, history.data.length, history.limit, dispatch]); + }, [hasMore, history.length, filters.limit, dispatch]); const infiniteScrollSettings = { style: { overflow: 'inherit' }, - dataLength: history.data.length, + dataLength: history.length, handleNext, hasMore, loading: isLoadingHistory, @@ -47,7 +51,7 @@ export const ActivityArea = () => {
    - {history?.data.map((e) => ( + {history?.map((e) => (
    diff --git a/src/containers/author-intro/AuthorIntroArea.tsx b/src/containers/author-intro/AuthorIntroArea.tsx index 78b38d7..985fc7c 100644 --- a/src/containers/author-intro/AuthorIntroArea.tsx +++ b/src/containers/author-intro/AuthorIntroArea.tsx @@ -1,10 +1,10 @@ import ShareModal from '@components/modals/share-modal'; import Image from 'next/image'; import { useMemo, useState } from 'react'; +import useAppSelector from '@store/hooks/useAppSelector'; import ItemStatus from '../../common/minting/enums/item-status.enum'; import ReportModal from '../../components/modals/report-modal/index'; import { AuthorData } from '../../data/AuthorData'; -import useAppSelector from '../../store/hooks/useAppSelector'; type Props = { address: string; diff --git a/src/containers/author-profile/AuthorProfileArea.tsx b/src/containers/author-profile/AuthorProfileArea.tsx index 1278a8b..5198727 100644 --- a/src/containers/author-profile/AuthorProfileArea.tsx +++ b/src/containers/author-profile/AuthorProfileArea.tsx @@ -5,12 +5,12 @@ import Nav from 'react-bootstrap/Nav'; import TabContainer from 'react-bootstrap/TabContainer'; import TabContent from 'react-bootstrap/TabContent'; import TabPane from 'react-bootstrap/TabPane'; +import useAppSelector from '@store/hooks/useAppSelector'; import { selectCreatedItems, selectOnSaleItems, selectOwnedItems, } from '../../features/account/store/account.slice'; -import useAppSelector from '../../store/hooks/useAppSelector'; type Props = { address: string; diff --git a/src/containers/collection-details/collection-details.container.tsx b/src/containers/collection-details/collection-details.container.tsx index 244f055..4390e92 100644 --- a/src/containers/collection-details/collection-details.container.tsx +++ b/src/containers/collection-details/collection-details.container.tsx @@ -3,8 +3,8 @@ import ItemStatsComponent from '@components/items-stats/items-stats.components'; import { useState } from 'react'; import { BsListUl, BsListTask } from 'react-icons/bs'; import { MdOutlineAutoAwesomeMosaic, MdAutoAwesomeMosaic } from 'react-icons/md'; -import useAppSelector from '../../store/hooks/useAppSelector'; -import CollectionItemsContainer from './collection-items.container'; +import useAppSelector from '@store/hooks/useAppSelector'; +import { CollectionItemsContainer } from './collection-items'; const NotFound = () => (
    diff --git a/src/containers/collection-details/collection-items.container.tsx b/src/containers/collection-details/collection-items.tsx similarity index 73% rename from src/containers/collection-details/collection-items.container.tsx rename to src/containers/collection-details/collection-items.tsx index a719ece..d7ec358 100644 --- a/src/containers/collection-details/collection-items.container.tsx +++ b/src/containers/collection-details/collection-items.tsx @@ -1,20 +1,20 @@ -import CollectionItemsComponent from '@components/collections/collection-items.component'; +import { CollectionItemsArea } from '@components/collections/collection-items'; import ItemCollectionFilter from '@components/collections/items-collection-filter.component'; import NoSearchResults from '@containers/marketplace/no-search-results'; import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import { useMemo } from 'react'; -import useAppSelector from '../../store/hooks/useAppSelector'; +import useAppSelector from '@store/hooks/useAppSelector'; -const CollectionItemsContainer = () => { +export const CollectionItemsContainer = () => { const { isPagingLoading, items } = useAppSelector((state) => state.marketplace); const renderedComponent = useMemo(() => { - if (items.length) return ; + if (items.length) return ; if (!isPagingLoading) return ; return null; - }, [items.length, isPagingLoading]); + }, [items, isPagingLoading]); return (
    @@ -32,5 +32,3 @@ const CollectionItemsContainer = () => {
    ); }; - -export default CollectionItemsContainer; diff --git a/src/containers/collections/collections.container.tsx b/src/containers/collections/collections.container.tsx index 590ef2e..0245faa 100644 --- a/src/containers/collections/collections.container.tsx +++ b/src/containers/collections/collections.container.tsx @@ -3,7 +3,7 @@ import CollectionRendered from '@components/collections/collections-rendered'; import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import Link from 'next/link'; import { useMemo } from 'react'; -import useAppSelector from '../../store/hooks/useAppSelector'; +import useAppSelector from '@store/hooks/useAppSelector'; const CollectionsContainer = () => { const { collections, collectionsCount, isLoadingCollections } = useAppSelector( diff --git a/src/containers/collections/index.tsx b/src/containers/collections/index.tsx index 7d167b3..b3c84d0 100644 --- a/src/containers/collections/index.tsx +++ b/src/containers/collections/index.tsx @@ -13,9 +13,9 @@ import { AiOutlinePlus } from 'react-icons/ai'; import { useClickAway } from 'react-use'; import Switch from 'react-switch'; import { RiDeleteBack2Fill } from 'react-icons/ri'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { mintNft } from '../../features/leda-nft/store/leda-nft.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { selectNftState } from '../../features/leda-nft/store/leda-nft.slice'; import useMetamask from '../../features/auth/hooks/useMetamask'; import { ItemProperty } from '../../common/types/ipfs-types'; diff --git a/src/containers/collections/newest-collections.container.tsx b/src/containers/collections/newest-collections.container.tsx index e2ec70d..95bac63 100644 --- a/src/containers/collections/newest-collections.container.tsx +++ b/src/containers/collections/newest-collections.container.tsx @@ -3,9 +3,9 @@ import Anchor from '@ui/anchor'; import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import { useEffect, useMemo } from 'react'; import ClipLoader from 'react-spinners/ClipLoader'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { getNewestCollections } from '../../features/collections/store/collections.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { ICollection } from '../../types/ICollection'; const NotFound = () => ( diff --git a/src/containers/connect/index.tsx b/src/containers/connect/index.tsx index c853708..67e326d 100644 --- a/src/containers/connect/index.tsx +++ b/src/containers/connect/index.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import Wallet from '@components/wallet'; -import useAppSelector from '../../store/hooks/useAppSelector'; +import useAppSelector from '@store/hooks/useAppSelector'; import { selectAuthState } from '../../features/auth/store/auth.slice'; import useMetamask from '../../features/auth/hooks/useMetamask'; diff --git a/src/containers/create-new/index.tsx b/src/containers/create-new/index.tsx index e3ccee2..a3bb7a1 100644 --- a/src/containers/create-new/index.tsx +++ b/src/containers/create-new/index.tsx @@ -17,6 +17,8 @@ import { AiOutlinePlus } from 'react-icons/ai'; import { RiDeleteBack2Fill } from 'react-icons/ri'; import { useClickAway } from 'react-use'; import { useForm } from 'react-hook-form'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { ItemProperty } from '../../common/types/ipfs-types'; import { findUserCollectionsWithoutItems } from '../../features/account/store/account.actions'; import useMetamask from '../../features/auth/hooks/useMetamask'; @@ -24,8 +26,6 @@ import { withAuthProtection } from '../../features/auth/store/auth.actions'; import { mintNft } from '../../features/leda-nft/store/leda-nft.actions'; import { selectNftState } from '../../features/leda-nft/store/leda-nft.slice'; import { setIsOpenPreviewProductModal } from '../../features/marketplace/store/marketplace.slice'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { CollectionCreateType } from '../../types/collection-type'; const tagsErrorMessages = { diff --git a/src/containers/hero/hero.tsx b/src/containers/hero/hero.tsx index 9ba017c..11dca2a 100644 --- a/src/containers/hero/hero.tsx +++ b/src/containers/hero/hero.tsx @@ -5,7 +5,7 @@ import { SpinnerContainer } from '@ui/spinner-container/spinner-container'; import Link from 'next/link'; import Dropdown from 'react-bootstrap/Dropdown'; import { BsCaretDownFill } from 'react-icons/bs'; -import useAppSelector from '../../store/hooks/useAppSelector'; +import useAppSelector from '@store/hooks/useAppSelector'; import { selectNftState } from '../../features/leda-nft/store/leda-nft.slice'; const Hero = () => { diff --git a/src/containers/item-details/item-details.tsx b/src/containers/item-details/item-details.tsx index aceae08..4401bbc 100644 --- a/src/containers/item-details/item-details.tsx +++ b/src/containers/item-details/item-details.tsx @@ -1,17 +1,17 @@ import { BidTab } from '@components/item-details/bid-tab/bid-tab'; -import BuyNftComponent from '@components/item-details/buy-nft-component'; +import { BuyNft } from '@components/item-details/buy-nft'; import ProductTitle from '@components/item-details/title'; import Button from '@ui/button'; import Sticky from '@ui/sticky'; import clsx from 'clsx'; import Link from 'next/link'; import { useMemo } from 'react'; +import useAppSelector from '@store/hooks/useAppSelector'; +import { selectUiReducer } from '@store/ui/ui.slice'; import appConfig from '../../common/configuration/app.config'; import ItemStatus from '../../common/minting/enums/item-status.enum'; import { selectAuthState } from '../../features/auth/store/auth.slice'; import { selectCanISeeItem } from '../../features/marketplace/store/marketplace.slice'; -import useAppSelector from '../../store/hooks/useAppSelector'; -import { selectUiReducer } from '../../store/ui/ui.slice'; const HiddenLayout = () => (
    @@ -109,7 +109,7 @@ const RenderedItem = () => {
      - {!isOwner && selectedItem.status === ItemStatus.Listed && } + {!isOwner && selectedItem.status === ItemStatus.Listed && }
    diff --git a/src/containers/marketplace/MarketplaceArea.tsx b/src/containers/marketplace/MarketplaceArea.tsx index 84bdc2a..9e0eb72 100644 --- a/src/containers/marketplace/MarketplaceArea.tsx +++ b/src/containers/marketplace/MarketplaceArea.tsx @@ -2,9 +2,9 @@ import InfiniteScroll from '@components/common/InfiniteScroll'; import Item from '@components/item'; import { Item as ItemType } from '@types'; import { useCallback } from 'react'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; import { findPagedItems } from '../../features/marketplace/store/marketplace.actions'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; export const MarketplaceArea = () => { const dispatch = useAppDispatch(); diff --git a/src/features/account/store/account.slice.ts b/src/features/account/store/account.slice.ts index f2c018d..ae2b499 100644 --- a/src/features/account/store/account.slice.ts +++ b/src/features/account/store/account.slice.ts @@ -1,7 +1,7 @@ import { createSelector, createSlice } from '@reduxjs/toolkit'; import { Item } from '@types'; +import type { RootState } from '@store/types'; import ItemStatus from '../../../common/minting/enums/item-status.enum'; -import type { RootState } from '../../../store/types'; type AccountState = { isLoading: boolean; diff --git a/src/features/auth/hooks/useMetamask.ts b/src/features/auth/hooks/useMetamask.ts index f58dcfb..5dbf625 100644 --- a/src/features/auth/hooks/useMetamask.ts +++ b/src/features/auth/hooks/useMetamask.ts @@ -1,11 +1,11 @@ +import useAppSelector from '@store/hooks/useAppSelector'; import { ethers } from 'ethers'; -import { useEffect, useCallback, useState } from 'react'; -import { authenticate, signin } from '../store/auth.actions'; -import { openToastError } from '../../../store/ui/ui.slice'; -import { selectAuthState, setEthAddress, setIsConnected } from '../store/auth.slice'; -import useAppDispatch from '../../../store/hooks/useAppDispatch'; +import { useCallback, useEffect, useState } from 'react'; import { NetworkNames } from '../../../common/enums/network-names.enum'; -import useAppSelector from '../../../store/hooks/useAppSelector'; +import useAppDispatch from '../../../store/hooks/useAppDispatch'; +import { openToastError } from '../../../store/ui/ui.slice'; +import { authenticate, signIn } from '../store/auth.actions'; +import { setEthAddress, setIsConnected } from '../store/auth.slice'; const useMetamask = () => { const dispatch = useAppDispatch(); @@ -13,7 +13,7 @@ const useMetamask = () => { const [connected, setConnected] = useState(false); const [isMetamaskIntalled, setIsMetamaskInstalled] = useState(false); const [signer, setSigner] = useState(); - const { address, isConnected } = useAppSelector(selectAuthState); + const { address, isConnected } = useAppSelector((state) => state.auth); useEffect(() => { const isInstalled = window.ethereum && window.ethereum.isMetaMask; @@ -59,7 +59,7 @@ const useMetamask = () => { return; } - dispatch(signin(address)); + dispatch(signIn(address)); }; useEffect(() => { diff --git a/src/features/auth/store/auth.actions.ts b/src/features/auth/store/auth.actions.ts index a9971d4..19edace 100644 --- a/src/features/auth/store/auth.actions.ts +++ b/src/features/auth/store/auth.actions.ts @@ -1,26 +1,18 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import Router from 'next/router'; +import MetaType from '../../../store/enums/meta-type.enum'; +import { openToastError, setIsMainnetModalOpen } from '../../../store/ui/ui.slice'; +import type { RootState } from '../../../store/types'; +import { rejectWithMetamask } from '../../../store/error/error-handler'; import { randomIntFromInterval } from '../../../utils/getRandomIntFromInterval'; import { authService } from '../services/auth.service'; import { getSigner } from '../../../common/utils/metamask-utils'; import { localStorageService } from '../../../common/services/local-storage.service'; -import { openToastError, setIsMainnetModalOpen } from '../../../store/ui/ui.slice'; -import { rejectWithMetamask } from '../../../store/error/error-handler'; import constants from '../../../common/configuration/constants'; -import MetaType from '../../../store/enums/meta-type.enum'; -import type { RootState } from '../../../store/types'; import { setProfileImage } from '../../account/store/account.slice'; import { EnvironmentsEnum } from '../../../common/enums/environments.enum'; -const authenticate = createAsyncThunk( - 'auth/authenticate', - async (address: string) => { - const validToken = await authService.authenticateLocalToken(address); - return !!validToken; - } -); - -const signin = createAsyncThunk( +export const signIn = createAsyncThunk( 'auth/signin', async (address: string, { dispatch, rejectWithValue }) => { try { @@ -49,8 +41,9 @@ const signin = createAsyncThunk( } ); -const withAuthProtection = createAsyncThunk( +export const withAuthProtection = createAsyncThunk( 'auth/withAuthProtection', + // eslint-disable-next-line @typescript-eslint/no-explicit-any async (action: any, { dispatch, getState }) => { const { NEXT_PUBLIC_NODE_ENV } = process.env; @@ -73,7 +66,7 @@ const withAuthProtection = createAsyncThunk( const validToken = await authService.authenticateLocalToken(auth.address); if (!validToken) { - const response = await dispatch(signin(auth.address)); + const response = await dispatch(signIn(auth.address)); if (response.meta.requestStatus === MetaType.rejected) return; } @@ -81,4 +74,7 @@ const withAuthProtection = createAsyncThunk( } ); -export { authenticate, signin, withAuthProtection }; +export const authenticate = createAsyncThunk('auth/authenticate', async (address: string) => { + const validToken = await authService.authenticateLocalToken(address); + return !!validToken; +}); diff --git a/src/features/auth/store/auth.slice.spec.ts b/src/features/auth/store/auth.slice.spec.ts index aeca21b..4134a93 100644 --- a/src/features/auth/store/auth.slice.spec.ts +++ b/src/features/auth/store/auth.slice.spec.ts @@ -1,5 +1,5 @@ import { AnyAction } from '@reduxjs/toolkit'; -import { authenticate, signin } from './auth.actions'; +import { authenticate, signIn } from './auth.actions'; import { authReducer, AuthState, setEthAddress, setIsConnected } from './auth.slice'; describe('Auth slice', () => { @@ -19,7 +19,7 @@ describe('Auth slice', () => { it('should return initial state', () => { const expected = initialState; - const actual = authReducer(undefined, {} as AnyAction); + const actual = authReducer(initialState, {} as AnyAction); expect(actual).toEqual(expected); }); @@ -29,7 +29,7 @@ describe('Auth slice', () => { it('should assign the address correctly', () => { const expected = '0x01123'; - const actual = authReducer(undefined, setEthAddress(expected)); + const actual = authReducer(initialState, setEthAddress(expected)); expect(actual.address).toEqual(expected); }); @@ -39,7 +39,7 @@ describe('Auth slice', () => { it('should assign the isConnected flag correctly', () => { const expected = true; - const actual = authReducer(undefined, setIsConnected(true)); + const actual = authReducer(initialState, setIsConnected(true)); expect(actual.isConnected).toEqual(expected); }); @@ -49,7 +49,7 @@ describe('Auth slice', () => { it('should assign the isAuthCompleted flags to false.', () => { const expected = false; - const actual = authReducer(undefined, authenticate.pending('', '')); + const actual = authReducer(initialState, authenticate.pending('', '', '')); expect(actual.isAuthCompleted).toEqual(expected); }); @@ -60,28 +60,28 @@ describe('Auth slice', () => { const expectedIsAuthenticated = true; const expectedIsAuthCompleted = true; - const actual = authReducer(undefined, authenticate.fulfilled(true, '', '')); + const actual = authReducer(initialState, authenticate.fulfilled(true, '', '')); expect(actual.isAuthenticated).toEqual(expectedIsAuthenticated); expect(actual.isAuthCompleted).toEqual(expectedIsAuthCompleted); }); }); - describe('When signin function is called', () => { + describe('When signIn function is called', () => { it('should assign the isAuthenticated flag to true.', () => { const expected = true; - const actual = authReducer(undefined, signin.fulfilled('', '', '')); + const actual = authReducer(initialState, signIn.fulfilled('', '', '')); expect(actual.isAuthenticated).toEqual(expected); }); }); - describe('When signin function is called and it is rejected', () => { + describe('When signIn function is called and it is rejected', () => { it('should assign the isAuthenticated flag to false.', () => { const expected = false; - const actual = authReducer(undefined, signin.rejected(null, '', '')); + const actual = authReducer(initialState, signIn.rejected(null, '', '')); expect(actual.isAuthenticated).toEqual(expected); }); diff --git a/src/features/auth/store/auth.slice.ts b/src/features/auth/store/auth.slice.ts index db78fba..89daed2 100644 --- a/src/features/auth/store/auth.slice.ts +++ b/src/features/auth/store/auth.slice.ts @@ -1,6 +1,6 @@ import { createSlice } from '@reduxjs/toolkit'; -import { authenticate, signin } from './auth.actions'; -import type { RootState } from '../../../store/types'; +import type { RootState } from '@store/types'; +import { authenticate, signIn } from './auth.actions'; export type AuthState = { address: string; @@ -40,10 +40,13 @@ const authSlice = createSlice({ state.isAuthenticated = payload; state.isAuthCompleted = true; }); - builder.addCase(signin.fulfilled, (state) => { + builder.addCase(authenticate.rejected, (state) => { + state.isAuthCompleted = false; + }); + builder.addCase(signIn.fulfilled, (state) => { state.isAuthenticated = true; }); - builder.addCase(signin.rejected, (state) => { + builder.addCase(signIn.rejected, (state) => { state.isAuthenticated = false; }); }, diff --git a/src/features/collections/store/collections.actions.ts b/src/features/collections/store/collections.actions.ts index 3405eca..a9affdc 100644 --- a/src/features/collections/store/collections.actions.ts +++ b/src/features/collections/store/collections.actions.ts @@ -4,32 +4,33 @@ import { collectionsService } from '../services/collections.service'; import { CollectionFilterType } from '../types/CollectionsFiltersTypes'; export const findCollectionById = createAsyncThunk( - 'collections/findById', + 'marketplace/findById', async (collectionId: string) => collectionsService.findById(collectionId) ); -export const getNewestCollections = createAsyncThunk('collections/getNewest', async (qty: number) => - collectionsService.findNewest(qty) +export const getNewestCollections = createAsyncThunk( + 'marketplace/getNewestCollections', + async (qty: number) => collectionsService.findNewest(qty) ); export const findPagedCollections = createAsyncThunk( - 'collections/findPagedCollections', + 'marketplace/findPagedCollections', async (filters: CollectionFilterType) => collectionsService.findPagedCollections(filters) ); export const findFilteredCollections = createAsyncThunk( - 'collections/findFilteredCollections', + 'marketplace/findFilteredCollections', async (filters: CollectionFilterType) => collectionsService.findPagedCollections(filters) ); export const findFilteredCollectionItems = createAsyncThunk( - 'collections/findFilteredCollectionItems', + 'marketplace/findFilteredCollectionItems', async ({ collectionId, filters }: { collectionId: string; filters: FilterType }) => collectionsService.findPagedCollectionItems(collectionId, filters) ); export const findPagedCollectionsNfts = createAsyncThunk( - 'collections/findFilteredCollectionsNfts', + 'marketplace/findFilteredCollectionsNfts', async ({ collectionId, page }: { collectionId: string; page: number }) => collectionsService.findPagedCollectionsNfts(collectionId, page) ); diff --git a/src/features/collections/store/collections.slice.ts b/src/features/collections/store/collections.slice.ts deleted file mode 100644 index 87f7500..0000000 --- a/src/features/collections/store/collections.slice.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from '../../../store/types'; - -type CollectionsState = { - isPagingLoading: boolean; -}; - -const initialState: CollectionsState = { - isPagingLoading: false, -}; - -const collectionsSlice = createSlice({ - name: 'collections', - initialState, - reducers: {}, - extraReducers: (builder) => {}, -}); - -export const collectionsReducer = collectionsSlice.reducer; - -export const selectCollectionsState = (state: RootState) => state.collections; diff --git a/src/features/leda-nft/store/leda-nft.actions.ts b/src/features/leda-nft/store/leda-nft.actions.ts index 2c8cfab..c0bfd46 100644 --- a/src/features/leda-nft/store/leda-nft.actions.ts +++ b/src/features/leda-nft/store/leda-nft.actions.ts @@ -1,13 +1,13 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { Item, ItemRequest } from '@types'; import Router from 'next/router'; +import { openToastError, openToastSuccess } from '@store/ui/ui.slice'; import BusinessError from '../../../common/exceptions/business-error'; import ClientProcessor from '../../../common/minting/clients/client-processor'; import CollectionType from '../../../common/minting/enums/collection-type.enum'; import ContractEvent from '../../../common/minting/enums/contract-event.enum'; import { LazyProcessType } from '../../../common/minting/enums/lazy-process-type.enum'; import MintState from '../../../common/minting/types/mint-state'; -import { openToastError, openToastSuccess } from '../../../store/ui/ui.slice'; import { getContracts } from '../../../utils/getContracts'; import { setIsModalOpen } from '../../marketplace/store/marketplace.slice'; import { itemService } from '../services/item.service'; diff --git a/src/features/leda-nft/store/leda-nft.slice.ts b/src/features/leda-nft/store/leda-nft.slice.ts index 9a43b86..2a5c0c9 100644 --- a/src/features/leda-nft/store/leda-nft.slice.ts +++ b/src/features/leda-nft/store/leda-nft.slice.ts @@ -1,6 +1,6 @@ import { createSlice } from '@reduxjs/toolkit'; import { Item } from '@types'; -import type { RootState } from '../../../store/types'; +import type { RootState } from '@store/types'; import { buyItem, likeItem, listItem } from '../../marketplace/store/marketplace.actions'; import { getNewest, mintNft, redeemVoucher } from './leda-nft.actions'; diff --git a/src/features/marketplace/process/commands/buy-item/buy-item-command.ts b/src/features/marketplace/process/commands/buy-item/buy-item-command.ts index 5661a3a..bf7c02d 100644 --- a/src/features/marketplace/process/commands/buy-item/buy-item-command.ts +++ b/src/features/marketplace/process/commands/buy-item/buy-item-command.ts @@ -1,6 +1,6 @@ import { ethers } from 'ethers'; -import { IMarketplaceService } from '../../../services/marketplace-service.interface'; import { rejectWithMetamask } from '../../../../../store/error/error-handler'; +import { IMarketplaceService } from '../../../services/marketplace-service.interface'; import ICommand from '../../interfaces/command.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import MarketplaceState from '../../types/marketplace-state'; diff --git a/src/features/marketplace/process/commands/buy-item/store-buy-item-command.ts b/src/features/marketplace/process/commands/buy-item/store-buy-item-command.ts index 1cf8d0c..d23dbf3 100644 --- a/src/features/marketplace/process/commands/buy-item/store-buy-item-command.ts +++ b/src/features/marketplace/process/commands/buy-item/store-buy-item-command.ts @@ -1,8 +1,8 @@ +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import IItemService from '../../../../leda-nft/interfaces/item-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; export default class StoreBuyItemCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/features/marketplace/process/commands/change-price-item/change-price-item-command.spec.ts b/src/features/marketplace/process/commands/change-price-item/change-price-item-command.spec.ts index 57b5f77..13cc605 100644 --- a/src/features/marketplace/process/commands/change-price-item/change-price-item-command.spec.ts +++ b/src/features/marketplace/process/commands/change-price-item/change-price-item-command.spec.ts @@ -1,9 +1,9 @@ import { ContractReceipt, ethers, Event } from 'ethers'; +import * as errorHandler from '../../../../../store/error/error-handler'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; import ChangePriceItemCommand from './change-price-item-command'; -import * as errorHandler from '../../../../../store/error/error-handler'; const marketplaceServiceMock = { init: jest.fn(), diff --git a/src/features/marketplace/process/commands/change-price-item/change-price-item-command.ts b/src/features/marketplace/process/commands/change-price-item/change-price-item-command.ts index 998927a..e69e17e 100644 --- a/src/features/marketplace/process/commands/change-price-item/change-price-item-command.ts +++ b/src/features/marketplace/process/commands/change-price-item/change-price-item-command.ts @@ -1,6 +1,6 @@ import { ethers } from 'ethers'; -import { IMarketplaceService } from '../../../services/marketplace-service.interface'; import { rejectWithMetamask } from '../../../../../store/error/error-handler'; +import { IMarketplaceService } from '../../../services/marketplace-service.interface'; import ICommand from '../../interfaces/command.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import MarketplaceState from '../../types/marketplace-state'; diff --git a/src/features/marketplace/process/commands/change-price-item/store-change-price-item-command.ts b/src/features/marketplace/process/commands/change-price-item/store-change-price-item-command.ts index 49fc995..d42b512 100644 --- a/src/features/marketplace/process/commands/change-price-item/store-change-price-item-command.ts +++ b/src/features/marketplace/process/commands/change-price-item/store-change-price-item-command.ts @@ -1,8 +1,8 @@ +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import IItemService from '../../../../leda-nft/interfaces/item-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; export default class StoreChangePriceItemCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/features/marketplace/process/commands/common-lazy/generate-jup-voucher-command.ts b/src/features/marketplace/process/commands/common-lazy/generate-jup-voucher-command.ts index 6065b0b..9a2936c 100644 --- a/src/features/marketplace/process/commands/common-lazy/generate-jup-voucher-command.ts +++ b/src/features/marketplace/process/commands/common-lazy/generate-jup-voucher-command.ts @@ -1,9 +1,9 @@ import { ethers } from 'ethers'; +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import ILazyMintService from '../../../../leda-nft/interfaces/lazy-mint-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; import IImageService from '../../../../leda-nft/interfaces/image-service.interface'; export default class GenerateJupVoucherCommand implements ICommand { diff --git a/src/features/marketplace/process/commands/common-lazy/generate-voucher-command.ts b/src/features/marketplace/process/commands/common-lazy/generate-voucher-command.ts index 074d7e4..8800456 100644 --- a/src/features/marketplace/process/commands/common-lazy/generate-voucher-command.ts +++ b/src/features/marketplace/process/commands/common-lazy/generate-voucher-command.ts @@ -1,9 +1,9 @@ import { ethers } from 'ethers'; +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import ILazyMintService from '../../../../leda-nft/interfaces/lazy-mint-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; import IImageService from '../../../../leda-nft/interfaces/image-service.interface'; export default class GenerateVoucherCommand implements ICommand { diff --git a/src/features/marketplace/process/commands/common-lazy/store-voucher-command.ts b/src/features/marketplace/process/commands/common-lazy/store-voucher-command.ts index 707f26a..f127279 100644 --- a/src/features/marketplace/process/commands/common-lazy/store-voucher-command.ts +++ b/src/features/marketplace/process/commands/common-lazy/store-voucher-command.ts @@ -1,9 +1,9 @@ +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import ProcessLazyItemRequest from '../../../../../common/types/process-lazy-item-request'; import IItemService from '../../../../leda-nft/interfaces/item-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; export default class StoreVoucherCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/features/marketplace/process/commands/delist-item/change-status-item-command.spec.ts b/src/features/marketplace/process/commands/delist-item/change-status-item-command.spec.ts index 26b4fdd..eecc9b2 100644 --- a/src/features/marketplace/process/commands/delist-item/change-status-item-command.spec.ts +++ b/src/features/marketplace/process/commands/delist-item/change-status-item-command.spec.ts @@ -169,7 +169,7 @@ describe('ListItemCommand', () => { const errorMessage = 'something went wrong'; - const expected = { ...state, error: MarketplaceError.ChangePriceItemFailure }; + const expected = { ...state, error: MarketplaceError.ChangeStatusItemFailure }; jest.spyOn(errorHandler, 'rejectWithMetamask').mockResolvedValue(expected); diff --git a/src/features/marketplace/process/commands/delist-item/store-delist-item-command.ts b/src/features/marketplace/process/commands/delist-item/store-delist-item-command.ts index 9283ccb..3263429 100644 --- a/src/features/marketplace/process/commands/delist-item/store-delist-item-command.ts +++ b/src/features/marketplace/process/commands/delist-item/store-delist-item-command.ts @@ -1,8 +1,8 @@ +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import IItemService from '../../../../leda-nft/interfaces/item-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; export default class StoreDelistItemCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/features/marketplace/process/commands/list-item/approve-command.ts b/src/features/marketplace/process/commands/list-item/approve-command.ts index b09b49e..c375d91 100644 --- a/src/features/marketplace/process/commands/list-item/approve-command.ts +++ b/src/features/marketplace/process/commands/list-item/approve-command.ts @@ -1,5 +1,5 @@ -import { getContracts } from '../../../../../utils/getContracts'; import { rejectWithMetamask } from '../../../../../store/error/error-handler'; +import { getContracts } from '../../../../../utils/getContracts'; import ICommand from '../../interfaces/command.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import MarketplaceState from '../../types/marketplace-state'; diff --git a/src/features/marketplace/process/commands/list-item/list-item-command.spec.ts b/src/features/marketplace/process/commands/list-item/list-item-command.spec.ts index bf17f65..1e974dd 100644 --- a/src/features/marketplace/process/commands/list-item/list-item-command.spec.ts +++ b/src/features/marketplace/process/commands/list-item/list-item-command.spec.ts @@ -1,9 +1,9 @@ import { ContractReceipt, ethers, Event } from 'ethers'; +import * as errorHandler from '../../../../../store/error/error-handler'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; import ListItemCommand from './list-item-command'; -import * as errorHandler from '../../../../../store/error/error-handler'; const marketplaceServiceMock = { init: jest.fn(), diff --git a/src/features/marketplace/process/commands/list-item/list-item-command.ts b/src/features/marketplace/process/commands/list-item/list-item-command.ts index 41212d9..5e9efad 100644 --- a/src/features/marketplace/process/commands/list-item/list-item-command.ts +++ b/src/features/marketplace/process/commands/list-item/list-item-command.ts @@ -1,6 +1,6 @@ import { ethers } from 'ethers'; -import { IMarketplaceService } from '../../../services/marketplace-service.interface'; import { rejectWithMetamask } from '../../../../../store/error/error-handler'; +import { IMarketplaceService } from '../../../services/marketplace-service.interface'; import ICommand from '../../interfaces/command.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import MarketplaceState from '../../types/marketplace-state'; diff --git a/src/features/marketplace/process/commands/list-item/store-list-item-command.ts b/src/features/marketplace/process/commands/list-item/store-list-item-command.ts index 45b1c5a..dae2921 100644 --- a/src/features/marketplace/process/commands/list-item/store-list-item-command.ts +++ b/src/features/marketplace/process/commands/list-item/store-list-item-command.ts @@ -1,8 +1,8 @@ +import { rejectWithHttp } from '../../../../../store/error/error-handler'; import IItemService from '../../../../leda-nft/interfaces/item-service.interface'; import MarketplaceError from '../../enums/marketplace-error.enum'; import ICommand from '../../interfaces/command.interface'; import MarketplaceState from '../../types/marketplace-state'; -import { rejectWithHttp } from '../../../../../store/error/error-handler'; export default class StoreListItemCommand implements ICommand { private readonly itemService: IItemService; diff --git a/src/features/marketplace/store/marketplace.actions.ts b/src/features/marketplace/store/marketplace.actions.ts index 67dd925..97aa55a 100644 --- a/src/features/marketplace/store/marketplace.actions.ts +++ b/src/features/marketplace/store/marketplace.actions.ts @@ -1,11 +1,11 @@ import { createAsyncThunk, Dispatch } from '@reduxjs/toolkit'; import Router from 'next/router'; +import type { RootState } from '../../../store/types'; +import { openToastError, openToastSuccess } from '../../../store/ui/ui.slice'; import BusinessError from '../../../common/exceptions/business-error'; import CollectionType from '../../../common/minting/enums/collection-type.enum'; import ItemStatus from '../../../common/minting/enums/item-status.enum'; import { LazyProcessType } from '../../../common/minting/enums/lazy-process-type.enum'; -import type { RootState } from '../../../store/types'; -import { openToastError, openToastSuccess } from '../../../store/ui/ui.slice'; import { Item } from '../../../types/item'; import { FilterType, FilterTypeBase } from '../../../types/item-filter-types'; import { getContracts } from '../../../utils/getContracts'; @@ -34,65 +34,58 @@ export const findPriceRange = createAsyncThunk('marketplace/findPriceRange', asy itemService.findPriceRange() ); -export const listItem = createAsyncThunk( - 'marketplace/listItem', - async ( - { +export const listItem = createAsyncThunk< + Item, + { + address: string; + price: string; + item: Item; + } +>('marketplace/listItem', async ({ address, price, item }, { dispatch }) => { + const { + tokenId, + listId, + itemId, + image, + isLazy, + royalty, + owner, + collectionAddress, + stakingRewards, + } = item; + try { + const listItemState = { address, + collection: CollectionType.LedaNft, + collectionAddress, + mintEventName: ContractEvent.LogCreateItem, price, - item, - }: { - address: string; - price: string; - item: Item; - }, - { dispatch } - ) => { - const { tokenId, - listId, itemId, - image, + item: { itemId, stakingRewards }, + status: ItemStatus.Listed, + ownerAddress: owner.address, + listId, + cid: image.cid, + imageUrl: image.url, + lazyProcessType: LazyProcessType.Listing, isLazy, royalty, - owner, - collectionAddress, - stakingRewards, - } = item; - try { - const listItemState = { - address, - collection: CollectionType.LedaNft, - collectionAddress, - mintEventName: ContractEvent.LogCreateItem, - price, - tokenId, - itemId, - item: { itemId, stakingRewards }, - status: ItemStatus.Listed, - ownerAddress: owner.address, - listId, - cid: image.cid, - imageUrl: image.url, - lazyProcessType: LazyProcessType.Listing, - isLazy, - royalty, - } as MarketplaceState; + } as MarketplaceState; - const processor = new MarketplaceClientProcessor(); - const listed = await processor.execute(listItemState); + const processor = new MarketplaceClientProcessor(); + const listed = await processor.execute(listItemState); - dispatch(openToastSuccess('The item has been successfully listed on the marketplace.')); + dispatch(openToastSuccess('The item has been successfully listed on the marketplace.')); - return listed.item; - } catch (err) { - if (err instanceof BusinessError) { - dispatch(openToastError(err.message)); - } - throw err; + return listed.item; + } catch (err) { + if (err instanceof BusinessError) { + dispatch(openToastError(err.message)); } + throw err; } -); +}); export const delistItem = createAsyncThunk( 'marketplace/delistItem', diff --git a/src/features/marketplace/store/marketplace.slice.spec.ts b/src/features/marketplace/store/marketplace.slice.spec.ts index 7d8ae60..61dac24 100644 --- a/src/features/marketplace/store/marketplace.slice.spec.ts +++ b/src/features/marketplace/store/marketplace.slice.spec.ts @@ -1,4 +1,5 @@ import { AnyAction } from '@reduxjs/toolkit'; +import { History } from '../../../types/history'; import { ICollection } from '../../../types/ICollection'; import { Item } from '../../../types/item'; import { FilterType } from '../../../types/item-filter-types'; @@ -6,8 +7,8 @@ import { findFilteredItems, findPagedItems, getNewest, listItem } from './market import { marketplaceReducer, MarketplaceState, - resetMarketplaceFilters, - setMarketplaceFilters, + resetFilters, + setFilters, } from './marketplace.slice'; describe('Marketplace slice', () => { @@ -21,6 +22,8 @@ describe('Marketplace slice', () => { collections: [], collectionsCount: 0, collectionsWithoutItems: [], + selectedItem: {} as Item, + newestItems: [], filters: { likesDirection: '', search: '', @@ -32,15 +35,9 @@ describe('Marketplace slice', () => { mostExpensive: '', page: 1, limit: 3, - } as FilterType, - selectedItem: {} as Item, - newestItems: [], - history: { - data: [], - count: 0, - limit: 3, - page: 1, }, + history: [] as History[], + historyCount: 0, newestCollections: [] as ICollection[], selectedCollection: {} as ICollection, isLoadingCollections: false, @@ -136,7 +133,7 @@ describe('Marketplace slice', () => { }, }; - const actual = marketplaceReducer(undefined, setMarketplaceFilters(expected)); + const actual = marketplaceReducer(undefined, setFilters(expected)); expect(actual.filters).toEqual(expected); }); @@ -144,23 +141,30 @@ describe('Marketplace slice', () => { describe('When resetMarketplaceFilters reducer is called', () => { it('should assign the marketplace filters initial state correctly', () => { - const actual = marketplaceReducer(undefined, resetMarketplaceFilters()); + const actual = marketplaceReducer(undefined, resetFilters()); expect(actual.filters).toEqual(initialState.filters); }); }); describe('When isListed is called', () => { - it('should return true when isListed is succesfull', () => { + it('should return true when isListed is successfully', () => { const expected = true; - const actual = marketplaceReducer(undefined, listItem.fulfilled); + const actual = marketplaceReducer( + initialState, + listItem.fulfilled({} as Item, '', { + address: 'string', + price: 'string', + item: {} as Item, + }) + ); expect(actual.isCompleted).toEqual(expected); }); it('should return false when isListed is rejected', () => { const expected = false; - const actual = marketplaceReducer(undefined, listItem.rejected); + const actual = marketplaceReducer(initialState, listItem.rejected); expect(actual.isCompleted).toEqual(expected); }); diff --git a/src/features/marketplace/store/marketplace.slice.ts b/src/features/marketplace/store/marketplace.slice.ts index 236a2ef..ae252f6 100644 --- a/src/features/marketplace/store/marketplace.slice.ts +++ b/src/features/marketplace/store/marketplace.slice.ts @@ -1,6 +1,6 @@ import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from '@store/types'; import ItemStatus from '../../../common/minting/enums/item-status.enum'; -import type { RootState } from '../../../store/types'; import { History } from '../../../types/history'; import { ICollection } from '../../../types/ICollection'; import { Item } from '../../../types/item'; @@ -21,7 +21,6 @@ import { findCollectionsByPriceRange, getNewestCollections, } from '../../collections/store/collections.actions'; - import { CollectionFilterType } from '../../collections/types/CollectionsFiltersTypes'; import { buyItem, @@ -43,18 +42,14 @@ export type MarketplaceState = { likedItems: Item[]; collections: ICollection[]; collectionsCount: number; + historyCount: number; collectionsWithoutItems: ICollection[]; itemsCount: number; filters: FilterType; collectionsFilters: CollectionFilterType; newestItems: Item[]; selectedItem: Item; - history: { - data: History[]; - count: number; - limit: number; - page: number; - }; + history: History[]; selectedCollection: ICollection; newestCollections: ICollection[]; isLoadingCollections: boolean; @@ -100,12 +95,8 @@ const initialState: MarketplaceState = { page: 1, limit: 3, }, - history: { - data: [], - count: 0, - limit: 3, - page: 1, - }, + history: [] as History[], + historyCount: 0, newestCollections: [] as ICollection[], selectedCollection: {} as ICollection, isLoadingCollections: false, @@ -133,11 +124,13 @@ const marketplaceSlice = createSlice({ name: 'marketplace', initialState, reducers: { - setMarketplaceFilters: (state, { payload }) => { + setFilters: (state, { payload }) => { state.filters = payload; }, - resetMarketplaceFilters: (state) => { + resetFilters: (state) => { state.filters = initialState.filters; + state.items = initialState.items; + state.history = initialState.history; }, setSelectedItem: (state, { payload }) => { state.selectedItem = payload; @@ -151,15 +144,6 @@ const marketplaceSlice = createSlice({ setCollectionsFilters: (state, { payload }) => { state.collectionsFilters = payload; }, - resetCollectionsFilters: (state) => { - state.collectionsFilters = initialState.collectionsFilters; - }, - setCollectionsNftsFilters: (state, { payload }) => { - state.filters = payload; - }, - resetCollectionsNftFilters: (state) => { - state.filters = initialState.filters; - }, setSelectedCollection: (state, { payload }) => { state.selectedCollection = payload; }, @@ -317,8 +301,8 @@ const marketplaceSlice = createSlice({ state.isLoading = false; state.isLoadingHistory = false; - state.history.data = [...state.history.data, ...payload.history]; - state.history.count = payload.count; + state.history = [...state.history, ...payload.history]; + state.historyCount = payload.count; }); builder.addCase(hideItem.fulfilled, (state, { payload }) => { @@ -368,7 +352,6 @@ const marketplaceSlice = createSlice({ state.isPagingLoading = true; }); builder.addCase(findPagedCollectionsNfts.fulfilled, (state, { payload }) => { - state.filters.limit = payload.limit; state.filters.page = payload.page; state.itemsCount = payload.totalCount; state.items = [...state.items, ...payload.items]; @@ -401,7 +384,7 @@ const marketplaceSlice = createSlice({ builder.addCase(findPagedCollections.rejected, (state) => { state.isLoadingCollections = false; }); - // get latets collections + // get latests collections builder.addCase(getNewestCollections.pending, (state) => { state.isLoadingCollections = true; }); @@ -425,8 +408,7 @@ const marketplaceSlice = createSlice({ }); }, }); - -export const selectNFTsMarketplace = (state: RootState) => state.marketplace; +export const selectMarketplaceState = (state: RootState) => state.marketplace; export const selectCanIList = (state: RootState) => { const { @@ -435,6 +417,7 @@ export const selectCanIList = (state: RootState) => { } = state; return selectedItem.owner?.address === address && selectedItem.status === ItemStatus.NotListed; }; + export const selectCanIDelist = (state: RootState) => { const { auth: { address }, @@ -468,8 +451,6 @@ export const selectIsOwner = (state: RootState) => { return address === selectedItem?.owner?.address; }; -export const selectMarketplaceState = (state: RootState) => state.marketplace; - export const selectIsLoadingWhileBuy = (state: RootState) => { const { marketplace, ledaNft } = state; @@ -481,15 +462,12 @@ export const selectIsLoadingWhileBuy = (state: RootState) => { }; export const { - setMarketplaceFilters, - resetMarketplaceFilters, + setFilters, + resetFilters, setSelectedItem, setIsModalOpen, setIsOpenPreviewProductModal, - resetCollectionsFilters, - setCollectionsNftsFilters, setCollectionsFilters, - resetCollectionsNftFilters, setSelectedCollection, } = marketplaceSlice.actions; diff --git a/src/layouts/header/NetworkNotice.tsx b/src/layouts/header/NetworkNotice.tsx index 7376e3f..835f468 100644 --- a/src/layouts/header/NetworkNotice.tsx +++ b/src/layouts/header/NetworkNotice.tsx @@ -1,9 +1,9 @@ import clsx from 'clsx'; -import { NetworkNames } from '../../common/enums/network-names.enum'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; +import { selectUiReducer, setIsNetworkAdviceOpen } from '@store/ui/ui.slice'; import useMetamask from '../../features/auth/hooks/useMetamask'; -import useAppDispatch from '../../store/hooks/useAppDispatch'; -import useAppSelector from '../../store/hooks/useAppSelector'; -import { selectUiReducer, setIsNetworkAdviceOpen } from '../../store/ui/ui.slice'; +import { NetworkNames } from '../../common/enums/network-names.enum'; const NETWORK_NAMES: { [key: string]: string } = { [NetworkNames.MAINNET]: 'Mainnet', diff --git a/src/layouts/header/index.tsx b/src/layouts/header/index.tsx index 40333a7..ca8b951 100644 --- a/src/layouts/header/index.tsx +++ b/src/layouts/header/index.tsx @@ -8,11 +8,11 @@ import BurgerButton from '@ui/burger-button'; import Button from '@ui/button'; import clsx from 'clsx'; import { useRouter } from 'next/router'; +import useAppSelector from '@store/hooks/useAppSelector'; import headerData from '../../data/general/header-01.json'; import menuData from '../../data/general/menu-01.json'; import useMetamask from '../../features/auth/hooks/useMetamask'; import { selectAuthState } from '../../features/auth/store/auth.slice'; -import useAppSelector from '../../store/hooks/useAppSelector'; import { NetworkNotice } from './NetworkNotice'; type Props = { diff --git a/src/layouts/wrapper.tsx b/src/layouts/wrapper.tsx index 088a5c6..5c1ced6 100644 --- a/src/layouts/wrapper.tsx +++ b/src/layouts/wrapper.tsx @@ -4,11 +4,11 @@ import Header from '@layout/header'; import ScrollToTop from '@ui/scroll-to-top'; import { useTheme } from 'next-themes'; import { useEffect } from 'react'; -import { selectAuthState, setIsMainnet } from '../features/auth/store/auth.slice'; -import useAppDispatch from '../store/hooks/useAppDispatch'; +import useAppDispatch from '@store/hooks/useAppDispatch'; +import useAppSelector from '@store/hooks/useAppSelector'; +import { setIsNetworkAdviceOpen } from '@store/ui/ui.slice'; import { findLikedItemsByAccount } from '../features/account/store/account.actions'; -import useAppSelector from '../store/hooks/useAppSelector'; -import { setIsNetworkAdviceOpen } from '../store/ui/ui.slice'; +import { selectAuthState, setIsMainnet } from '../features/auth/store/auth.slice'; import { NetworkNames } from '../common/enums/network-names.enum'; import useMetamask from '../features/auth/hooks/useMetamask'; import NetworkRequestModal from '../components/modals/network-request-modal/network-request.modal'; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 43ef6c5..1b1c61c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -14,6 +14,7 @@ import type { AppProps as NextAppProps } from 'next/app'; import Wrapper from '@layout/wrapper'; import store from '../store'; // modified version - allows for custom pageProps type, falling back to 'any' +// eslint-disable-next-line @typescript-eslint/no-explicit-any type AppProps

    = { pageProps: P; } & Omit, 'pageProps'>; diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 7c20df1..3ccf688 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import Document, { Html, Head, Main, NextScript } from 'next/document'; class MyDocument extends Document { @@ -14,6 +15,7 @@ class MyDocument extends Document {