diff --git a/web-devtools/.env.local.example b/web-devtools/.env.local.example index 7e7efe9fa..acab203c2 100644 --- a/web-devtools/.env.local.example +++ b/web-devtools/.env.local.example @@ -2,4 +2,5 @@ export NEXT_PUBLIC_ALCHEMY_API_KEY= export NEXT_PUBLIC_DEPLOYMENT=devnet export NEXT_PUBLIC_CORE_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-core-devnet/version/latest -export NEXT_PUBLIC_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest \ No newline at end of file +export NEXT_PUBLIC_DRT_ARBSEPOLIA_SUBGRAPH=https://api.studio.thegraph.com/query/61738/kleros-v2-drt-arbisep-devnet/version/latest +export NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID= \ No newline at end of file diff --git a/web-devtools/package.json b/web-devtools/package.json index 4513cdc6b..b01cede32 100644 --- a/web-devtools/package.json +++ b/web-devtools/package.json @@ -30,19 +30,28 @@ "@types/node": "^20", "@types/react": "18.2.0", "@types/react-dom": "^18.2.18", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "@typescript-eslint/utils": "^5.62.0", "@wagmi/cli": "^2.0.3", + "eslint": "^8.56.0", "eslint-config-next": "^14.2.5", + "eslint-config-prettier": "^8.10.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", "ts-node": "^10.9.2", "typescript": "^5.5.3" }, "dependencies": { "@kleros/kleros-sdk": "workspace:^", - "@kleros/ui-components-library": "^2.10.0", + "@kleros/ui-components-library": "^2.15.0", "graphql": "^16.9.0", "next": "14.2.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.7", + "react-toastify": "^10.0.5", "typewriter-effect": "^2.21.0", "vanilla-jsoneditor": "^0.21.4", "viem": "^2.1.0", diff --git a/web-devtools/src/app/(main)/layout.tsx b/web-devtools/src/app/(main)/layout.tsx index dcd0d0f0f..3d754b11f 100644 --- a/web-devtools/src/app/(main)/layout.tsx +++ b/web-devtools/src/app/(main)/layout.tsx @@ -2,22 +2,35 @@ import React from "react"; import styled from "styled-components"; +import "react-toastify/dist/ReactToastify.css"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ToastContainer } from "react-toastify"; import GraphqlBatcherProvider from "context/GraphqlBatcher"; +import Web3Provider from "context/Web3Provider"; const Main = styled.main` min-height: calc(100vh - 130px); `; const queryClient = new QueryClient(); +const StyledToastContainer = styled(ToastContainer)` + padding: 16px; + padding-top: 70px; +`; + const Layout = ({ children }: Readonly<{ children: React.ReactNode }>) => { return ( - - -
{children}
-
-
+ + + +
+ + {children} +
+
+
+
); }; diff --git a/web-devtools/src/app/(main)/ruler/ChangeDeveloper.tsx b/web-devtools/src/app/(main)/ruler/ChangeDeveloper.tsx index 8d9624ff7..6b6f62edc 100644 --- a/web-devtools/src/app/(main)/ruler/ChangeDeveloper.tsx +++ b/web-devtools/src/app/(main)/ruler/ChangeDeveloper.tsx @@ -1,11 +1,20 @@ -import React from "react"; +import React, { useCallback, useMemo, useState } from "react"; import styled from "styled-components"; +import { Address, isAddress } from "viem"; +import { useAccount, usePublicClient } from "wagmi"; + import { Button } from "@kleros/ui-components-library"; +import { useRulerContext } from "context/RulerContext"; +import { useSimulateKlerosCoreRulerChangeRuler, useWriteKlerosCoreRulerChangeRuler } from "hooks/contracts/generated"; +import { isUndefined } from "utils/isUndefined"; +import { wrapWithToast } from "utils/wrapWithToast"; + import LabeledInput from "components/LabeledInput"; import Header from "./Header"; +import { DEFAULT_CHAIN } from "consts/chains"; const Container = styled.div` width: 100%; @@ -25,14 +34,60 @@ const StyledLabel = styled.label` `; const ChangeDeveloper: React.FC = () => { + const { isConnected, chainId } = useAccount(); + const { arbitrable, currentDeveloper, refetchData } = useRulerContext(); + const [newDeveloper, setNewDeveloper] = useState(""); + const [isChanging, setIsChanging] = useState(false); + const publicClient = usePublicClient(); + + const isValid = useMemo(() => newDeveloper === "" || isAddress(newDeveloper), [newDeveloper]); + + const { + data: changeRulerConfig, + isLoading, + isError, + } = useSimulateKlerosCoreRulerChangeRuler({ + query: { + enabled: !isUndefined(arbitrable) && !isUndefined(newDeveloper) && isAddress(newDeveloper), + }, + args: [(arbitrable ?? "") as Address, newDeveloper as Address], + }); + + const { writeContractAsync: changeRuler } = useWriteKlerosCoreRulerChangeRuler(); + + const handleClick = useCallback(() => { + if (!publicClient || !changeRulerConfig) return; + setIsChanging(true); + wrapWithToast(async () => changeRuler(changeRulerConfig.request), publicClient) + .then(() => refetchData()) + .finally(() => setIsChanging(false)); + }, [publicClient, changeRulerConfig, changeRuler, refetchData]); + + const isDisabled = useMemo( + () => + !isConnected || + chainId !== DEFAULT_CHAIN || + !changeRulerConfig || + isError || + isLoading || + isChanging || + isUndefined(arbitrable) || + !isValid, + [changeRulerConfig, isError, isLoading, isChanging, arbitrable, isValid, isConnected, chainId] + ); return ( -
+
- Current Developer : 0xbe8d95497E53aB41d5A45CC8def90d0e59b49f99 - + Current Developer : {currentDeveloper ?? "None"} + setNewDeveloper(e.target.value)} + message={isValid ? "" : "Invalid Address"} + variant={isValid ? "" : "error"} + /> -