diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b541b27e..47923e856 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Features
+- [#1093](https://github.com/alleslabs/celatone-frontend/pull/1093) My past module verifications with functionalities
- [#1085](https://github.com/alleslabs/celatone-frontend/pull/1085) Add my module verification details
- [#1087](https://github.com/alleslabs/celatone-frontend/pull/1087) Add modules verify page
- [#1085](https://github.com/alleslabs/celatone-frontend/pull/1085) Add verify module store
diff --git a/src/config/project/index.ts b/src/config/project/index.ts
index c4dcca27f..ed4fa6af3 100644
--- a/src/config/project/index.ts
+++ b/src/config/project/index.ts
@@ -10,4 +10,5 @@ export const PROJECT_CONSTANTS: ProjectConstants = {
maxContractNameLength: 50,
maxContractDescriptionLength: 250,
maxListNameLength: 50,
+ maxMoveVerifyTaskRequestNoteLength: 50,
};
diff --git a/src/config/project/types.ts b/src/config/project/types.ts
index e0960077e..27364cc4e 100644
--- a/src/config/project/types.ts
+++ b/src/config/project/types.ts
@@ -10,4 +10,7 @@ export interface ProjectConstants {
maxContractNameLength: number;
maxContractDescriptionLength: number;
maxCodeNameLength: number;
+
+ // move verify task
+ maxMoveVerifyTaskRequestNoteLength: number;
}
diff --git a/src/lib/components/ExplorerLink.tsx b/src/lib/components/ExplorerLink.tsx
index 2a455a521..011ddf551 100644
--- a/src/lib/components/ExplorerLink.tsx
+++ b/src/lib/components/ExplorerLink.tsx
@@ -21,7 +21,8 @@ export type LinkType =
| "code_id"
| "block_height"
| "proposal_id"
- | "pool_id";
+ | "pool_id"
+ | "task_id";
interface ExplorerLinkProps extends FlexProps {
value: string;
@@ -75,6 +76,9 @@ export const getNavigationUrl = ({
case "pool_id":
url = "/pools";
break;
+ case "task_id":
+ url = "/my-module-verifications";
+ break;
case "invalid_address":
return "";
default:
diff --git a/src/lib/components/state/EmptyState.tsx b/src/lib/components/state/EmptyState.tsx
index 7c7f97715..d23d21392 100644
--- a/src/lib/components/state/EmptyState.tsx
+++ b/src/lib/components/state/EmptyState.tsx
@@ -15,6 +15,7 @@ export interface EmptyStateProps {
alignItems?: FlexProps["alignItems"];
textVariant?: TextProps["variant"];
hasBorderTop?: boolean;
+ children?: React.ReactNode;
}
export const EmptyState = ({
@@ -28,6 +29,7 @@ export const EmptyState = ({
alignItems = "center",
textVariant = "body1",
hasBorderTop = true,
+ children,
}: EmptyStateProps) => (
{message}
+ {children}
);
diff --git a/src/lib/pages/modules-verify/index.tsx b/src/lib/pages/modules-verify/index.tsx
index 5de7f65d7..dc3eb8222 100644
--- a/src/lib/pages/modules-verify/index.tsx
+++ b/src/lib/pages/modules-verify/index.tsx
@@ -17,7 +17,7 @@ import { FooterCta } from "lib/components/layouts";
import { NoMobile } from "lib/components/modal";
import PageContainer from "lib/components/PageContainer";
import { CelatoneSeo } from "lib/components/Seo";
-import { useVerifyModuleTaskStore } from "lib/providers/store";
+import { useMoveVerifyTaskStore } from "lib/providers/store";
import { useSubmitMoveVerify } from "lib/services/verification/move";
import {
@@ -37,7 +37,7 @@ export const ModulesVerify = observer(() => {
const { currentChainId } = useCelatoneApp();
const { mutateAsync, isError, isLoading } = useSubmitMoveVerify();
const { isOpen, onOpen, onClose } = useDisclosure();
- const { addVerifyModuleTask } = useVerifyModuleTaskStore();
+ const { addMoveVerifyTask } = useMoveVerifyTaskStore();
const { control, watch, handleSubmit, setValue } = useForm({
mode: "all",
@@ -68,7 +68,7 @@ export const ModulesVerify = observer(() => {
if (!data) return;
setValue("taskId", data.id);
- addVerifyModuleTask({
+ addMoveVerifyTask({
taskId: data.id,
chainId: currentChainId,
requestNote,
diff --git a/src/lib/pages/my-module-verification-details/components/MyModuleVerificationDetailsStatusBadge.tsx b/src/lib/pages/my-module-verification-details/components/MyModuleVerificationDetailsStatusBadge.tsx
index 181755868..5032cb42d 100644
--- a/src/lib/pages/my-module-verification-details/components/MyModuleVerificationDetailsStatusBadge.tsx
+++ b/src/lib/pages/my-module-verification-details/components/MyModuleVerificationDetailsStatusBadge.tsx
@@ -1,14 +1,19 @@
import { Tag, TagLabel } from "@chakra-ui/react";
import { ActiveDot } from "lib/components/ActiveDot";
+import { CustomIcon } from "lib/components/icon";
import { MoveVerifyTaskStatus } from "lib/services/types";
interface MyModuleVerificationDetailsStatusBadgeProps {
status: MoveVerifyTaskStatus;
+ hasCloseBtn?: boolean;
+ isActiveOnVerifying?: boolean;
}
export const MyModuleVerificationDetailsStatusBadge = ({
status,
+ hasCloseBtn,
+ isActiveOnVerifying = true,
}: MyModuleVerificationDetailsStatusBadgeProps) => {
const renderStatus = () => {
if (status === MoveVerifyTaskStatus.Pending)
@@ -37,15 +42,16 @@ export const MyModuleVerificationDetailsStatusBadge = ({
return (
<>
-
+
Verifying
>
);
};
return (
-
+
{renderStatus()}
+ {hasCloseBtn && }
);
};
diff --git a/src/lib/pages/my-module-verification-details/index.tsx b/src/lib/pages/my-module-verification-details/index.tsx
index dc7440e84..5c377763a 100644
--- a/src/lib/pages/my-module-verification-details/index.tsx
+++ b/src/lib/pages/my-module-verification-details/index.tsx
@@ -6,7 +6,7 @@ import { Loading } from "lib/components/Loading";
import PageContainer from "lib/components/PageContainer";
import { CelatoneSeo } from "lib/components/Seo";
import { EmptyState } from "lib/components/state";
-import { useVerifyModuleTaskStore } from "lib/providers/store";
+import { useMoveVerifyTaskStore } from "lib/providers/store";
import { useMoveVerifyTaskInfo } from "lib/services/verification/move";
import {
@@ -19,9 +19,9 @@ import {
import { zMyModuleVerificationDetailsQueryParams } from "./types";
const MyModuleVerificationDetailsBody = ({ taskId }: { taskId: string }) => {
- const { data, isLoading, error } = useMoveVerifyTaskInfo(taskId);
- const { getVerifyModuleTask } = useVerifyModuleTaskStore();
- const verifyModuleTask = getVerifyModuleTask(taskId);
+ const { data, isLoading, error } = useMoveVerifyTaskInfo(taskId, !!taskId);
+ const { getMoveVerifyTask } = useMoveVerifyTaskStore();
+ const verifyModuleTask = getMoveVerifyTask(taskId);
if (isLoading) return ;
if (!data || error || !verifyModuleTask)
diff --git a/src/lib/pages/my-module-verifications/components/MoveVerifyTaskStatusFilter.tsx b/src/lib/pages/my-module-verifications/components/MoveVerifyTaskStatusFilter.tsx
new file mode 100644
index 000000000..c469f7c86
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/components/MoveVerifyTaskStatusFilter.tsx
@@ -0,0 +1,138 @@
+import type { InputProps } from "@chakra-ui/react";
+import { Flex, FormControl, useOutsideClick } from "@chakra-ui/react";
+import { matchSorter } from "match-sorter";
+import { forwardRef, useMemo, useRef, useState } from "react";
+
+import {
+ DropdownContainer,
+ FilterChip,
+ FilterDropdownItem,
+ FilterInput,
+} from "lib/components/filter";
+import { MyModuleVerificationDetailsStatusBadge } from "lib/pages/my-module-verification-details/components";
+import { MoveVerifyTaskStatus } from "lib/services/types";
+import { toggleItem } from "lib/utils";
+
+export interface MoveVerifyTaskStatusFilterProps extends InputProps {
+ result: MoveVerifyTaskStatus[];
+ minW?: string;
+ label?: string;
+ placeholder?: string;
+ setResult: (option: MoveVerifyTaskStatus[]) => void;
+ isMulti: boolean;
+}
+
+const OPTIONS = [
+ { label: "completed", value: MoveVerifyTaskStatus.Finished },
+ { label: "failed", value: MoveVerifyTaskStatus.NotFound },
+ { label: "pending", value: MoveVerifyTaskStatus.Pending },
+ { label: "verifying", value: MoveVerifyTaskStatus.Running },
+];
+
+export const MoveVerifyTaskStatusFilter = forwardRef<
+ HTMLInputElement,
+ MoveVerifyTaskStatusFilterProps
+>(
+ (
+ {
+ result,
+ minW = "50%",
+ setResult,
+ placeholder,
+ label,
+ isMulti,
+ }: MoveVerifyTaskStatusFilterProps,
+ ref
+ ) => {
+ const [keyword, setKeyword] = useState("");
+ const [isDropdown, setIsDropdown] = useState(false);
+ const inputRef = useRef(null);
+ const boxRef = useRef(null);
+
+ const dropdownValue = useMemo(
+ () =>
+ keyword
+ ? matchSorter(OPTIONS, keyword, {
+ keys: ["label"],
+ threshold: matchSorter.rankings.CONTAINS,
+ })
+ : OPTIONS,
+ [keyword]
+ );
+
+ const isOptionSelected = (option: MoveVerifyTaskStatus) =>
+ result.some((selectedOption) => selectedOption === option);
+
+ const selectOption = (option: MoveVerifyTaskStatus) => {
+ setKeyword("");
+
+ if (!isMulti) {
+ setIsDropdown(false);
+
+ if (result[0] === option) setResult([]);
+ else setResult([option]);
+ } else {
+ setResult(toggleItem(result, option));
+ }
+ };
+
+ useOutsideClick({
+ ref: boxRef,
+ handler: () => setIsDropdown(false),
+ });
+
+ return (
+
+
+ {result.map((option) => (
+
+ }
+ onSelect={() => setResult(toggleItem(result, option))}
+ />
+ ))}
+
+ }
+ />
+
+ {isDropdown && (
+
+ {!dropdownValue.length && No filter matched}
+
+ {/* option selection section */}
+ {dropdownValue.map((option) => (
+
+ }
+ isOptionSelected={isOptionSelected(option.value)}
+ onSelect={() => selectOption(option.value)}
+ />
+ ))}
+
+ )}
+
+ );
+ }
+);
diff --git a/src/lib/pages/my-module-verifications/components/my-module-verifications-table/FileNamesCell.tsx b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/FileNamesCell.tsx
new file mode 100644
index 000000000..93d89e0a7
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/FileNamesCell.tsx
@@ -0,0 +1,39 @@
+import { Flex, Text } from "@chakra-ui/react";
+import { useMemo, useState } from "react";
+
+import type { MoveVerifyTaskInfo } from "../../data";
+
+interface FileNamesCellProps {
+ task: MoveVerifyTaskInfo;
+}
+
+export const FileNamesCell = ({ task }: FileNamesCellProps) => {
+ const [isHoverText, setIsHoverText] = useState(false);
+
+ const formattedText = useMemo(() => {
+ const files = Object.keys(task.fileMap)
+ .filter((file) => !file.includes(".toml"))
+ .map((file) => file.slice(0, -5)); // remove ".move" extension
+
+ if (isHoverText) return files.join(", ");
+
+ const firstPart = files.slice(0, 3).join(", ");
+ const remaining = files.length - 3;
+
+ // eslint-disable-next-line sonarjs/no-nested-template-literals
+ return `${firstPart}${remaining > 0 ? `, +${remaining}` : ""}`;
+ }, [isHoverText, task.fileMap]);
+
+ return (
+ setIsHoverText(true)}
+ onMouseOut={() => setIsHoverText(false)}
+ flexWrap="wrap"
+ wordBreak="break-word"
+ >
+ {formattedText}
+
+ );
+};
diff --git a/src/lib/pages/my-module-verifications/components/my-module-verifications-table/MyModuleVerificationsHeader.tsx b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/MyModuleVerificationsHeader.tsx
new file mode 100644
index 000000000..6b3452de2
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/MyModuleVerificationsHeader.tsx
@@ -0,0 +1,19 @@
+import type { GridProps } from "@chakra-ui/react";
+import { Grid } from "@chakra-ui/react";
+
+import { TableHeader } from "lib/components/table";
+
+interface ModulesTableHeaderProps {
+ templateColumns: GridProps["templateColumns"];
+}
+export const MyModuleVerificationsTableHeader = ({
+ templateColumns,
+}: ModulesTableHeaderProps) => (
+
+ Request ID
+ Request Note
+ Files
+ Status
+ Verified at
+
+);
diff --git a/src/lib/pages/my-module-verifications/components/my-module-verifications-table/MyModuleVerificationsRow.tsx b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/MyModuleVerificationsRow.tsx
new file mode 100644
index 000000000..4e5b2ad8a
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/MyModuleVerificationsRow.tsx
@@ -0,0 +1,66 @@
+import { Flex, Grid, Text } from "@chakra-ui/react";
+import type { GridProps } from "@chakra-ui/react";
+
+import type { MoveVerifyTaskInfo } from "../../data";
+import { useInternalNavigate } from "lib/app-provider";
+import { ExplorerLink } from "lib/components/ExplorerLink";
+import { TableRow } from "lib/components/table";
+import { MyModuleVerificationDetailsStatusBadge } from "lib/pages/my-module-verification-details/components";
+import { dateFromNow, formatUTC } from "lib/utils";
+
+import { FileNamesCell } from "./FileNamesCell";
+import { RequestNoteCell } from "./RequestNoteCell";
+
+interface MyModuleVerificationsRowProps {
+ templateColumns: GridProps["templateColumns"];
+ task: MoveVerifyTaskInfo;
+}
+
+export const MyModuleVerificationsRow = ({
+ templateColumns,
+ task,
+}: MyModuleVerificationsRowProps) => {
+ const navigate = useInternalNavigate();
+
+ return (
+
+ navigate({
+ pathname: "/my-module-verifications/[taskId]",
+ query: { taskId: task.taskId },
+ })
+ }
+ _hover={{ bg: "gray.900" }}
+ transition="all 0.25s ease-in-out"
+ cursor="pointer"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {task.verifiedAt ? (
+
+
+ {formatUTC(task.verifiedAt)}
+
+
+ ({dateFromNow(task.verifiedAt)})
+
+
+ ) : (
+ -
+ )}
+
+
+ );
+};
diff --git a/src/lib/pages/my-module-verifications/components/my-module-verifications-table/RequestNoteCell.tsx b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/RequestNoteCell.tsx
new file mode 100644
index 000000000..21cb7527d
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/RequestNoteCell.tsx
@@ -0,0 +1,30 @@
+import { observer } from "mobx-react-lite";
+
+import type { MoveVerifyTaskInfo } from "../../data";
+import { useCelatoneApp } from "lib/app-provider";
+import { EditableCell } from "lib/components/table";
+import { useMoveVerifyTaskStore } from "lib/providers/store";
+
+interface RequestNoteProps {
+ moveVerifyTask: MoveVerifyTaskInfo;
+}
+
+export const RequestNoteCell = observer(
+ ({ moveVerifyTask }: RequestNoteProps) => {
+ const { constants } = useCelatoneApp();
+ const { updateRequestNote } = useMoveVerifyTaskStore();
+
+ return (
+ 0
+ ? moveVerifyTask.requestNote
+ : "-"
+ }
+ maxLength={constants.maxMoveVerifyTaskRequestNoteLength}
+ onSave={(value) => updateRequestNote(moveVerifyTask.taskId, value)}
+ />
+ );
+ }
+);
diff --git a/src/lib/pages/my-module-verifications/components/my-module-verifications-table/index.tsx b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/index.tsx
new file mode 100644
index 000000000..f302c6325
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/components/my-module-verifications-table/index.tsx
@@ -0,0 +1,98 @@
+/* eslint-disable react/button-has-type */
+import { Box, Button, Grid, Stack } from "@chakra-ui/react";
+import { observer } from "mobx-react-lite";
+import { useMemo, useState } from "react";
+
+import { useMyModuleVerifications } from "../../data";
+import { MoveVerifyTaskStatusFilter } from "../MoveVerifyTaskStatusFilter";
+import { useInternalNavigate } from "lib/app-provider";
+import { CustomIcon } from "lib/components/icon";
+import InputWithIcon from "lib/components/InputWithIcon";
+import { Loading } from "lib/components/Loading";
+import { EmptyState } from "lib/components/state";
+import { TableContainer } from "lib/components/table";
+import type { MoveVerifyTaskStatus } from "lib/services/types";
+
+import { MyModuleVerificationsTableHeader } from "./MyModuleVerificationsHeader";
+import { MyModuleVerificationsRow } from "./MyModuleVerificationsRow";
+
+export const MyModuleVerificationsTable = observer(() => {
+ const navigate = useInternalNavigate();
+ const [keyword, setKeyword] = useState("");
+ const [statuses, setStatuses] = useState([]);
+ const { data, isLoading } = useMyModuleVerifications();
+
+ const isFiltering = keyword.length > 0 || statuses.length > 0;
+
+ const templateColumns = "repeat(3, 1fr) 200px 1.2fr";
+
+ const filteredTasks = useMemo(
+ () =>
+ data
+ ?.filter((task) =>
+ task.taskId?.toLowerCase().includes(keyword.toLowerCase())
+ )
+ .filter((task) => {
+ if (statuses.length === 0) return true;
+ return statuses.includes(task.status);
+ }),
+ [data, keyword, statuses]
+ );
+
+ if (isLoading) return ;
+
+ return (
+
+
+ setKeyword(e.target.value)}
+ amptrackSection="my-published-modules-search"
+ size="lg"
+ />
+
+
+
+ {filteredTasks.length === 0 ? (
+
+
+
+
+
+ ) : (
+
+
+ {filteredTasks.map((task) => (
+
+ ))}
+
+ )}
+
+ );
+});
diff --git a/src/lib/pages/my-module-verifications/data.ts b/src/lib/pages/my-module-verifications/data.ts
new file mode 100644
index 000000000..1be1eb585
--- /dev/null
+++ b/src/lib/pages/my-module-verifications/data.ts
@@ -0,0 +1,55 @@
+import { useMoveVerifyTaskStore } from "lib/providers/store";
+import { MoveVerifyTaskStatus } from "lib/services/types";
+import { useMoveVerifyTaskInfos } from "lib/services/verification/move";
+import type { MoveVerifyTaskLocalInfo } from "lib/stores/verify-module";
+
+export type MoveVerifyTaskInfo = MoveVerifyTaskLocalInfo & {
+ status: MoveVerifyTaskStatus;
+};
+
+export const useMyModuleVerifications = (): {
+ isLoading: boolean;
+ data: MoveVerifyTaskInfo[];
+} => {
+ const { latestMoveVerifyTasks, completeMoveVerifyTask } =
+ useMoveVerifyTaskStore();
+ const localTasks = latestMoveVerifyTasks();
+ const verificationInfos = useMoveVerifyTaskInfos(
+ localTasks
+ .filter(({ completed }) => !completed)
+ .map((module) => module.taskId),
+ ({ task, result }) => {
+ if (
+ task.status === MoveVerifyTaskStatus.Finished ||
+ task.status === MoveVerifyTaskStatus.NotFound
+ ) {
+ completeMoveVerifyTask(task.id, result?.verifiedAt);
+ }
+ }
+ );
+
+ return {
+ isLoading: verificationInfos.some(
+ (verificationInfo) => verificationInfo.isLoading
+ ),
+ data: localTasks.map((task) => {
+ if (task.completed) {
+ return {
+ ...task,
+ status: task.verifiedAt
+ ? MoveVerifyTaskStatus.Finished
+ : MoveVerifyTaskStatus.NotFound,
+ };
+ }
+
+ const fetchedTaskInfo = verificationInfos
+ ?.map(({ data }) => data)
+ ?.find((verificationInfo) => verificationInfo?.task.id === task.taskId);
+
+ return {
+ ...task,
+ status: fetchedTaskInfo?.task.status ?? MoveVerifyTaskStatus.NotFound,
+ };
+ }),
+ };
+};
diff --git a/src/lib/pages/my-module-verifications/index.tsx b/src/lib/pages/my-module-verifications/index.tsx
index 3da35d265..8b808306b 100644
--- a/src/lib/pages/my-module-verifications/index.tsx
+++ b/src/lib/pages/my-module-verifications/index.tsx
@@ -1,3 +1,45 @@
+import { Box, Button, Flex, Heading, Text } from "@chakra-ui/react";
+
+import { useInternalNavigate, useMoveConfig } from "lib/app-provider";
+import { CustomIcon } from "lib/components/icon";
+import PageContainer from "lib/components/PageContainer";
+import { CelatoneSeo } from "lib/components/Seo";
+
+import { MyModuleVerificationsTable } from "./components/my-module-verifications-table";
+
export const MyModuleVerifications = () => {
- return My Module Verifications
;
+ const navigate = useInternalNavigate();
+ useMoveConfig({ shouldRedirect: true });
+
+ return (
+
+
+
+
+
+ My Past Verification
+
+
+ Display the request queue for module verifications through
+ InitiaScan
+
+
+
+ {/* }
+ variant="outline-white"
+ >
+ View Verification Guideline
+ */}
+
+
+
+
+
+ );
};
diff --git a/src/lib/providers/network-guard/index.tsx b/src/lib/providers/network-guard/index.tsx
index e2f09f79a..062ee9dd9 100644
--- a/src/lib/providers/network-guard/index.tsx
+++ b/src/lib/providers/network-guard/index.tsx
@@ -9,8 +9,8 @@ import {
useAccountStore,
useCodeStore,
useContractStore,
+ useMoveVerifyTaskStore,
usePublicProjectStore,
- useVerifyModuleTaskStore,
} from "lib/providers/store";
import { formatUserKey } from "lib/utils";
@@ -31,8 +31,8 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => {
const { setCodeUserKey, isCodeUserKeyExist } = useCodeStore();
const { setContractUserKey, isContractUserKeyExist } = useContractStore();
const { setProjectUserKey, isProjectUserKeyExist } = usePublicProjectStore();
- const { setVerifyModuleTaskUserKey, isVerifyModuleTaskUserKeyExist } =
- useVerifyModuleTaskStore();
+ const { setMoveVerifyTaskUserKey, isMoveVerifyTaskUserKeyExist } =
+ useMoveVerifyTaskStore();
useEffect(() => {
if (isHydrated) {
@@ -41,7 +41,7 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => {
setCodeUserKey(userKey);
setContractUserKey(userKey);
setProjectUserKey(userKey);
- setVerifyModuleTaskUserKey(userKey);
+ setMoveVerifyTaskUserKey(userKey);
}
}, [
isHydrated,
@@ -50,7 +50,7 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => {
setCodeUserKey,
setContractUserKey,
setProjectUserKey,
- setVerifyModuleTaskUserKey,
+ setMoveVerifyTaskUserKey,
]);
if (isHydrated && !(currentChainId in chainConfigs))
@@ -62,7 +62,7 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => {
!isCodeUserKeyExist() ||
!isContractUserKeyExist() ||
!isProjectUserKeyExist() ||
- !isVerifyModuleTaskUserKeyExist()
+ !isMoveVerifyTaskUserKeyExist()
)
return ;
diff --git a/src/lib/providers/store.tsx b/src/lib/providers/store.tsx
index b918f8651..5547bd7a0 100644
--- a/src/lib/providers/store.tsx
+++ b/src/lib/providers/store.tsx
@@ -70,7 +70,7 @@ export function useLocalChainConfigStore() {
return localChainConfigStore;
}
-export function useVerifyModuleTaskStore() {
- const { verifyModuleTaskStore } = useStore();
- return verifyModuleTaskStore;
+export function useMoveVerifyTaskStore() {
+ const { moveVerifyTaskStore } = useStore();
+ return moveVerifyTaskStore;
}
diff --git a/src/lib/services/chain-config/index.ts b/src/lib/services/chain-config/index.ts
index 2e29f7429..e064c4736 100644
--- a/src/lib/services/chain-config/index.ts
+++ b/src/lib/services/chain-config/index.ts
@@ -11,5 +11,7 @@ export const useApiChainConfigs = (chainIds: string[]) =>
{
retry: 1,
refetchOnWindowFocus: false,
+ refetchOnMount: false,
+ staleTime: Infinity,
}
);
diff --git a/src/lib/services/verification/move/index.ts b/src/lib/services/verification/move/index.ts
index 5b3d62ae1..fe537e80b 100644
--- a/src/lib/services/verification/move/index.ts
+++ b/src/lib/services/verification/move/index.ts
@@ -1,4 +1,4 @@
-import { useMutation, useQuery } from "@tanstack/react-query";
+import { useMutation, useQueries, useQuery } from "@tanstack/react-query";
import type { UseQueryResult } from "@tanstack/react-query";
import { CELATONE_QUERY_KEYS, useCelatoneApp } from "lib/app-provider";
@@ -21,6 +21,32 @@ export const useSubmitMoveVerify = () =>
mutationFn: submitMoveVerify,
});
+export const useMoveVerifyTaskInfos = (
+ taskIds: string[],
+ onSuccess?: (data: MoveVerifyByTaskIdResponse) => void
+) => {
+ const { chainConfig } = useCelatoneApp();
+ const {
+ extra: { layer },
+ } = chainConfig;
+
+ return useQueries({
+ queries: taskIds.map((taskId) => ({
+ queryKey: [
+ CELATONE_QUERY_KEYS.MOVE_VERIFY_TASK_BY_TASK_ID,
+ taskId,
+ layer,
+ ],
+ queryFn: () => getMoveVerifyByTaskId(taskId),
+ enabled: layer === "1",
+ retry: 0,
+ refetchOnWindowFocus: false,
+ keepPreviousData: true,
+ onSuccess,
+ })),
+ });
+};
+
export const useMoveVerifyTaskInfo = (
taskId: string,
enabled = true
diff --git a/src/lib/stores/root.ts b/src/lib/stores/root.ts
index e42b4032f..941ea2e03 100644
--- a/src/lib/stores/root.ts
+++ b/src/lib/stores/root.ts
@@ -5,7 +5,7 @@ import { ContractStore } from "./contract";
import { NetworkStore } from "./networks";
import { PublicProjectStore } from "./project";
import { SchemaStore } from "./schema";
-import { VerifyModuleTaskStore } from "./verify-module";
+import { MoveVerifyTaskStore } from "./verify-module";
export class RootStore {
accountStore: AccountStore;
@@ -22,7 +22,7 @@ export class RootStore {
localChainConfigStore: LocalChainConfigStore;
- verifyModuleTaskStore: VerifyModuleTaskStore;
+ moveVerifyTaskStore: MoveVerifyTaskStore;
constructor() {
this.accountStore = new AccountStore();
@@ -32,6 +32,6 @@ export class RootStore {
this.schemaStore = new SchemaStore();
this.networkStore = new NetworkStore();
this.localChainConfigStore = new LocalChainConfigStore();
- this.verifyModuleTaskStore = new VerifyModuleTaskStore();
+ this.moveVerifyTaskStore = new MoveVerifyTaskStore();
}
}
diff --git a/src/lib/stores/verify-module.test.ts b/src/lib/stores/verify-module.test.ts
index fa2c3d076..946361dea 100644
--- a/src/lib/stores/verify-module.test.ts
+++ b/src/lib/stores/verify-module.test.ts
@@ -1,66 +1,103 @@
-import { VerifyModuleTaskStore } from "./verify-module";
+import { MoveVerifyTaskStore } from "./verify-module";
-let verifyModuleTaskStore: VerifyModuleTaskStore;
+let moveVerifyTaskStore: MoveVerifyTaskStore;
beforeAll(() => {
- verifyModuleTaskStore = new VerifyModuleTaskStore();
+ moveVerifyTaskStore = new MoveVerifyTaskStore();
});
-describe("VerifyModuleTaskStore initialization", () => {
- test("Correctly initialize VerifyModuleTaskStore", () => {
- expect(verifyModuleTaskStore instanceof VerifyModuleTaskStore).toBeTruthy();
+describe("MoveVerifyTaskStore initialization", () => {
+ test("Correctly initialize MoveVerifyTaskStore", () => {
+ expect(moveVerifyTaskStore instanceof MoveVerifyTaskStore).toBeTruthy();
});
});
-describe("isVerifyModuleTaskUserKeyExist", () => {
+describe("isMoveVerifyTaskUserKeyExist", () => {
test("correctly check if user key exist", () => {
- expect(verifyModuleTaskStore.isVerifyModuleTaskUserKeyExist()).toBeFalsy();
- verifyModuleTaskStore.setVerifyModuleTaskUserKey("userKey");
- expect(verifyModuleTaskStore.isVerifyModuleTaskUserKeyExist()).toBeTruthy();
+ expect(moveVerifyTaskStore.isMoveVerifyTaskUserKeyExist()).toBeFalsy();
+ moveVerifyTaskStore.setMoveVerifyTaskUserKey("userKey");
+ expect(moveVerifyTaskStore.isMoveVerifyTaskUserKeyExist()).toBeTruthy();
});
});
-describe("verifyModule", () => {
+describe("verifyModuleTask", () => {
const verifyModule = {
taskId: "taskId",
- fileMap: { file: "map" },
+ fileMap: { "coin.move": "sources/coin.move", "Move.toml": "Move.toml" },
chainId: "chainId",
};
- test("correctly get verify modules", () => {
- expect(verifyModuleTaskStore.getVerifyModuleTasks()).toEqual([]);
+ test("correctly get verify module tasks", () => {
+ expect(moveVerifyTaskStore.latestMoveVerifyTasks()).toEqual([]);
});
- test("correctly get verify modules after adding", () => {
- verifyModuleTaskStore.addVerifyModuleTask(verifyModule);
- expect(verifyModuleTaskStore.getVerifyModuleTasks()).toEqual([
- verifyModule,
+ test("correctly get verify module tasks after adding new task", () => {
+ moveVerifyTaskStore.addMoveVerifyTask(verifyModule);
+ expect(
+ moveVerifyTaskStore.latestMoveVerifyTasks().map((task) => ({
+ taskId: task.taskId,
+ fileMap: task.fileMap,
+ chainId: task.chainId,
+ completed: task.completed,
+ }))
+ ).toEqual([
+ {
+ taskId: verifyModule.taskId,
+ fileMap: verifyModule.fileMap,
+ chainId: verifyModule.chainId,
+ completed: false,
+ },
]);
});
- test("correctly get verify modules after adding multiple", () => {
+ test("correctly get verify module tasks after adding multiple", () => {
const verifyModule1 = {
taskId: "taskId1",
- fileMap: { file: "map" },
+ fileMap: {
+ "common.move": "sources/common.move",
+ "Move.toml": "Move.toml",
+ },
chainId: "chainId",
};
const verifyModule2 = {
taskId: "taskId2",
- fileMap: { file: "map" },
+ fileMap: {
+ "simple.move": "sources/simple.move",
+ "Move.toml": "Move.toml",
+ },
chainId: "chainId",
};
- verifyModuleTaskStore.addVerifyModuleTask(verifyModule1);
- verifyModuleTaskStore.addVerifyModuleTask(verifyModule2);
- expect(verifyModuleTaskStore.getVerifyModuleTasks()).toEqual([
- verifyModule2,
- verifyModule1,
- verifyModule,
- ]);
+ moveVerifyTaskStore.addMoveVerifyTask(verifyModule1);
+ moveVerifyTaskStore.addMoveVerifyTask(verifyModule2);
+ expect(moveVerifyTaskStore.latestMoveVerifyTasks().length).toBe(3);
+
+ const actualVerifyModuleTask1 = moveVerifyTaskStore.getMoveVerifyTask(
+ verifyModule1.taskId
+ );
+ const actualVerifyModuleTask2 = moveVerifyTaskStore.getMoveVerifyTask(
+ verifyModule2.taskId
+ );
+
+ expect(actualVerifyModuleTask1?.taskId).toBe(verifyModule1.taskId);
+ expect(actualVerifyModuleTask1?.fileMap).toEqual(verifyModule1.fileMap);
+ expect(actualVerifyModuleTask1?.chainId).toBe(verifyModule1.chainId);
+ expect(actualVerifyModuleTask1?.completed).toBeFalsy();
+
+ expect(actualVerifyModuleTask2?.taskId).toBe(verifyModule2.taskId);
+ expect(actualVerifyModuleTask2?.fileMap).toEqual(verifyModule2.fileMap);
+ expect(actualVerifyModuleTask2?.chainId).toBe(verifyModule2.chainId);
+ expect(actualVerifyModuleTask2?.completed).toBeFalsy();
});
- test("correctly check if module is verified", () => {
+ test("correctly check if verify module tasks is added", () => {
expect(
- verifyModuleTaskStore.isVerifyModuleTaskExist(verifyModule.taskId)
+ moveVerifyTaskStore.isMoveVerifyTaskExist(verifyModule.taskId)
).toBeTruthy();
- expect(
- verifyModuleTaskStore.isVerifyModuleTaskExist("randomId")
- ).toBeFalsy();
+ expect(moveVerifyTaskStore.isMoveVerifyTaskExist("randomId")).toBeFalsy();
+ });
+ test("update verify module task", () => {
+ const verifiedAt = new Date();
+ moveVerifyTaskStore.completeMoveVerifyTask(verifyModule.taskId, verifiedAt);
+
+ const actual = moveVerifyTaskStore.getMoveVerifyTask(verifyModule.taskId);
+ expect(actual?.completed).toBeTruthy();
+ expect(actual?.verifiedAt).toEqual(verifiedAt);
});
});
diff --git a/src/lib/stores/verify-module.ts b/src/lib/stores/verify-module.ts
index 815eb430e..1c0279572 100644
--- a/src/lib/stores/verify-module.ts
+++ b/src/lib/stores/verify-module.ts
@@ -3,17 +3,20 @@ import { isHydrated, makePersistable } from "mobx-persist-store";
import type { Dict } from "lib/types";
-export interface VerifyModuleLocalInfo {
+export interface MoveVerifyTaskLocalInfo {
taskId: string;
requestNote?: string;
chainId: string;
fileMap: Record;
+ created: Date;
+ verifiedAt?: Date;
+ completed: boolean;
}
-export class VerifyModuleTaskStore {
+export class MoveVerifyTaskStore {
private userKey: string;
- modules: Dict;
+ modules: Dict;
constructor() {
this.userKey = "";
@@ -22,7 +25,7 @@ export class VerifyModuleTaskStore {
makeAutoObservable(this, {}, { autoBind: true });
makePersistable(this, {
- name: "VerifyModuleTaskStore",
+ name: "MoveVerifyTaskStore",
properties: ["modules"],
});
}
@@ -31,37 +34,61 @@ export class VerifyModuleTaskStore {
return isHydrated(this);
}
- isVerifyModuleTaskUserKeyExist(): boolean {
+ isMoveVerifyTaskUserKeyExist(): boolean {
return !!this.userKey;
}
- setVerifyModuleTaskUserKey(userKey: string) {
+ setMoveVerifyTaskUserKey(userKey: string) {
this.userKey = userKey;
}
- isVerifyModuleTaskExist(taskId: string): boolean {
+ isMoveVerifyTaskExist(taskId: string): boolean {
return (
- this.getVerifyModuleTasks().findIndex((item) => item.taskId === taskId) >
- -1
+ this.getMoveVerifyTasks().findIndex((item) => item.taskId === taskId) > -1
);
}
- getVerifyModuleTasks(): VerifyModuleLocalInfo[] {
- return this.modules[this.userKey]?.reverse() ?? [];
+ getMoveVerifyTasks(): MoveVerifyTaskLocalInfo[] {
+ return this.modules[this.userKey] ?? [];
}
- getVerifyModuleTask(taskId: string): VerifyModuleLocalInfo | undefined {
- return this.getVerifyModuleTasks().find(
- (module) => module.taskId === taskId
- );
+ latestMoveVerifyTasks(): MoveVerifyTaskLocalInfo[] {
+ return this.getMoveVerifyTasks().slice().reverse();
+ }
+
+ getMoveVerifyTask(taskId: string): MoveVerifyTaskLocalInfo | undefined {
+ return this.getMoveVerifyTasks().find((module) => module.taskId === taskId);
}
- addVerifyModuleTask(verifyModule: VerifyModuleLocalInfo): void {
- if (!this.isVerifyModuleTaskExist(verifyModule.taskId)) {
+ addMoveVerifyTask(
+ verifyModule: Omit<
+ MoveVerifyTaskLocalInfo,
+ "created" | "completed" | "verifiedAt"
+ >
+ ): void {
+ if (!this.isMoveVerifyTaskExist(verifyModule.taskId)) {
this.modules[this.userKey] = [
- ...this.getVerifyModuleTasks(),
- verifyModule,
+ ...this.getMoveVerifyTasks(),
+ { ...verifyModule, created: new Date(), completed: false },
];
}
}
+
+ completeMoveVerifyTask(taskId: string, verifiedAt?: Date): void {
+ const modules = this.getMoveVerifyTasks().map((module) =>
+ module.taskId === taskId
+ ? { ...module, verifiedAt, completed: true }
+ : module
+ );
+ this.modules[this.userKey] = modules;
+ }
+
+ updateRequestNote(taskId: string, newRequestNote?: string): void {
+ const modules = this.getMoveVerifyTasks().map((module) =>
+ module.taskId === taskId
+ ? { ...module, requestNote: newRequestNote }
+ : module
+ );
+ this.modules[this.userKey] = modules;
+ }
}