diff --git a/pkgs/frontend/app/components/ContentContainer.tsx b/pkgs/frontend/app/components/ContentContainer.tsx
new file mode 100644
index 00000000..874e4dda
--- /dev/null
+++ b/pkgs/frontend/app/components/ContentContainer.tsx
@@ -0,0 +1,18 @@
+import { Box } from "@chakra-ui/react";
+
+export const ContentContainer = ({
+ children,
+}: {
+ children: React.ReactNode;
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/pkgs/frontend/app/components/Header.tsx b/pkgs/frontend/app/components/Header.tsx
index 6cb69869..afb50cb9 100644
--- a/pkgs/frontend/app/components/Header.tsx
+++ b/pkgs/frontend/app/components/Header.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect, useMemo } from "react";
+import { useState, useEffect, useMemo, FC } from "react";
import { Box, Flex, Text } from "@chakra-ui/react";
import { WorkspaceIcon } from "./icon/WorkspaceIcon";
import { UserIcon } from "./icon/UserIcon";
@@ -14,7 +14,7 @@ import axios from "axios";
import { HatsDetailSchama } from "types/hats";
import { abbreviateAddress } from "utils/wallet";
-const NO_HEADER_PATHS: string[] = ["/login", "/signup"]; // 適宜ヘッダーが不要なページのパスを追加
+const NO_HEADER_PATHS: string[] = ["/login", "/signup", "/"]; // 適宜ヘッダーが不要なページのパスを追加
const WORKSPACES_PATHS: string[] = ["/workspace", "/workspace/new"]; // 適宜ワークスペースが未選択な状態のページのパスを追加
const headerTextStyle = {
@@ -28,7 +28,7 @@ enum HeaderType {
WorkspaceAndUserIcons = "WorkspaceAndUserIcons",
}
-export const Header = () => {
+export const Header: FC = () => {
const [headerType, setHeaderType] = useState(
HeaderType.NonHeader
);
diff --git a/pkgs/frontend/app/components/RoleAttributesList.tsx b/pkgs/frontend/app/components/RoleAttributesList.tsx
new file mode 100644
index 00000000..d81456ba
--- /dev/null
+++ b/pkgs/frontend/app/components/RoleAttributesList.tsx
@@ -0,0 +1,44 @@
+import { FC } from "react";
+import { Box, Text } from "@chakra-ui/react";
+import { HatsDetailsAttributes } from "types/hats";
+import { EditRoleAttributeDialog } from "~/components/roleAttributeDialog/EditRoleAttributeDialog";
+
+export const RoleAttributesList: FC<{
+ items: HatsDetailsAttributes;
+ setItems: (value: HatsDetailsAttributes) => void;
+}> = ({ items, setItems }) => {
+ return (
+
+ {items.map((_, index) => (
+
+
+ {items[index]?.label}
+
+
+
+
+
+ ))}
+
+ );
+};
diff --git a/pkgs/frontend/app/components/common/CommonInput.tsx b/pkgs/frontend/app/components/common/CommonInput.tsx
index a92e650c..721a8f43 100644
--- a/pkgs/frontend/app/components/common/CommonInput.tsx
+++ b/pkgs/frontend/app/components/common/CommonInput.tsx
@@ -2,9 +2,9 @@ import { Input, InputProps } from "@chakra-ui/react";
import { FC } from "react";
interface CommonInputProps extends Omit {
- minHeight?: string;
+ minHeight?: InputProps["minHeight"];
value: string | number;
- onChange: (event: React.ChangeEvent) => void;
+ onChange?: (event: React.ChangeEvent) => void;
}
export const CommonInput: FC = ({
@@ -16,6 +16,7 @@ export const CommonInput: FC = ({
}: CommonInputProps) => {
return (
{
- minHeight?: string;
+ minHeight?: TextareaProps["minHeight"];
value: string;
onChange: (event: React.ChangeEvent) => void;
}
diff --git a/pkgs/frontend/app/components/input/InputDescription.tsx b/pkgs/frontend/app/components/input/InputDescription.tsx
new file mode 100644
index 00000000..60181b5d
--- /dev/null
+++ b/pkgs/frontend/app/components/input/InputDescription.tsx
@@ -0,0 +1,22 @@
+import { Box, BoxProps } from "@chakra-ui/react";
+import { CommonTextArea } from "../common/CommonTextarea";
+
+export const InputDescription = ({
+ description,
+ setDescription,
+ ...boxProps
+}: {
+ description: string;
+ setDescription: (description: string) => void;
+} & BoxProps) => {
+ return (
+
+ setDescription(e.target.value)}
+ />
+
+ );
+};
diff --git a/pkgs/frontend/app/components/input/InputImage.tsx b/pkgs/frontend/app/components/input/InputImage.tsx
new file mode 100644
index 00000000..9292e826
--- /dev/null
+++ b/pkgs/frontend/app/components/input/InputImage.tsx
@@ -0,0 +1,71 @@
+import { Box, Input, Text } from "@chakra-ui/react";
+import { CommonIcon } from "../common/CommonIcon";
+import { HiOutlinePlus } from "react-icons/hi2";
+
+const EmptyImage = () => {
+ return (
+
+
+
+
+ 画像を選択
+
+ );
+};
+
+export const InputImage = ({
+ imageFile,
+ setImageFile,
+ previousImageUrl,
+}: {
+ imageFile: File | null;
+ setImageFile: (file: File | null) => void;
+ previousImageUrl?: string;
+}) => {
+ const imageUrl = imageFile
+ ? URL.createObjectURL(imageFile)
+ : previousImageUrl
+ ? previousImageUrl
+ : undefined;
+
+ return (
+
+ {
+ const file = e.target.files?.[0];
+ if (file && file.type.startsWith("image/")) {
+ setImageFile(file);
+ } else {
+ alert("画像ファイルを選択してください");
+ }
+ }}
+ />
+ }
+ size={200}
+ borderRadius="3xl"
+ />
+
+ );
+};
diff --git a/pkgs/frontend/app/components/input/InputLink.tsx b/pkgs/frontend/app/components/input/InputLink.tsx
new file mode 100644
index 00000000..e2becee5
--- /dev/null
+++ b/pkgs/frontend/app/components/input/InputLink.tsx
@@ -0,0 +1,20 @@
+import { Box, BoxProps } from "@chakra-ui/react";
+import { CommonInput } from "../common/CommonInput";
+
+interface InputLinkProps extends BoxProps {
+ link: string;
+ setLink: (link: string) => void;
+}
+
+export const InputLink = ({ link, setLink, ...boxProps }: InputLinkProps) => {
+ return (
+
+ setLink(e.target.value)}
+ />
+
+ );
+};
diff --git a/pkgs/frontend/app/components/input/InputName.tsx b/pkgs/frontend/app/components/input/InputName.tsx
new file mode 100644
index 00000000..9d76526e
--- /dev/null
+++ b/pkgs/frontend/app/components/input/InputName.tsx
@@ -0,0 +1,23 @@
+import { Box, BoxProps } from "@chakra-ui/react";
+import { CommonInput } from "../common/CommonInput";
+
+export const InputName = ({
+ name,
+ setName,
+ ...boxProps
+}: {
+ name: string;
+ setName: (name: string) => void;
+} & BoxProps) => {
+ return (
+
+ setName(e.target.value)}
+ w="100%"
+ />
+
+ );
+};
diff --git a/pkgs/frontend/app/components/roleAttributeDialog/AddRoleAttributeDialog.tsx b/pkgs/frontend/app/components/roleAttributeDialog/AddRoleAttributeDialog.tsx
new file mode 100644
index 00000000..d87e8031
--- /dev/null
+++ b/pkgs/frontend/app/components/roleAttributeDialog/AddRoleAttributeDialog.tsx
@@ -0,0 +1,47 @@
+import { Button } from "@chakra-ui/react";
+import { DialogTrigger } from "../ui/dialog";
+import { BaseRoleAttributeDialog } from "./BaseRoleAttributeDialog";
+import { HatsDetailsAttributes } from "types/hats";
+
+const PlusButton = () => {
+ return (
+
+
+
+ );
+};
+
+interface AddRoleAttributeDialogProps {
+ type: "responsibility" | "authority";
+ attributes: HatsDetailsAttributes;
+ setAttributes: (attributes: HatsDetailsAttributes) => void;
+}
+
+export const AddRoleAttributeDialog = ({
+ type,
+ attributes,
+ setAttributes,
+}: AddRoleAttributeDialogProps) => {
+ const onClick = (name: string, description: string, link: string) => {
+ setAttributes([...attributes, { label: name, description, link }]);
+ };
+
+ return (
+ <>
+ }
+ onClick={onClick}
+ />
+ >
+ );
+};
diff --git a/pkgs/frontend/app/components/roleAttributeDialog/BaseRoleAttributeDialog.tsx b/pkgs/frontend/app/components/roleAttributeDialog/BaseRoleAttributeDialog.tsx
new file mode 100644
index 00000000..29d24787
--- /dev/null
+++ b/pkgs/frontend/app/components/roleAttributeDialog/BaseRoleAttributeDialog.tsx
@@ -0,0 +1,156 @@
+import { Box, VStack, Button } from "@chakra-ui/react";
+import {
+ DialogRoot,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogBody,
+ DialogFooter,
+ DialogActionTrigger,
+} from "../ui/dialog";
+import { InputName } from "../input/InputName";
+import { InputDescription } from "../input/InputDescription";
+import { InputLink } from "../input/InputLink";
+import { useEffect, useState } from "react";
+import { HatsDetailsAttributes } from "types/hats";
+
+const BUTTON_TEXT_MAP = {
+ add: "Add",
+ edit: "Save",
+} as const;
+
+const DIALOG_TITLE_MAP = {
+ responsibility: {
+ add: "Add a responsibility",
+ edit: "Edit responsibility",
+ },
+ authority: {
+ add: "Add an authority",
+ edit: "Edit authority",
+ },
+} as const;
+
+type RoleAttribute = HatsDetailsAttributes[number];
+
+interface BaseRoleAttributeDialogProps {
+ attribute?: RoleAttribute;
+ type: "responsibility" | "authority";
+ mode: "add" | "edit";
+ TriggerButton: React.ReactNode;
+ onClick: (name: string, description: string, link: string) => void;
+ onClickDelete?: () => void;
+}
+
+export const BaseRoleAttributeDialog = ({
+ attribute,
+ type,
+ mode,
+ TriggerButton,
+ onClick,
+ onClickDelete,
+}: BaseRoleAttributeDialogProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [name, setName] = useState(attribute?.label ?? "");
+ const [description, setDescription] = useState(attribute?.description ?? "");
+ const [link, setLink] = useState(attribute?.link ?? "");
+
+ const resetFormValues = () => {
+ setName("");
+ setDescription("");
+ setLink("");
+ };
+
+ const setAttribute = (attribute: RoleAttribute) => {
+ setName(attribute.label);
+ setDescription(attribute.description ?? "");
+ setLink(attribute.link ?? "");
+ };
+
+ useEffect(() => {
+ if (mode === "edit" && attribute) {
+ setAttribute(attribute);
+ }
+ }, [attribute, mode]);
+
+ return (
+ <>
+ {
+ setIsOpen(details.open);
+ if (!details.open) {
+ resetFormValues();
+ } else {
+ if (mode === "edit" && attribute) {
+ setAttribute(attribute);
+ }
+ }
+ }}
+ >
+ {TriggerButton}
+
+
+
+ {DIALOG_TITLE_MAP[type][mode]}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {mode === "edit" && onClickDelete && (
+
+
+
+ )}
+
+
+
+
+ >
+ );
+};
diff --git a/pkgs/frontend/app/components/roleAttributeDialog/EditRoleAttributeDialog.tsx b/pkgs/frontend/app/components/roleAttributeDialog/EditRoleAttributeDialog.tsx
new file mode 100644
index 00000000..30499b14
--- /dev/null
+++ b/pkgs/frontend/app/components/roleAttributeDialog/EditRoleAttributeDialog.tsx
@@ -0,0 +1,61 @@
+import { Button } from "@chakra-ui/react";
+import { DialogTrigger } from "../ui/dialog";
+import { BaseRoleAttributeDialog } from "./BaseRoleAttributeDialog";
+import { HatsDetailsAttributes } from "types/hats";
+import { GrEdit } from "react-icons/gr";
+
+const PencilButton = () => {
+ return (
+
+
+
+ );
+};
+
+interface EditRoleAttributeDialogProps {
+ type: "responsibility" | "authority";
+ attributes: HatsDetailsAttributes;
+ setAttributes: (attributes: HatsDetailsAttributes) => void;
+ attributeIndex: number;
+}
+
+export const EditRoleAttributeDialog = ({
+ type,
+ attributes,
+ setAttributes,
+ attributeIndex,
+}: EditRoleAttributeDialogProps) => {
+ const onClick = (name: string, description: string, link: string) => {
+ const newAttributes = [
+ ...attributes.slice(0, attributeIndex),
+ { ...attributes[attributeIndex], label: name, description, link },
+ ...attributes.slice(attributeIndex + 1),
+ ];
+ setAttributes(newAttributes);
+ };
+
+ const onClickDelete = () => {
+ setAttributes(attributes.filter((_, index) => index !== attributeIndex));
+ };
+
+ return (
+ <>
+ }
+ onClick={onClick}
+ onClickDelete={onClickDelete}
+ />
+ >
+ );
+};
diff --git a/pkgs/frontend/app/root.tsx b/pkgs/frontend/app/root.tsx
index 720fbe67..82876a7d 100644
--- a/pkgs/frontend/app/root.tsx
+++ b/pkgs/frontend/app/root.tsx
@@ -1,21 +1,21 @@
import { withEmotionCache } from "@emotion/react";
import {
- json,
+ data,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
-import { ApolloProvider } from "@apollo/client/react";
-import { Box, Container } from "@chakra-ui/react";
+import { ChakraProvider } from "./components/chakra-provider";
+import { useInjectStyles } from "./emotion/emotion-client";
import { PrivyProvider } from "@privy-io/react-auth";
+import { Header } from "./components/Header";
+import { ApolloProvider } from "@apollo/client/react";
+import { Container } from "@chakra-ui/react";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { goldskyClient } from "utils/apollo";
-import { ChakraProvider } from "./components/chakra-provider";
-import { Header } from "./components/Header";
import i18nServer, { localeCookie } from "./config/i18n.server";
-import { useInjectStyles } from "./emotion/emotion-client";
interface LayoutProps extends React.PropsWithChildren {}
@@ -23,7 +23,7 @@ export const handle = { i18n: ["translation"] };
export async function loader({ request }: LoaderFunctionArgs) {
const locale = await i18nServer.getLocale(request);
- return json(
+ return data(
{ locale },
{ headers: { "Set-Cookie": await localeCookie.serialize(locale) } }
);
diff --git a/pkgs/frontend/app/routes/$treeId.tsx b/pkgs/frontend/app/routes/$treeId._index.tsx
similarity index 100%
rename from pkgs/frontend/app/routes/$treeId.tsx
rename to pkgs/frontend/app/routes/$treeId._index.tsx
diff --git a/pkgs/frontend/app/routes/$treeId_.$hatId_.edit.tsx b/pkgs/frontend/app/routes/$treeId_.$hatId_.edit.tsx
new file mode 100644
index 00000000..8c319b39
--- /dev/null
+++ b/pkgs/frontend/app/routes/$treeId_.$hatId_.edit.tsx
@@ -0,0 +1,229 @@
+import { FC, useEffect, useState } from "react";
+import { useNavigate, useParams } from "@remix-run/react";
+import { Box, Text } from "@chakra-ui/react";
+import { InputImage } from "~/components/input/InputImage";
+import {
+ useUploadImageFileToIpfs,
+ useUploadHatsDetailsToIpfs,
+} from "hooks/useIpfs";
+import { ContentContainer } from "~/components/ContentContainer";
+import { InputName } from "~/components/input/InputName";
+import { InputDescription } from "~/components/input/InputDescription";
+import { BasicButton } from "~/components/BasicButton";
+import { useActiveWallet } from "hooks/useWallet";
+import { useHats } from "hooks/useHats";
+import {
+ HatsDetailsAttributes,
+ HatsDetailsAuthorities,
+ HatsDetailSchama,
+ HatsDetailsResponsabilities,
+} from "types/hats";
+import { AddRoleAttributeDialog } from "~/components/roleAttributeDialog/AddRoleAttributeDialog";
+import { ipfs2https, ipfs2httpsJson } from "utils/ipfs";
+import { Hat } from "@hatsprotocol/sdk-v1-subgraph";
+import { RoleAttributesList } from "~/components/RoleAttributesList";
+
+const SectionHeading: FC<{ children: React.ReactNode }> = ({ children }) => (
+ {children}
+);
+
+const EditRole: FC = () => {
+ const { treeId, hatId } = useParams();
+
+ const { uploadImageFileToIpfs, imageFile, setImageFile } =
+ useUploadImageFileToIpfs();
+
+ const [roleName, setRoleName] = useState("");
+ const [roleDescription, setRoleDescription] = useState("");
+
+ const [responsibilities, setResponsibilities] = useState<
+ NonNullable
+ >([]);
+
+ const [authorities, setAuthorities] = useState<
+ NonNullable
+ >([]);
+ const { wallet } = useActiveWallet();
+ const [isLoading, setIsLoading] = useState(false);
+ const { changeHatDetails, changeHatImageURI } = useHats();
+ const { uploadHatsDetailsToIpfs } = useUploadHatsDetailsToIpfs();
+ const navigate = useNavigate();
+ const { getHat } = useHats();
+ const [hat, setHat] = useState(undefined);
+ const [details, setDetails] = useState(
+ undefined
+ );
+
+ useEffect(() => {
+ const fetchHat = async () => {
+ if (!hatId) return;
+ const resHat = await getHat(hatId);
+ console.log("hat", resHat);
+ if (resHat && resHat !== hat) {
+ setHat(resHat);
+ }
+ };
+ fetchHat();
+ }, [hatId]);
+
+ useEffect(() => {
+ const setStates = async () => {
+ if (!hat) return;
+ const detailsJson: HatsDetailSchama = hat.details
+ ? await ipfs2httpsJson(hat.details)
+ : undefined;
+ console.log("detailsJson", detailsJson);
+ setDetails(detailsJson);
+ setRoleName(detailsJson?.data.name ?? "");
+ setRoleDescription(detailsJson?.data.description ?? "");
+ setResponsibilities(detailsJson?.data.responsabilities ?? []);
+ setAuthorities(detailsJson?.data.authorities ?? []);
+ };
+ setStates();
+ }, [hat]);
+
+ const areArraysEqual = (
+ arr1: HatsDetailsAttributes,
+ arr2: HatsDetailsAttributes
+ ) => {
+ if (arr1.length !== arr2.length) return false;
+ return JSON.stringify(arr1) === JSON.stringify(arr2);
+ };
+
+ const isChangedDetails = () => {
+ if (!details) return false;
+
+ return (
+ details.data.name !== roleName ||
+ details.data.description !== roleDescription ||
+ !areArraysEqual(details.data.responsabilities ?? [], responsibilities) ||
+ !areArraysEqual(details.data.authorities ?? [], authorities)
+ );
+ };
+
+ const changeDetails = async () => {
+ if (!hatId) return;
+
+ const isChanged = isChangedDetails();
+ if (!isChanged) return;
+
+ const resUploadHatsDetails = await uploadHatsDetailsToIpfs({
+ name: roleName,
+ description: roleDescription,
+ responsabilities: responsibilities,
+ authorities: authorities,
+ });
+ if (!resUploadHatsDetails)
+ throw new Error("Failed to upload metadata to ipfs");
+ const ipfsUri = resUploadHatsDetails.ipfsUri;
+ const parsedLog = await changeHatDetails({
+ hatId: BigInt(hatId),
+ newDetails: ipfsUri,
+ });
+ if (!parsedLog) throw new Error("Failed to change hat details");
+ console.log("parsedLog", parsedLog);
+ };
+
+ const changeImage = async () => {
+ if (!hatId || !hat || !imageFile) return;
+ const resUploadImage = await uploadImageFileToIpfs();
+ if (!resUploadImage) throw new Error("Failed to upload image to ipfs");
+ const ipfsUri = resUploadImage.ipfsUri;
+ const parsedLog = await changeHatImageURI({
+ hatId: BigInt(hatId),
+ newImageURI: ipfsUri,
+ });
+ if (!parsedLog) throw new Error("Failed to change hat image");
+ console.log("parsedLog", parsedLog);
+ };
+
+ const handleSubmit = async () => {
+ if (!wallet) {
+ alert("ウォレットを接続してください。");
+ return;
+ }
+ if (!roleName || !roleDescription) {
+ alert("全ての項目を入力してください。");
+ return;
+ }
+
+ try {
+ setIsLoading(true);
+
+ await Promise.all([changeDetails(), changeImage()]);
+
+ navigate(`/${treeId}/roles`);
+ } catch (error) {
+ console.error(error);
+ alert("エラーが発生しました。" + error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+ <>
+
+ ロールを編集
+
+
+
+
+
+ Responsibilities
+
+
+
+
+ Authorities
+
+
+
+
+
+
+ 保存
+
+
+
+ >
+ );
+};
+
+export default EditRole;
diff --git a/pkgs/frontend/app/routes/$treeId_.assistcredit-history.tsx b/pkgs/frontend/app/routes/$treeId_.assistcredit-history.tsx
new file mode 100644
index 00000000..6deafbc1
--- /dev/null
+++ b/pkgs/frontend/app/routes/$treeId_.assistcredit-history.tsx
@@ -0,0 +1,38 @@
+import { Box, Heading, List, Text } from "@chakra-ui/react";
+import { useParams } from "@remix-run/react";
+import { useGetTransferFractionTokens } from "hooks/useFractionToken";
+import { FC } from "react";
+import { abbreviateAddress } from "utils/wallet";
+import { StickyNav } from "~/components/StickyNav";
+
+const WorkspaceMember: FC = () => {
+ const { treeId } = useParams();
+
+ const { data } = useGetTransferFractionTokens({
+ where: {
+ workspaceId: treeId,
+ },
+ });
+
+ return (
+ <>
+ {/* Members */}
+
+ Transaction History
+
+ {data?.transferFractionTokens.map((token) => (
+
+
+ {abbreviateAddress(token.from)} → {abbreviateAddress(token.to)}{" "}
+ : {token.amount}
+
+
+ ))}
+
+
+
+ >
+ );
+};
+
+export default WorkspaceMember;
diff --git a/pkgs/frontend/app/routes/$treeId_.member.tsx b/pkgs/frontend/app/routes/$treeId_.member.tsx
index 20f2e921..a03475ac 100644
--- a/pkgs/frontend/app/routes/$treeId_.member.tsx
+++ b/pkgs/frontend/app/routes/$treeId_.member.tsx
@@ -135,7 +135,7 @@ const WorkspaceMember: FC = () => {
All Contributors
{assistantMembers.map((m, i) => (
-
+
= ({ children }) => (
+ {children}
+);
+
+const NewRole: FC = () => {
+ const { treeId } = useParams();
+
+ const { uploadImageFileToIpfs, imageFile, setImageFile } =
+ useUploadImageFileToIpfs();
+
+ const [roleName, setRoleName] = useState("");
+ const [roleDescription, setRoleDescription] = useState("");
+
+ const [responsibilities, setResponsibilities] = useState<
+ NonNullable
+ >([]);
+
+ const [authorities, setAuthorities] = useState<
+ NonNullable
+ >([]);
+ const { wallet } = useActiveWallet();
+ const [isLoading, setIsLoading] = useState(false);
+ const { createHat } = useHats();
+ const { uploadHatsDetailsToIpfs } = useUploadHatsDetailsToIpfs();
+ const { getTreeInfo } = useHats();
+ const navigate = useNavigate();
+
+ const handleSubmit = async () => {
+ if (!wallet) {
+ alert("ウォレットを接続してください。");
+ return;
+ }
+ if (!roleName || !roleDescription || !imageFile) {
+ alert("全ての項目を入力してください。");
+ return;
+ }
+
+ try {
+ setIsLoading(true);
+
+ const [resUploadHatsDetails, resUploadImage, treeInfo] =
+ await Promise.all([
+ uploadHatsDetailsToIpfs({
+ name: roleName,
+ description: roleDescription,
+ responsabilities: responsibilities,
+ authorities: authorities,
+ }),
+ uploadImageFileToIpfs(),
+ getTreeInfo({ treeId: Number(treeId) }),
+ ]);
+
+ if (!resUploadHatsDetails)
+ throw new Error("Failed to upload metadata to ipfs");
+ if (!resUploadImage) throw new Error("Failed to upload image to ipfs");
+
+ const hatterHatId = treeInfo?.hats?.[1]?.id;
+ if (!hatterHatId) throw new Error("Hat ID is required");
+
+ console.log("resUploadHatsDetails", resUploadHatsDetails);
+ console.log("resUploadImage", resUploadImage);
+ console.log("hatterHatId", hatterHatId);
+
+ const parsedLog = await createHat({
+ parentHatId: BigInt(hatterHatId),
+ details: resUploadHatsDetails?.ipfsUri,
+ imageURI: resUploadImage?.ipfsUri,
+ });
+ if (!parsedLog) throw new Error("Failed to create hat transaction");
+ console.log("parsedLog", parsedLog);
+
+ navigate(`/${treeId}/roles`);
+ } catch (error) {
+ console.error(error);
+ alert("エラーが発生しました。" + error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+ Responsibilities
+
+
+
+
+ Authorities
+
+
+
+
+
+
+ 作成
+
+
+
+ >
+ );
+};
+
+export default NewRole;
diff --git a/pkgs/frontend/app/routes/_index.tsx b/pkgs/frontend/app/routes/_index.tsx
index c94136f8..d629bb8a 100644
--- a/pkgs/frontend/app/routes/_index.tsx
+++ b/pkgs/frontend/app/routes/_index.tsx
@@ -1,11 +1,7 @@
-import { Box, Input } from "@chakra-ui/react";
+import { Box, Container, Heading, Image } from "@chakra-ui/react";
import type { MetaFunction } from "@remix-run/node";
-import { useBigBang } from "hooks/useBigBang";
-import {
- useUploadImageFileToIpfs,
- useUploadMetadataToIpfs,
-} from "hooks/useIpfs";
-import { CommonButton } from "~/components/common/CommonButton";
+import { Link } from "@remix-run/react";
+import CommonButton from "~/components/common/CommonButton";
export const meta: MetaFunction = () => {
return [
@@ -15,61 +11,44 @@ export const meta: MetaFunction = () => {
};
export default function Index() {
- const { bigbang, isLoading } = useBigBang();
- const { uploadMetadataToIpfs, isLoading: isUploadingMetadataToIpfs } =
- useUploadMetadataToIpfs();
- const {
- uploadImageFileToIpfs,
- setImageFile,
- isLoading: isUploadingImageFileToIpfs,
- } = useUploadImageFileToIpfs();
-
- const handleBigBang = async () => {
- const res = await bigbang({
- owner: "0xdCb93093424447bF4FE9Df869750950922F1E30B",
- topHatDetails: "Top Hat Details",
- topHatImageURI: "https://example.com/top-hat.png",
- hatterHatDetails: "Hatter Hat Details",
- hatterHatImageURI: "https://example.com/hatter-hat.png",
- trustedForwarder: "0x1234567890123456789012345678901234567890",
- });
-
- console.log(res);
- };
+ return (
+
+
+
+
+ いちばん簡単な
+
+ 貢献の記録と報酬の分配
+
+
+ Simplest way of contribution recording and rewards distribution.
+
+
- const metadata = {
- name: "Toban test",
- description: "Toban test",
- responsibilities: "Toban test",
- authorities: "Toban test",
- eligibility: true,
- toggle: true,
- };
+
+
+
+
- return (
-
-
- BigBang
-
- uploadMetadataToIpfs(metadata)}
- >
- Upload Metadata to IPFS
-
- ) =>
- setImageFile(e.target.files?.[0] || null)
- }
- />
-
- Upload Image File to IPFS
-
+
+
+
+ はじめる
+
+
+
);
}
diff --git a/pkgs/frontend/app/routes/api.namestone.$action.tsx b/pkgs/frontend/app/routes/api.namestone.$action.tsx
index 24926d70..99d8662b 100644
--- a/pkgs/frontend/app/routes/api.namestone.$action.tsx
+++ b/pkgs/frontend/app/routes/api.namestone.$action.tsx
@@ -1,4 +1,4 @@
-import { ActionFunction, LoaderFunction } from "@remix-run/node";
+import { ActionFunction, data, LoaderFunction } from "@remix-run/node";
import NameStone, { NameData } from "namestone-sdk";
const ns = new NameStone(import.meta.env.VITE_NAMESTONE_API_KEY);
@@ -11,32 +11,30 @@ export const loader: LoaderFunction = async ({ request, params }) => {
switch (action) {
case "resolve-names":
const addresses = searchParams.get("addresses");
- if (!addresses) return Response.json([]);
+ if (!addresses) return [];
const resolvedNames = await Promise.all(
addresses.split(",").map((address) => ns.getNames({ domain, address }))
);
- return Response.json(resolvedNames);
+ return resolvedNames;
case "resolve-addresses":
const names = searchParams.get("names");
- if (!names) return Response.json([]);
+ if (!names) return [];
const exactMatch = searchParams.get("exact_match");
const resolvedAddresses = await Promise.all(
- names
- .split(",")
- .map((name) =>
- ns.searchNames({
- domain,
- name,
- exact_match: exactMatch === "true" ? 1 : (0 as any),
- })
- )
+ names.split(",").map((name) =>
+ ns.searchNames({
+ domain,
+ name,
+ exact_match: exactMatch === "true" ? 1 : (0 as any),
+ })
+ )
);
- return Response.json(resolvedAddresses);
+ return resolvedAddresses;
default:
- throw Response.json({ message: "Not Found" }, { status: 404 });
+ throw data({ message: "Not Found" }, 404);
}
};
@@ -49,11 +47,11 @@ export const action: ActionFunction = async ({ request, params }) => {
case "set-name":
const { name, address, text_records } = await request.json();
await ns.setName({ domain, name, address, text_records });
- return Response.json({ message: "OK" });
+ return { message: "OK" };
default:
- throw Response.json({ message: "Not Found" }, { status: 404 });
+ throw data({ message: "Not Found" }, 404);
}
}
- throw Response.json({ message: "Not Found" }, { status: 404 });
+ throw data({ message: "Not Found" }, 404);
};
diff --git a/pkgs/frontend/app/routes/api_.locales.ts b/pkgs/frontend/app/routes/api_.locales.ts
index 0ee46f0a..ffc25f18 100644
--- a/pkgs/frontend/app/routes/api_.locales.ts
+++ b/pkgs/frontend/app/routes/api_.locales.ts
@@ -1,4 +1,4 @@
-import { type LoaderFunctionArgs } from "@remix-run/node";
+import { data, type LoaderFunctionArgs } from "@remix-run/node";
import { cacheHeader } from "pretty-cache-header";
import { z } from "zod";
import { resources } from "~/config/i18n";
@@ -44,5 +44,5 @@ export async function loader({ request }: LoaderFunctionArgs) {
);
}
- return Response.json(namespaces[ns], { headers });
+ return data(namespaces[ns], { headers });
}
diff --git a/pkgs/frontend/app/routes/workspace.new.tsx b/pkgs/frontend/app/routes/workspace.new.tsx
index 64ede700..7f7de86e 100644
--- a/pkgs/frontend/app/routes/workspace.new.tsx
+++ b/pkgs/frontend/app/routes/workspace.new.tsx
@@ -1,26 +1,25 @@
import { FC, useState } from "react";
-import { Box, Float, Grid, Input, Text } from "@chakra-ui/react";
-import { HiOutlinePlus } from "react-icons/hi2";
-import { CommonInput } from "~/components/common/CommonInput";
+import { Box, Grid } from "@chakra-ui/react";
import { BasicButton } from "~/components/BasicButton";
-import { CommonTextArea } from "~/components/common/CommonTextarea";
import {
- useUploadMetadataToIpfs,
+ useUploadHatsDetailsToIpfs,
useUploadImageFileToIpfs,
} from "hooks/useIpfs";
import { useNavigate } from "@remix-run/react";
-import { CommonIcon } from "~/components/common/CommonIcon";
import { useBigBang } from "hooks/useBigBang";
import { useActiveWallet } from "hooks/useWallet";
import { Address } from "viem";
import { hatIdToTreeId } from "@hatsprotocol/sdk-v1-core";
import { PageHeader } from "~/components/PageHeader";
+import { InputImage } from "~/components/input/InputImage";
+import { InputName } from "~/components/input/InputName";
+import { InputDescription } from "~/components/input/InputDescription";
const WorkspaceNew: FC = () => {
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [isLoading, setIsLoading] = useState(false);
- const { uploadMetadataToIpfs } = useUploadMetadataToIpfs();
+ const { uploadHatsDetailsToIpfs } = useUploadHatsDetailsToIpfs();
const { uploadImageFileToIpfs, imageFile, setImageFile } =
useUploadImageFileToIpfs();
const { bigbang } = useBigBang();
@@ -39,13 +38,11 @@ const WorkspaceNew: FC = () => {
setIsLoading(true);
try {
- const resUploadMetadata = await uploadMetadataToIpfs({
+ const resUploadMetadata = await uploadHatsDetailsToIpfs({
name,
description,
- responsibilities: "",
- authorities: "",
- eligibility: true,
- toggle: true,
+ responsabilities: [],
+ authorities: [],
});
if (!resUploadMetadata)
throw new Error("Failed to upload metadata to ipfs");
@@ -82,34 +79,6 @@ const WorkspaceNew: FC = () => {
}
};
- const EmptyImage = () => {
- return (
-
-
-
-
- 画像を選択
-
- );
- };
-
return (
@@ -121,43 +90,13 @@ const WorkspaceNew: FC = () => {
mt={10}
alignItems="center"
>
-
- {
- const file = e.target.files?.[0];
- if (file && file.type.startsWith("image/")) {
- setImageFile(file);
- } else {
- alert("画像ファイルを選択してください");
- }
- }}
- />
- }
- size={200}
- borderRadius="3xl"
- />
-
-
- setName(e.target.value)}
- />
-
-
- setDescription(e.target.value)}
- />
-
+
+
+
;
and?: InputMaybe>>;
+ blockNumber?: InputMaybe;
+ blockNumber_gt?: InputMaybe;
+ blockNumber_gte?: InputMaybe;
+ blockNumber_in?: InputMaybe>;
+ blockNumber_lt?: InputMaybe;
+ blockNumber_lte?: InputMaybe;
+ blockNumber_not?: InputMaybe;
+ blockNumber_not_in?: InputMaybe>;
+ blockTimestamp?: InputMaybe;
+ blockTimestamp_gt?: InputMaybe;
+ blockTimestamp_gte?: InputMaybe;
+ blockTimestamp_in?: InputMaybe>;
+ blockTimestamp_lt?: InputMaybe;
+ blockTimestamp_lte?: InputMaybe;
+ blockTimestamp_not?: InputMaybe;
+ blockTimestamp_not_in?: InputMaybe>;
hatId?: InputMaybe;
hatId_gt?: InputMaybe;
hatId_gte?: InputMaybe;
@@ -104,6 +122,8 @@ export type InitializedFractionToken_Filter = {
};
export enum InitializedFractionToken_OrderBy {
+ BlockNumber = 'blockNumber',
+ BlockTimestamp = 'blockTimestamp',
HatId = 'hatId',
Id = 'id',
Wearer = 'wearer',
@@ -261,6 +281,8 @@ export type SubscriptionWorkspacesArgs = {
export type TransferFractionToken = {
__typename?: 'TransferFractionToken';
amount: Scalars['BigInt']['output'];
+ blockNumber: Scalars['BigInt']['output'];
+ blockTimestamp: Scalars['BigInt']['output'];
from: Scalars['String']['output'];
hatId: Scalars['BigInt']['output'];
id: Scalars['ID']['output'];
@@ -282,6 +304,22 @@ export type TransferFractionToken_Filter = {
amount_not?: InputMaybe;
amount_not_in?: InputMaybe>;
and?: InputMaybe>>;
+ blockNumber?: InputMaybe;
+ blockNumber_gt?: InputMaybe;
+ blockNumber_gte?: InputMaybe;
+ blockNumber_in?: InputMaybe>;
+ blockNumber_lt?: InputMaybe;
+ blockNumber_lte?: InputMaybe;
+ blockNumber_not?: InputMaybe;
+ blockNumber_not_in?: InputMaybe>;
+ blockTimestamp?: InputMaybe;
+ blockTimestamp_gt?: InputMaybe;
+ blockTimestamp_gte?: InputMaybe;
+ blockTimestamp_in?: InputMaybe>;
+ blockTimestamp_lt?: InputMaybe;
+ blockTimestamp_lte?: InputMaybe;
+ blockTimestamp_not?: InputMaybe;
+ blockTimestamp_not_in?: InputMaybe>;
from?: InputMaybe;
from_contains?: InputMaybe;
from_contains_nocase?: InputMaybe;
@@ -379,6 +417,8 @@ export type TransferFractionToken_Filter = {
export enum TransferFractionToken_OrderBy {
Amount = 'amount',
+ BlockNumber = 'blockNumber',
+ BlockTimestamp = 'blockTimestamp',
From = 'from',
HatId = 'hatId',
Id = 'id',
@@ -390,6 +430,8 @@ export enum TransferFractionToken_OrderBy {
export type Workspace = {
__typename?: 'Workspace';
+ blockNumber: Scalars['BigInt']['output'];
+ blockTimestamp: Scalars['BigInt']['output'];
creator: Scalars['String']['output'];
hatsTimeFrameModule: Scalars['String']['output'];
hatterHatId: Scalars['BigInt']['output'];
@@ -402,6 +444,22 @@ export type Workspace_Filter = {
/** Filter for the block changed event. */
_change_block?: InputMaybe;
and?: InputMaybe>>;
+ blockNumber?: InputMaybe;
+ blockNumber_gt?: InputMaybe;
+ blockNumber_gte?: InputMaybe;
+ blockNumber_in?: InputMaybe>;
+ blockNumber_lt?: InputMaybe;
+ blockNumber_lte?: InputMaybe;
+ blockNumber_not?: InputMaybe;
+ blockNumber_not_in?: InputMaybe>;
+ blockTimestamp?: InputMaybe;
+ blockTimestamp_gt?: InputMaybe;
+ blockTimestamp_gte?: InputMaybe;
+ blockTimestamp_in?: InputMaybe>;
+ blockTimestamp_lt?: InputMaybe;
+ blockTimestamp_lte?: InputMaybe;
+ blockTimestamp_not?: InputMaybe;
+ blockTimestamp_not_in?: InputMaybe>;
creator?: InputMaybe;
creator_contains?: InputMaybe;
creator_contains_nocase?: InputMaybe;
@@ -490,6 +548,8 @@ export type Workspace_Filter = {
};
export enum Workspace_OrderBy {
+ BlockNumber = 'blockNumber',
+ BlockTimestamp = 'blockTimestamp',
Creator = 'creator',
HatsTimeFrameModule = 'hatsTimeFrameModule',
HatterHatId = 'hatterHatId',
@@ -534,20 +594,28 @@ export enum _SubgraphErrorPolicy_ {
Deny = 'deny'
}
+export type GetTransferFractionTokensQueryVariables = Exact<{
+ where?: InputMaybe;
+}>;
+
+
+export type GetTransferFractionTokensQuery = { __typename?: 'Query', transferFractionTokens: Array<{ __typename?: 'TransferFractionToken', amount: any, from: string, to: string, tokenId: any, blockNumber: any, blockTimestamp: any, hatId: any, id: string, wearer: string, workspaceId: string }> };
+
export type GetWorkspacesQueryVariables = Exact<{
where?: InputMaybe;
}>;
-export type GetWorkspacesQuery = { __typename?: 'Query', workspaces: Array<{ __typename?: 'Workspace', topHatId: any, splitCreator: string, id: string, hatterHatId: any, hatsTimeFrameModule: string, creator: string }> };
+export type GetWorkspacesQuery = { __typename?: 'Query', workspaces: Array<{ __typename?: 'Workspace', creator: string, topHatId: any, splitCreator: string, id: string, hatterHatId: any, hatsTimeFrameModule: string, blockTimestamp: any, blockNumber: any }> };
export type GetWorkspaceQueryVariables = Exact<{
workspaceId: Scalars['ID']['input'];
}>;
-export type GetWorkspaceQuery = { __typename?: 'Query', workspace?: { __typename?: 'Workspace', creator: string, hatsTimeFrameModule: string, hatterHatId: any, id: string, splitCreator: string, topHatId: any } | null };
+export type GetWorkspaceQuery = { __typename?: 'Query', workspace?: { __typename?: 'Workspace', creator: string, hatsTimeFrameModule: string, hatterHatId: any, id: string, splitCreator: string, topHatId: any, blockTimestamp: any, blockNumber: any } | null };
-export const GetWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaces"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace_filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"topHatId"}},{"kind":"Field","name":{"kind":"Name","value":"splitCreator"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hatterHatId"}},{"kind":"Field","name":{"kind":"Name","value":"hatsTimeFrameModule"}},{"kind":"Field","name":{"kind":"Name","value":"creator"}}]}}]}}]} as unknown as DocumentNode;
-export const GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creator"}},{"kind":"Field","name":{"kind":"Name","value":"hatsTimeFrameModule"}},{"kind":"Field","name":{"kind":"Name","value":"hatterHatId"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"splitCreator"}},{"kind":"Field","name":{"kind":"Name","value":"topHatId"}}]}}]}}]} as unknown as DocumentNode;
\ No newline at end of file
+export const GetTransferFractionTokensDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetTransferFractionTokens"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"TransferFractionToken_filter"}},"defaultValue":{"kind":"ObjectValue","fields":[]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"transferFractionTokens"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"from"}},{"kind":"Field","name":{"kind":"Name","value":"to"}},{"kind":"Field","name":{"kind":"Name","value":"tokenId"}},{"kind":"Field","name":{"kind":"Name","value":"blockNumber"}},{"kind":"Field","name":{"kind":"Name","value":"blockTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"hatId"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"wearer"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}}]}}]}}]} as unknown as DocumentNode;
+export const GetWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaces"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace_filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creator"}},{"kind":"Field","name":{"kind":"Name","value":"topHatId"}},{"kind":"Field","name":{"kind":"Name","value":"splitCreator"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"hatterHatId"}},{"kind":"Field","name":{"kind":"Name","value":"hatsTimeFrameModule"}},{"kind":"Field","name":{"kind":"Name","value":"blockTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"blockNumber"}}]}}]}}]} as unknown as DocumentNode;
+export const GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creator"}},{"kind":"Field","name":{"kind":"Name","value":"hatsTimeFrameModule"}},{"kind":"Field","name":{"kind":"Name","value":"hatterHatId"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"splitCreator"}},{"kind":"Field","name":{"kind":"Name","value":"topHatId"}},{"kind":"Field","name":{"kind":"Name","value":"blockTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"blockNumber"}}]}}]}}]} as unknown as DocumentNode;
\ No newline at end of file
diff --git a/pkgs/frontend/hooks/useFractionToken.ts b/pkgs/frontend/hooks/useFractionToken.ts
index dd71b572..0dc501ea 100644
--- a/pkgs/frontend/hooks/useFractionToken.ts
+++ b/pkgs/frontend/hooks/useFractionToken.ts
@@ -7,6 +7,13 @@ import {
} from "./useContracts";
import { useActiveWallet } from "./useWallet";
import { publicClient } from "./useViem";
+import { gql } from "@apollo/client/core";
+import { useQuery } from "@apollo/client/react/hooks";
+import {
+ GetTransferFractionTokensQuery,
+ GetTransferFractionTokensQueryVariables,
+ TransferFractionToken_Filter,
+} from "gql/graphql";
export const useTokenRecipients = (
params: {
@@ -106,7 +113,6 @@ export const useBalanceOfFractionToken = (
*/
export const useFractionToken = () => {
const { wallet } = useActiveWallet();
-
const [isLoading, setIsLoading] = useState(false);
/**
@@ -115,8 +121,6 @@ export const useFractionToken = () => {
*/
const getTokenRecipients = useCallback(
async (params: { tokenId: bigint }) => {
- if (!wallet) return;
-
setIsLoading(true);
try {
@@ -133,7 +137,7 @@ export const useFractionToken = () => {
setIsLoading(false);
}
},
- [wallet]
+ []
);
/**
@@ -143,8 +147,6 @@ export const useFractionToken = () => {
*/
const getTokenId = useCallback(
async (params: { hatId: bigint; account: Address }) => {
- if (!wallet) return;
-
setIsLoading(true);
try {
@@ -161,7 +163,7 @@ export const useFractionToken = () => {
setIsLoading(false);
}
},
- [wallet]
+ []
);
const mintInitialSupplyFractionToken = useCallback(
@@ -417,3 +419,39 @@ export const useTransferFractionToken = (hatId: bigint, wearer: Address) => {
return { isLoading, transferFractionToken };
};
+
+/////////////////////////////////////
+/////// Start subgraph section //////
+/////////////////////////////////////
+
+const queryGetTransferFractionTokens = gql(`
+ query GetTransferFractionTokens($where: TransferFractionToken_filter = {}) {
+ transferFractionTokens(where: $where) {
+ amount
+ from
+ to
+ tokenId
+ blockNumber
+ blockTimestamp
+ hatId
+ id
+ wearer
+ workspaceId
+ }
+ }
+`);
+
+export const useGetTransferFractionTokens = (params: {
+ where?: TransferFractionToken_Filter;
+}) => {
+ const result = useQuery<
+ GetTransferFractionTokensQuery,
+ GetTransferFractionTokensQueryVariables
+ >(queryGetTransferFractionTokens, {
+ variables: {
+ where: params.where,
+ },
+ });
+
+ return result;
+};
diff --git a/pkgs/frontend/hooks/useHats.ts b/pkgs/frontend/hooks/useHats.ts
index 19088b33..5c601afe 100644
--- a/pkgs/frontend/hooks/useHats.ts
+++ b/pkgs/frontend/hooks/useHats.ts
@@ -2,7 +2,7 @@ import { hatIdToTreeId, treeIdHexToDecimal } from "@hatsprotocol/sdk-v1-core";
import { Hat, HatsSubgraphClient, Tree } from "@hatsprotocol/sdk-v1-subgraph";
import { HATS_ABI } from "abi/hats";
import { useCallback, useEffect, useState } from "react";
-import { Address, decodeEventLog } from "viem";
+import { Address, parseEventLogs } from "viem";
import { base, optimism, sepolia } from "viem/chains";
import { HATS_ADDRESS } from "./useContracts";
import { useActiveWallet } from "./useWallet";
@@ -352,28 +352,14 @@ export const useHats = () => {
hash: txHash,
});
- const log = receipt.logs.find((log) => {
- try {
- const decodedLog = decodeEventLog({
- abi: HATS_ABI,
- data: log.data,
- topics: log.topics,
- });
- return decodedLog.eventName === "HatCreated";
- } catch (error) {
- console.error("error occured when creating Hats :", error);
- }
- })!;
-
- if (log) {
- const decodedLog = decodeEventLog({
- abi: HATS_ABI,
- data: log.data,
- topics: log.topics,
- });
- console.log({ decodedLog });
- }
- return txHash;
+ const parsedLog = parseEventLogs({
+ abi: HATS_ABI,
+ eventName: "HatCreated",
+ logs: receipt.logs,
+ strict: false,
+ });
+
+ return parsedLog;
} catch (error) {
console.error("error occured when creating Hats:", error);
} finally {
@@ -418,6 +404,76 @@ export const useHats = () => {
[wallet]
);
+ const changeHatDetails = useCallback(
+ async (params: { hatId: bigint; newDetails: string }) => {
+ if (!wallet) return;
+
+ setIsLoading(true);
+
+ try {
+ const txHash = await wallet.writeContract({
+ abi: HATS_ABI,
+ address: HATS_ADDRESS,
+ functionName: "changeHatDetails",
+ args: [params.hatId, params.newDetails],
+ });
+
+ const receipt = await publicClient.waitForTransactionReceipt({
+ hash: txHash,
+ });
+
+ const parsedLog = parseEventLogs({
+ abi: HATS_ABI,
+ eventName: "HatDetailsChanged",
+ logs: receipt.logs,
+ strict: false,
+ });
+
+ return parsedLog;
+ } catch (error) {
+ console.error("error occured when creating Hats:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ },
+ [wallet]
+ );
+
+ const changeHatImageURI = useCallback(
+ async (params: { hatId: bigint; newImageURI: string }) => {
+ if (!wallet) return;
+
+ setIsLoading(true);
+
+ try {
+ const txHash = await wallet.writeContract({
+ abi: HATS_ABI,
+ address: HATS_ADDRESS,
+ functionName: "changeHatImageURI",
+ args: [params.hatId, params.newImageURI],
+ });
+
+ const receipt = await publicClient.waitForTransactionReceipt({
+ hash: txHash,
+ });
+
+ const parsedLog = parseEventLogs({
+ abi: HATS_ABI,
+ eventName: "HatImageURIChanged",
+ logs: receipt.logs,
+ strict: false,
+ });
+
+ return parsedLog;
+ } catch (error) {
+ console.error("error occured when creating Hats:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ },
+ [wallet]
+ );
+
return {
isLoading,
getTreeInfo,
@@ -429,6 +485,8 @@ export const useHats = () => {
getHatsTimeframeModuleAddress,
createHat,
mintHat,
+ changeHatDetails,
+ changeHatImageURI,
};
};
diff --git a/pkgs/frontend/hooks/useIpfs.ts b/pkgs/frontend/hooks/useIpfs.ts
index 6b18ffae..4ed0c427 100644
--- a/pkgs/frontend/hooks/useIpfs.ts
+++ b/pkgs/frontend/hooks/useIpfs.ts
@@ -1,40 +1,19 @@
import { useState } from "react";
+import { HatsDetailSchama, HatsDetailsData } from "types/hats";
import { ipfsUploadJson, ipfsUploadFile } from "utils/ipfs";
export const useUploadMetadataToIpfs = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
- const uploadMetadataToIpfs = async ({
- name,
- description,
- responsibilities,
- authorities,
- eligibility,
- toggle,
- }: {
- name: string;
- description: string;
- responsibilities: string;
- authorities: string;
- eligibility: boolean;
- toggle: boolean;
- }): Promise<{ ipfsCid: string; ipfsUri: string } | null> => {
+ const uploadMetadataToIpfs = async (
+ metadata: object
+ ): Promise<{ ipfsCid: string; ipfsUri: string } | null> => {
setIsLoading(true);
setError(null);
try {
- const upload = await ipfsUploadJson({
- type: "1.0",
- data: {
- name,
- description,
- responsibilities,
- authorities,
- eligibility,
- toggle,
- },
- });
+ const upload = await ipfsUploadJson(metadata);
const ipfsCid = upload.IpfsHash;
const ipfsUri = `ipfs://${ipfsCid}`;
@@ -57,6 +36,33 @@ export const useUploadMetadataToIpfs = () => {
return { uploadMetadataToIpfs, isLoading, error };
};
+export const useUploadHatsDetailsToIpfs = () => {
+ const { uploadMetadataToIpfs, isLoading, error } = useUploadMetadataToIpfs();
+
+ const uploadHatsDetailsToIpfs = async ({
+ name,
+ description,
+ responsabilities,
+ authorities,
+ }: HatsDetailsData): Promise<{ ipfsCid: string; ipfsUri: string } | null> => {
+ const details: HatsDetailSchama = {
+ type: "1.0",
+ data: {
+ name,
+ description,
+ responsabilities,
+ authorities,
+ },
+ };
+
+ const res = await uploadMetadataToIpfs(details);
+
+ return res;
+ };
+
+ return { uploadHatsDetailsToIpfs, isLoading, error };
+};
+
export const useUploadImageFileToIpfs = () => {
const [isLoading, setIsLoading] = useState(false);
const [imageFile, setImageFile] = useState(null);
diff --git a/pkgs/frontend/hooks/useWorkspace.ts b/pkgs/frontend/hooks/useWorkspace.ts
index 67ea518d..780d471c 100644
--- a/pkgs/frontend/hooks/useWorkspace.ts
+++ b/pkgs/frontend/hooks/useWorkspace.ts
@@ -5,12 +5,14 @@ import { GetWorkspaceQuery, GetWorkspaceQueryVariables } from "gql/graphql";
const queryGetWorkspaces = gql(`
query GetWorkspaces($where: Workspace_filter) {
workspaces(where: $where) {
+ creator
topHatId
splitCreator
id
hatterHatId
hatsTimeFrameModule
- creator
+ blockTimestamp
+ blockNumber
}
}
`);
@@ -24,6 +26,8 @@ const queryGetWorkspace = gql(`
id
splitCreator
topHatId
+ blockTimestamp
+ blockNumber
}
}
`);
diff --git a/pkgs/frontend/package.json b/pkgs/frontend/package.json
index 8b47dd0a..2995ba48 100644
--- a/pkgs/frontend/package.json
+++ b/pkgs/frontend/package.json
@@ -23,7 +23,7 @@
"@emotion/server": "^11.11.0",
"@hatsprotocol/sdk-v1-core": "^0.10.0",
"@hatsprotocol/sdk-v1-subgraph": "^1.0.0",
- "@privy-io/react-auth": "^1.95.0",
+ "@privy-io/react-auth": "^1.99.0",
"@remix-run/node": "^2.15.0",
"@remix-run/react": "^2.15.0",
"@remix-run/serve": "^2.15.0",
diff --git a/pkgs/frontend/public/images/favicon.ico b/pkgs/frontend/public/images/favicon.ico
new file mode 100644
index 00000000..b570f989
Binary files /dev/null and b/pkgs/frontend/public/images/favicon.ico differ
diff --git a/pkgs/frontend/public/images/lp/people-deco.svg b/pkgs/frontend/public/images/lp/people-deco.svg
new file mode 100644
index 00000000..312f237e
--- /dev/null
+++ b/pkgs/frontend/public/images/lp/people-deco.svg
@@ -0,0 +1,670 @@
+
diff --git a/pkgs/frontend/public/images/lp/wave-deco.svg b/pkgs/frontend/public/images/lp/wave-deco.svg
new file mode 100644
index 00000000..5a6bec2b
--- /dev/null
+++ b/pkgs/frontend/public/images/lp/wave-deco.svg
@@ -0,0 +1,20 @@
+
diff --git a/pkgs/frontend/public/images/toban-logo-text.svg b/pkgs/frontend/public/images/toban-logo-text.svg
new file mode 100644
index 00000000..cc1adba6
--- /dev/null
+++ b/pkgs/frontend/public/images/toban-logo-text.svg
@@ -0,0 +1,20 @@
+
diff --git a/pkgs/frontend/types/hats.ts b/pkgs/frontend/types/hats.ts
index ae584ccc..dc9b0693 100644
--- a/pkgs/frontend/types/hats.ts
+++ b/pkgs/frontend/types/hats.ts
@@ -18,3 +18,14 @@ export interface HatsDetailSchama {
}[];
};
}
+
+export type HatsDetailsData = HatsDetailSchama["data"];
+
+export type HatsDetailsResponsabilities = HatsDetailsData["responsabilities"];
+
+export type HatsDetailsAuthorities = HatsDetailsData["authorities"];
+
+// 共通の型を作成
+export type HatsDetailsAttributes = NonNullable<
+ HatsDetailsResponsabilities | HatsDetailsAuthorities
+>;
diff --git a/yarn.lock b/yarn.lock
index b7240cfb..d7abbf55 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4557,10 +4557,10 @@
dependencies:
zod "^3.21.4"
-"@privy-io/js-sdk-core@0.35.1":
- version "0.35.1"
- resolved "https://registry.yarnpkg.com/@privy-io/js-sdk-core/-/js-sdk-core-0.35.1.tgz#20e18d03deeb365a5e633c632d5471f67c752a98"
- integrity sha512-KuE6bQ1KMw4fgK3nPClTfiC55byu/AVWyBQwrRKDh0Jacw0L8eLRhGoQtRcJOKqwa+LukiSBf6cgutlxdRVZqQ==
+"@privy-io/js-sdk-core@0.37.0":
+ version "0.37.0"
+ resolved "https://registry.yarnpkg.com/@privy-io/js-sdk-core/-/js-sdk-core-0.37.0.tgz#32b370b6a4ce6d1721511f88ef499d1afd3d7beb"
+ integrity sha512-9TXaZfCD1+3SW05yYiO/CDiQ/+aRmgrG0jjpl8iilyfge65CEhzQ5Qp0XkOWH7cT6+pGwFO1Dh7VGPuGimlAyw==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
@@ -4569,7 +4569,7 @@
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/units" "^5.7.0"
"@privy-io/api-base" "^1.4.1"
- "@privy-io/public-api" "2.15.2"
+ "@privy-io/public-api" "2.15.9"
eventemitter3 "^5.0.1"
fetch-retry "^5.0.6"
jose "^4.15.5"
@@ -4578,10 +4578,10 @@
set-cookie-parser "^2.6.0"
uuid ">=8 <10"
-"@privy-io/public-api@2.15.2":
- version "2.15.2"
- resolved "https://registry.yarnpkg.com/@privy-io/public-api/-/public-api-2.15.2.tgz#9e89d3aab6cce4bb568b3da3449b8d87b2b350d5"
- integrity sha512-p8D2TPI0A319+tF5R8XvaaaKbXopltcd1d0/zELB6pXY5kkd1sCv9RZy+b52aweQgh3rvGrUo/lmlM11QRNPog==
+"@privy-io/public-api@2.15.9":
+ version "2.15.9"
+ resolved "https://registry.yarnpkg.com/@privy-io/public-api/-/public-api-2.15.9.tgz#f79877500a3abe3f0a5bb33ff92a2a86509a7702"
+ integrity sha512-z5iA+A+2ih0evTjrIOdkF0JyrWltr1P32GB+Ar0WBuDgmC35/BA2aOuivO24hxWuEFXaVioNpA4NKrxUO/poJw==
dependencies:
"@privy-io/api-base" "1.4.1"
bs58 "^5.0.0"
@@ -4589,10 +4589,10 @@
libphonenumber-js "^1.10.31"
zod "^3.22.4"
-"@privy-io/react-auth@^1.95.0":
- version "1.95.2"
- resolved "https://registry.yarnpkg.com/@privy-io/react-auth/-/react-auth-1.95.2.tgz#916cfa3c2ebbfd0f5db43734f5d21ce3576ef8ea"
- integrity sha512-JWJmpH16vEN5NlneniCzYmKWDnUSAtQKKwnf149/fJLKBv+AaArrUNsOcuckmspCcamAGBFNB/n8ovGDYQBqYA==
+"@privy-io/react-auth@^1.99.0":
+ version "1.99.0"
+ resolved "https://registry.yarnpkg.com/@privy-io/react-auth/-/react-auth-1.99.0.tgz#444eeeaf40ad18a5330d0e1dc61dd5212ab6e0f1"
+ integrity sha512-XZiHfCcuvt3ZmP1Q90jGlwGMF8MZiqBE6ulFhXdkT6m5blzPXOQDHq/OO0faLURJ1oDNISJ/OPbXJYS2AcGZ+g==
dependencies:
"@coinbase/wallet-sdk" "4.0.3"
"@ethersproject/abstract-signer" "^5.7.0"
@@ -4610,7 +4610,7 @@
"@heroicons/react" "^2.1.1"
"@marsidev/react-turnstile" "^0.4.1"
"@metamask/eth-sig-util" "^6.0.0"
- "@privy-io/js-sdk-core" "0.35.1"
+ "@privy-io/js-sdk-core" "0.37.0"
"@simplewebauthn/browser" "^9.0.1"
"@solana/wallet-adapter-base" "^0.9.23"
"@solana/wallet-standard-wallet-adapter-base" "^1.1.2"
@@ -4640,6 +4640,7 @@
viem "^2.21.9"
web3-core "^1.8.0"
web3-core-helpers "^1.8.0"
+ zustand "^5.0.0"
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
@@ -13169,6 +13170,11 @@ humanize-ms@^1.2.1:
dependencies:
ms "^2.0.0"
+hyperlinker@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
+ integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==
+
i18next-browser-languagedetector@^8.0.0:
version "8.0.2"
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.2.tgz#037ca25c26877cad778f060a9e177054d9f8eaa3"
@@ -13188,12 +13194,6 @@ i18next@^23.12.2:
dependencies:
"@babel/runtime" "^7.23.2"
-hyperlinker@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
- integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==
-
-
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -19828,7 +19828,7 @@ string-hash@^1.1.3:
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -19845,6 +19845,15 @@ string-width@^2.1.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
+string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -19953,7 +19962,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -19981,6 +19990,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
dependencies:
ansi-regex "^5.0.1"
+strip-ansi@^7.0.1:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+ integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+ dependencies:
+ ansi-regex "^6.0.1"
+
strip-bom-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92"
@@ -20342,10 +20358,6 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
-timestring@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/timestring/-/timestring-6.0.0.tgz#b0c7c331981ecf2066ce88bcfb8ee3ae32e7a0f6"
- integrity sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==
timeout-abort-controller@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/timeout-abort-controller/-/timeout-abort-controller-2.0.0.tgz#d6a59209132e520413092dd4b4d71eaaf5887feb"
@@ -20355,6 +20367,11 @@ timeout-abort-controller@^2.0.0:
native-abort-controller "^1.0.4"
retimer "^3.0.0"
+timestring@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/timestring/-/timestring-6.0.0.tgz#b0c7c331981ecf2066ce88bcfb8ee3ae32e7a0f6"
+ integrity sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==
+
tiny-invariant@^1.0.2:
version "1.3.3"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
@@ -21313,18 +21330,6 @@ viem@^2.20.1, viem@^2.21.15, viem@^2.21.51, viem@^2.21.9:
webauthn-p256 "0.0.10"
ws "8.18.0"
-vite-env-only@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/vite-env-only/-/vite-env-only-3.0.3.tgz#cf43be571af1ed6f71d715b51625a81dc7f9d029"
- integrity sha512-iAb7cTXRrvFShaF1n+G8f6Yqq7sRJcxipNYNQQu0DN5N9P55vJMmLG5lNU5moYGpd+ZH1WhBHdkWi5WjrfImHg==
- dependencies:
- "@babel/core" "^7.23.7"
- "@babel/generator" "^7.23.6"
- "@babel/parser" "^7.23.6"
- "@babel/traverse" "^7.23.7"
- "@babel/types" "^7.23.6"
- babel-dead-code-elimination "^1.0.6"
- micromatch "^4.0.5"
viem@^2.21.55:
version "2.22.2"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.22.2.tgz#ec25affced2491ea3984cc8ce5d3d4b760ae85b1"
@@ -21340,6 +21345,19 @@ viem@^2.21.55:
webauthn-p256 "0.0.10"
ws "8.18.0"
+vite-env-only@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/vite-env-only/-/vite-env-only-3.0.3.tgz#cf43be571af1ed6f71d715b51625a81dc7f9d029"
+ integrity sha512-iAb7cTXRrvFShaF1n+G8f6Yqq7sRJcxipNYNQQu0DN5N9P55vJMmLG5lNU5moYGpd+ZH1WhBHdkWi5WjrfImHg==
+ dependencies:
+ "@babel/core" "^7.23.7"
+ "@babel/generator" "^7.23.6"
+ "@babel/parser" "^7.23.6"
+ "@babel/traverse" "^7.23.7"
+ "@babel/types" "^7.23.6"
+ babel-dead-code-elimination "^1.0.6"
+ micromatch "^4.0.5"
+
vite-node@^1.2.0, vite-node@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.6.0.tgz#2c7e61129bfecc759478fa592754fd9704aaba7f"
@@ -21880,7 +21898,7 @@ workerpool@^6.5.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -21898,6 +21916,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
@@ -22109,6 +22136,11 @@ zod@^3.21.4, zod@^3.22.4, zod@^3.23.8:
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
+zustand@^5.0.0:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.2.tgz#f7595ada55a565f1fd6464f002a91e701ee0cfca"
+ integrity sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==
+
zwitch@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"