From 3a0a8af0901c4855df2b37f324ba83c7c05fea0c Mon Sep 17 00:00:00 2001 From: yu23ki14 Date: Tue, 10 Dec 2024 00:16:59 +0900 Subject: [PATCH] make role parser --- pkgs/frontend/app/components/BasicRole.tsx | 19 +++++++ .../app/components/color-mode-toggle.tsx | 15 ------ .../components/common/HatsListItemParser.tsx | 50 +++++++++++++++++++ .../frontend/app/components/icon/RoleIcon.tsx | 37 ++++++++++++++ pkgs/frontend/app/routes/$treeId.tsx | 29 +++++++++++ pkgs/frontend/hooks/useHats.ts | 46 +++++++++++------ pkgs/frontend/package.json | 1 - pkgs/frontend/types/hats.ts | 20 ++++++++ pkgs/frontend/utils/ipfs.ts | 2 +- yarn.lock | 5 -- 10 files changed, 188 insertions(+), 36 deletions(-) create mode 100644 pkgs/frontend/app/components/BasicRole.tsx delete mode 100644 pkgs/frontend/app/components/color-mode-toggle.tsx create mode 100644 pkgs/frontend/app/components/common/HatsListItemParser.tsx create mode 100644 pkgs/frontend/app/components/icon/RoleIcon.tsx create mode 100644 pkgs/frontend/app/routes/$treeId.tsx create mode 100644 pkgs/frontend/types/hats.ts diff --git a/pkgs/frontend/app/components/BasicRole.tsx b/pkgs/frontend/app/components/BasicRole.tsx new file mode 100644 index 0000000..7dfd4c9 --- /dev/null +++ b/pkgs/frontend/app/components/BasicRole.tsx @@ -0,0 +1,19 @@ +import { Box, Image, Text } from "@chakra-ui/react"; +import { FC } from "react"; +import { HatsDetailSchama } from "types/hats"; + +interface BasicRoleProps { + detail?: HatsDetailSchama; + imageUri?: string; +} + +export const BasicRole: FC = (params?) => { + const { detail, imageUri } = params!; + + return ( + + {detail?.data.name} + + + ); +}; diff --git a/pkgs/frontend/app/components/color-mode-toggle.tsx b/pkgs/frontend/app/components/color-mode-toggle.tsx deleted file mode 100644 index 36d9723..0000000 --- a/pkgs/frontend/app/components/color-mode-toggle.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { IconButton } from "@chakra-ui/react"; -import { Moon, Sun } from "lucide-react"; -import { useTheme } from "next-themes"; - -export function ColorModeToggle() { - const { theme, setTheme } = useTheme(); - const toggleColorMode = () => { - setTheme(theme === "light" ? "dark" : "light"); - }; - return ( - - {theme === "light" ? : } - - ); -} diff --git a/pkgs/frontend/app/components/common/HatsListItemParser.tsx b/pkgs/frontend/app/components/common/HatsListItemParser.tsx new file mode 100644 index 0000000..7864f2a --- /dev/null +++ b/pkgs/frontend/app/components/common/HatsListItemParser.tsx @@ -0,0 +1,50 @@ +import { + Children, + cloneElement, + FC, + isValidElement, + ReactNode, + useEffect, + useState, +} from "react"; +import { ipfs2https } from "utils/ipfs"; +import { HatsDetailSchama } from "types/hats"; +import axios from "axios"; + +interface HatsListItemParserProps { + children: ReactNode; + detailUri?: string; + imageUri?: string; +} + +export const HatsListItemParser: FC = (props) => { + const parsedImageUri = props.imageUri + ? ipfs2https(props.imageUri) + : props.imageUri; + const parsedDetailUri = props.detailUri + ? ipfs2https(props.detailUri) + : props.detailUri; + + const [detail, setDetail] = useState(); + + useEffect(() => { + if (!parsedDetailUri) return; + + const fetch = async () => { + const { data } = await axios.get(parsedDetailUri); + setDetail(data); + }; + + fetch(); + }, [parsedDetailUri]); + + return ( + <> + {Children.map(props.children, (child) => + isValidElement(child) + ? cloneElement(child, { imageUri: parsedImageUri, detail } as any) + : child + )} + + ); +}; diff --git a/pkgs/frontend/app/components/icon/RoleIcon.tsx b/pkgs/frontend/app/components/icon/RoleIcon.tsx new file mode 100644 index 0000000..781d530 --- /dev/null +++ b/pkgs/frontend/app/components/icon/RoleIcon.tsx @@ -0,0 +1,37 @@ +import { FaCircle, FaPeopleGroup } from "react-icons/fa6"; +import { CommonIcon } from "../common/CommonIcon"; +import { useEffect, useState } from "react"; +import { ipfs2https } from "utils/ipfs"; + +interface RoleIconProps { + roleImageUrl?: string; + size?: number | "full"; +} + +export const RoleIcon = ({ roleImageUrl, size = "full" }: RoleIconProps) => { + const [imageUrl, setImageUrl] = useState(); + + useEffect(() => { + if (roleImageUrl?.includes("ipfs://")) { + setImageUrl(ipfs2https(roleImageUrl)); + } else { + setImageUrl(roleImageUrl); + } + }, [roleImageUrl]); + + return ( + + } + /> + ); +}; diff --git a/pkgs/frontend/app/routes/$treeId.tsx b/pkgs/frontend/app/routes/$treeId.tsx new file mode 100644 index 0000000..65aa046 --- /dev/null +++ b/pkgs/frontend/app/routes/$treeId.tsx @@ -0,0 +1,29 @@ +import { Box } from "@chakra-ui/react"; +import { useParams } from "@remix-run/react"; +import { useTreeInfo } from "hooks/useHats"; +import { FC } from "react"; +import { BasicRole } from "~/components/BasicRole"; +import { HatsListItemParser } from "~/components/common/HatsListItemParser"; + +const WorkspaceTop: FC = () => { + const { treeId } = useParams(); + + const tree = useTreeInfo(Number(treeId)); + + return ( + // filterでlevelAtLocalTreeが0以上のものだけを表示にしているが、実際には2以上のものだけを表示される + + {tree?.hats + ?.filter((h) => Number(h.levelAtLocalTree) >= 0) + .map((h) => ( + ) as any} + /> + ))} + + ); +}; + +export default WorkspaceTop; diff --git a/pkgs/frontend/hooks/useHats.ts b/pkgs/frontend/hooks/useHats.ts index 0f7b2e4..a67fd3d 100644 --- a/pkgs/frontend/hooks/useHats.ts +++ b/pkgs/frontend/hooks/useHats.ts @@ -1,12 +1,12 @@ import { hatIdToTreeId } from "@hatsprotocol/sdk-v1-core"; -import { Hat, HatsSubgraphClient } from "@hatsprotocol/sdk-v1-subgraph"; +import { Hat, HatsSubgraphClient, Tree } from "@hatsprotocol/sdk-v1-subgraph"; import { HATS_ABI } from "abi/hats"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { Address, decodeEventLog, encodeFunctionData } from "viem"; import { base, optimism, sepolia } from "viem/chains"; import { HATS_ADDRESS } from "./useContracts"; import { useSmartAccountClient } from "./useWallet"; -import { publicClient } from "./useViem"; +import { currentChain, publicClient } from "./useViem"; // ############################################################### // Read with subgraph @@ -30,6 +30,30 @@ export const hatsSubgraphClient = new HatsSubgraphClient({ }, }); +export const useTreeInfo = (treeId: number) => { + const [treeInfo, setTreeInfo] = useState(); + + const { getTreeInfo } = useHats(); + + useEffect(() => { + const fetch = async () => { + if (!treeId) return; + const tree = await getTreeInfo({ + chainId: currentChain.id, + treeId: treeId, + }); + + if (!tree) return; + + setTreeInfo(tree); + }; + + fetch(); + }, [treeId]); + + return treeInfo; +}; + /** * Hats 向けの React Hooks * @returns @@ -46,8 +70,6 @@ export const useHats = () => { */ const getTreeInfo = useCallback( async (params: { chainId: number; treeId: number }) => { - if (!smartAccountClient) return; - setIsLoading(true); try { @@ -80,7 +102,7 @@ export const useHats = () => { setIsLoading(false); } }, - [smartAccountClient] + [] ); /** @@ -90,8 +112,6 @@ export const useHats = () => { */ const getWearersInfo = useCallback( async (params: { chainId: number; hatId: string }) => { - if (!smartAccountClient) return; - setIsLoading(true); try { @@ -111,7 +131,7 @@ export const useHats = () => { setIsLoading(false); } }, - [smartAccountClient] + [] ); /** @@ -121,7 +141,7 @@ export const useHats = () => { */ const getWearerInfo = useCallback( async (params: { chainId: number; walletAddress: string }) => { - if (!smartAccountClient) return; + if (!params.walletAddress) return; setIsLoading(true); @@ -156,7 +176,7 @@ export const useHats = () => { setIsLoading(false); } }, - [smartAccountClient] + [] ); /** @@ -164,8 +184,6 @@ export const useHats = () => { */ const getHatsTimeframeModuleAddress = useCallback( async (params: { chainId: number; hatId: string }) => { - if (!smartAccountClient) return; - setIsLoading(true); try { @@ -202,7 +220,7 @@ export const useHats = () => { setIsLoading(false); } }, - [smartAccountClient] + [] ); /** diff --git a/pkgs/frontend/package.json b/pkgs/frontend/package.json index 454836d..544bc75 100644 --- a/pkgs/frontend/package.json +++ b/pkgs/frontend/package.json @@ -27,7 +27,6 @@ "@solana/web3.js": "^1.95.5", "axios": "^1.7.9", "isbot": "^5.1.11", - "lucide-react": "^0.399.0", "namestone-sdk": "^0.2.11", "next-themes": "^0.4.3", "permissionless": "^0.2.20", diff --git a/pkgs/frontend/types/hats.ts b/pkgs/frontend/types/hats.ts new file mode 100644 index 0000000..ae584cc --- /dev/null +++ b/pkgs/frontend/types/hats.ts @@ -0,0 +1,20 @@ +export interface HatsDetailSchama { + type: "1.0"; + data: { + name: string; + description?: string; + responsabilities?: { + label: string; + description?: string | undefined; + link?: string | undefined; + imageUrl?: string | undefined; + }[]; + authorities?: { + label: string; + description?: string | undefined; + link?: string | undefined; + imageUrl?: string | undefined; + gate?: string | undefined; + }[]; + }; +} diff --git a/pkgs/frontend/utils/ipfs.ts b/pkgs/frontend/utils/ipfs.ts index 8797d59..5d59aef 100644 --- a/pkgs/frontend/utils/ipfs.ts +++ b/pkgs/frontend/utils/ipfs.ts @@ -53,5 +53,5 @@ export const ipfsUploadFile = async (file: File) => { export const ipfs2https = (ipfsUri: string) => { const { pinataGateway } = getPinataConfig(); const cid = ipfsUri.replace("ipfs://", ""); - return `${pinataGateway}/ipfs/${cid}`; + return `${pinataGateway}/ipfs/${cid}?pinataGatewayToken=M-iEBglWoUCZWJYsihe1IRrngs7HIGeIr3s5lObVw96hv7GTuCw1QrlmnNtwvuXt`; }; diff --git a/yarn.lock b/yarn.lock index 3addbee..f35dddc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12435,11 +12435,6 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== -lucide-react@^0.399.0: - version "0.399.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.399.0.tgz#cb072fd99fa751083cc08fba7266f1c7bff9e280" - integrity sha512-UyTNa3djBISdzL2UktgCrESXexQXaDQWi/WsDkbw6fBFfHlapajR58WoR+gxQ4laxfEyiHmoFrEIM3V+5XOVQg== - make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"