Skip to content

Commit 82ee50b

Browse files
committed
feat: profile page lists NFTs by wallet
1 parent 7a97f8d commit 82ee50b

30 files changed

+1082
-446
lines changed

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"lodash": "^4.17.21",
3939
"materialize-css": "^1.0.0-rc.2",
4040
"moment": "^2.29.1",
41+
"moralis": "^2.26.1",
4142
"next": "^14.2.1",
4243
"next-i18next": "^8.2.0",
4344
"react": "^18.2.0",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createContext } from "react";
2+
3+
import { Erc721ContextType } from "./Erc721Context.types";
4+
5+
export const Erc721Context = createContext<Erc721ContextType | undefined>(undefined);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { GetNFTsByWalletResult } from "api/evm/ERC721/types";
2+
import { ReactNode } from "react";
3+
4+
export type Erc721ContextControllerProps = {
5+
children: ReactNode;
6+
};
7+
8+
export type Erc721ContextType = {
9+
nftsByWallet: GetNFTsByWalletResult[][];
10+
getNFTsByWallet: () => Promise<void>;
11+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from "react";
2+
import { useAccount } from "wagmi";
3+
import axios from "axios";
4+
import { GetNFTsByWalletRequest, GetNFTsByWalletResult } from "api/evm/ERC721/types";
5+
import _ from "lodash";
6+
7+
import { useRoutes } from "hooks/useRoutes/useRoutes";
8+
9+
import { Erc721ContextControllerProps, Erc721ContextType } from "./Erc721Context.types";
10+
import { Erc721Context } from "./Erc721Context";
11+
12+
const groupNFTsByContract = (nfts: GetNFTsByWalletResult[]) => {
13+
const group = _.groupBy(nfts, "tokenAddress");
14+
15+
console.log(_.map(group));
16+
17+
return _.map(group);
18+
};
19+
20+
export const Erc721ContextController = ({ children }: Erc721ContextControllerProps) => {
21+
const [nftsByWallet, setNftsByWallet] = React.useState<Erc721ContextType["nftsByWallet"]>([]);
22+
23+
const { address, chainId } = useAccount();
24+
const routes = useRoutes();
25+
26+
const getNFTsByWallet = async () => {
27+
try {
28+
const data: GetNFTsByWalletRequest = {
29+
chainId: chainId!,
30+
walletAddress: address!,
31+
};
32+
33+
const result = await axios.post<GetNFTsByWalletResult[]>(routes.api.evm.ERC721.getNFTsByWallet(), {
34+
data,
35+
});
36+
37+
console.log(result.data);
38+
39+
setNftsByWallet(groupNFTsByContract(result.data));
40+
} catch (error) {
41+
console.error(error);
42+
}
43+
};
44+
45+
const props: Erc721ContextType = {
46+
getNFTsByWallet,
47+
nftsByWallet,
48+
};
49+
50+
return <Erc721Context.Provider value={props}>{children}</Erc721Context.Provider>;
51+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { useContext } from "react";
2+
3+
import { Erc721Context } from "./Erc721Context";
4+
5+
export const useErc721Context = () => {
6+
const context = useContext(Erc721Context);
7+
8+
if (context === undefined) {
9+
throw new Error("useErc721Context must be used within a Erc721Context");
10+
}
11+
12+
return context;
13+
};

app/src/hooks/useRoutes/useRoutes.tsx

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,14 @@
1-
type RouteMap = {
2-
home: () => string;
3-
market: {
4-
price: (args: { marketId: string }) => string;
5-
};
6-
api: {
7-
promptWars: {
8-
createGuestAccount: () => string;
9-
create: () => string;
10-
reveal: () => string;
11-
resolve: () => string;
12-
};
13-
chat: {
14-
dropboxESign: () => string;
15-
openai: {
16-
completionsAPI: () => string;
17-
assistantsAPI: () => string;
18-
};
19-
googleai: {
20-
completionsAPI: () => string;
21-
};
22-
};
23-
};
24-
dashboard: {
25-
latestTrends: () => string;
26-
promptWars: {
27-
home: () => string;
28-
previousMarkets: () => string;
29-
market: (args: { marketId: string }) => string;
30-
};
31-
market: (args: { marketId: string }) => string;
32-
};
33-
};
34-
35-
export const routes: RouteMap = {
1+
export const routes = {
362
home: () => `/`,
37-
market: {
38-
price: ({ marketId }) => `/market/price/${marketId}`,
3+
profile: {
4+
index: () => `/profile`,
395
},
406
api: {
7+
evm: {
8+
ERC721: {
9+
getNFTsByWallet: () => `/api/evm/ERC721/get-nfts-by-wallet`,
10+
},
11+
},
4112
promptWars: {
4213
createGuestAccount: () => `/api/prompt-wars/create-guest-account`,
4314
create: () => `/api/prompt-wars/create`,
@@ -55,15 +26,6 @@ export const routes: RouteMap = {
5526
},
5627
},
5728
},
58-
dashboard: {
59-
latestTrends: () => `/`,
60-
promptWars: {
61-
home: () => `/`,
62-
previousMarkets: () => `/previous-rounds`,
63-
market: ({ marketId }) => `/${marketId}`,
64-
},
65-
market: ({ marketId }) => `/market/${marketId}`,
66-
},
6729
};
6830

69-
export const useRoutes: () => RouteMap = () => routes;
31+
export const useRoutes = () => routes;

app/src/layouts/home-layout/HomeLayout.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ToastContextController } from "context/toast/ToastContextController";
88
import { ThemeContextController } from "context/theme/ThemeContextController";
99
import { EvmWalletSelectorContextController } from "context/evm/wallet-selector/EvmWalletSelectorContextController";
1010
import { Footer } from "ui/footer/Footer";
11+
import { Erc721ContextController } from "context/evm/ERC721/Erc721ContextController";
1112

1213
import { ChatLayoutProps } from "./HomeLayout.types";
1314
import styles from "./HomeLayout.module.scss";
@@ -27,17 +28,19 @@ export const HomeLayout: React.FC<ChatLayoutProps> = ({ children }) => {
2728
</Head>
2829
<ThemeContextController>
2930
<EvmWalletSelectorContextController>
30-
<ToastContextController>
31-
<div id="modal-root" />
32-
<div id="dropdown-portal" />
33-
<div className={clsx(styles["home-layout"])}>
34-
<Navbar />
31+
<Erc721ContextController>
32+
<ToastContextController>
33+
<div id="modal-root" />
34+
<div id="dropdown-portal" />
35+
<div className={clsx(styles["home-layout"])}>
36+
<Navbar />
3537

36-
<MainPanel>{children}</MainPanel>
38+
<MainPanel>{children}</MainPanel>
3739

38-
<Footer />
39-
</div>
40-
</ToastContextController>
40+
<Footer />
41+
</div>
42+
</ToastContextController>
43+
</Erc721ContextController>
4144
</EvmWalletSelectorContextController>
4245
</ThemeContextController>
4346
</>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { NextApiRequest, NextApiResponse } from "next";
2+
3+
import logger from "providers/logger";
4+
import moralis from "providers/moralis";
5+
6+
export default async function Fn(request: NextApiRequest, response: NextApiResponse) {
7+
try {
8+
await moralis.loadClient();
9+
} catch (error) {
10+
logger.error(error);
11+
}
12+
13+
try {
14+
const { data } = request.body;
15+
logger.info(`api/evm/ERC721/get-nfts-by-wallet: ${JSON.stringify(data)}`);
16+
17+
const result = await moralis.client.EvmApi.nft.getWalletNFTs({
18+
chain: data.chainId,
19+
format: "decimal",
20+
mediaItems: false,
21+
address: data.walletAddress,
22+
});
23+
24+
response.status(200).json(result.result);
25+
} catch (error) {
26+
logger.error(error);
27+
28+
response.status(500).json({ error: (error as Error).message });
29+
}
30+
}

app/src/pages/api/evm/ERC721/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { EvmNft } from "@moralisweb3/common-evm-utils/lib";
2+
import { MoralisDataObjectValue } from "@moralisweb3/common-core/lib";
3+
4+
export type GetNFTsByWalletResult = EvmNft & {
5+
metadata: MoralisDataObjectValue & Record<string, any>;
6+
};
7+
8+
export type GetNFTsByWalletRequest = {
9+
walletAddress: string;
10+
chainId: number;
11+
};

app/src/pages/profile/index.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { GetServerSidePropsContext, NextPage } from "next";
2+
import { i18n, useTranslation } from "next-i18next";
3+
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
4+
import Head from "next/head";
5+
6+
import { HomeLayout } from "layouts/home-layout/HomeLayout";
7+
import { Collections } from "ui/lease721/profile/collections/Collections";
8+
9+
const Index: NextPage = () => {
10+
const { t } = useTranslation("head");
11+
12+
return (
13+
<HomeLayout>
14+
<Head>
15+
<title>{t("head.og.title")}</title>
16+
<meta name="description" content={t("head.og.description")} />
17+
<meta property="og:title" content={t("head.og.title")} />
18+
<meta property="og:description" content={t("head.og.description")} />
19+
<meta property="og:url" content="https://lease721.com/" />
20+
</Head>
21+
22+
<Collections />
23+
</HomeLayout>
24+
);
25+
};
26+
27+
export const getServerSideProps = async ({ locale }: GetServerSidePropsContext) => {
28+
await i18n?.reloadResources();
29+
30+
return {
31+
props: {
32+
...(await serverSideTranslations(locale!, ["common", "head", "chat", "prompt-wars"])),
33+
},
34+
};
35+
};
36+
37+
export default Index;

0 commit comments

Comments
 (0)