diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e8b9f81b..101a6893a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ As a minor extension, we also keep a semantic version for the `UNRELEASED` changes. ## [Unreleased] +- Add possibility to vote on behalf of myself - Sole Voter [Issue 119](https://github.com/IntersectMBO/govtool/issues/119) +- Create DRep registration page about roles [Issue 205](https://github.com/IntersectMBO/govtool/issues/205) - Create Checkbox component. Improve Field and ControlledField [Issue 177](https://github.com/IntersectMBO/govtool/pull/177) - Vitest unit tests added for utils functions [Issue 81](https://github.com/IntersectMBO/govtool/issues/81) - i18next library added to FE [Issue 80](https://github.com/IntersectMBO/govtool/issues/80) @@ -17,13 +19,16 @@ changes. - Added `isRegisteredAsSoleVoter` and `wasRegisteredAsSoleVoter` fields to the drep/info response [Issue 212](https://github.com/IntersectMBO/govtool/issues/212) ### Fixed +- Fix make button disble when wallet tries connect [Issue 265](https://github.com/IntersectMBO/govtool/issues/265) - Fix drep voting power calculation [Issue 231](https://github.com/IntersectMBO/govtool/issues/231) - Fix proposal/list and network/metrics bug that appeared when noone has delegated their funds either to drep_always_abstain or drep_always_no_confidence [Issue 231](https://github.com/IntersectMBO/govtool/issues/231) +- Fix GA details [Issue 272](https://github.com/IntersectMBO/govtool/issues/272) - Fix copy for maintenance page [Issue 180](https://github.com/IntersectMBO/govtool/issues/180) - Fix misleading metadata hash text [Issue 90](https://github.com/IntersectMBO/govtool/issues/90) - Fixed vote calculation problems related to NoConfidence DRep [Issue 59](https://github.com/IntersectMBO/govtool/issues/59) - Fixed ada-holder/get-current-delegation error when delegated to NoConfidence or AlwaysAbstain dreps. [Issue 82](https://github.com/IntersectMBO/govtool/issues/82) - Fixed deployment scripts to address [Issue 171](https://github.com/IntersectMBO/govtool/issues/171). +- Fixed get drep voting power incorrectly executed endpoint [Issue 280](https://github.com/IntersectMBO/govtool/issues/280) ### Changed - `isRegistered` and `wasRegistered` fields in the drep/info endpoint changed to `isRegisteredAsDRep` and `wasRegisteredAsDRep` respectively [Issue 212](https://github.com/IntersectMBO/govtool/issues/212) diff --git a/govtool/backend/Dockerfile b/govtool/backend/Dockerfile index d51a9307e..75600043c 100644 --- a/govtool/backend/Dockerfile +++ b/govtool/backend/Dockerfile @@ -1,5 +1,6 @@ -FROM haskell:9.2-buster +ARG BASE_IMAGE_TAG +FROM 733019650473.dkr.ecr.eu-west-1.amazonaws.com/backend-base:$BASE_IMAGE_TAG WORKDIR /src COPY . . -RUN cabal update && cabal configure && cabal build +RUN cabal build RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.8/vva-be-0.1.0.0/x/vva-be/build/vva-be/vva-be /usr/local/bin diff --git a/govtool/backend/Dockerfile.base b/govtool/backend/Dockerfile.base new file mode 100644 index 000000000..e9b93f093 --- /dev/null +++ b/govtool/backend/Dockerfile.base @@ -0,0 +1,10 @@ +# NOTE: This Dockerfile sets up an environment with precompiled dependencies for +# the GovTool Haskell backend project, streamlining the project's compilation +# process by ensuring it only needs to compile against these dependencies. This +# is a common practice in Haskell projects, as it can significantly reduce the +# time it takes to build the project. + +FROM haskell:9.2-buster +WORKDIR /src +COPY . . +RUN cabal update && cabal configure && cabal install --only-dependencies && rm -rf /src/* diff --git a/govtool/backend/sql/get-drep-info.sql b/govtool/backend/sql/get-drep-info.sql index e1bfcbbc6..210803c08 100644 --- a/govtool/backend/sql/get-drep-info.sql +++ b/govtool/backend/sql/get-drep-info.sql @@ -9,6 +9,7 @@ WITH DRepId AS ( CROSS JOIN DRepId WHERE drep_hash.raw = DRepId.raw and deposit is not null + and drep_registration.voting_anchor_id is not null ORDER BY drep_registration.tx_id DESC LIMIT 1 ), WasRegisteredAsDRep AS ( diff --git a/govtool/frontend/public/icons/ArrowLeftThin.svg b/govtool/frontend/public/icons/ArrowLeftThin.svg new file mode 100644 index 000000000..38c648693 --- /dev/null +++ b/govtool/frontend/public/icons/ArrowLeftThin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/govtool/frontend/public/images/GovActionsSoleVoter.png b/govtool/frontend/public/images/GovActionsSoleVoter.png new file mode 100644 index 000000000..69048a4c3 Binary files /dev/null and b/govtool/frontend/public/images/GovActionsSoleVoter.png differ diff --git a/govtool/frontend/src/App.tsx b/govtool/frontend/src/App.tsx index 91dab6356..a282b9e74 100644 --- a/govtool/frontend/src/App.tsx +++ b/govtool/frontend/src/App.tsx @@ -21,6 +21,7 @@ import { UpdatedRepMetadata, GovernanceActionsCategory, DashboardGovernanceActionsCategory, + RetireAsSoleVoter, } from "@pages"; import { callAll, @@ -30,9 +31,10 @@ import { } from "@utils"; import { SetupInterceptors } from "./services"; import { useGetDRepInfo, useWalletConnectionListener } from "./hooks"; +import { RegisterAsSoleVoter } from "./pages/RegisterAsSoleVoter"; export default function App() { - const { enable, setDRep, setIsDrepLoading } = useCardano(); + const { enable, setVoter, setIsDrepLoading } = useCardano(); const navigate = useNavigate(); const { data } = useGetDRepInfo(); const { modal, openModal, modals } = useModal(); @@ -45,11 +47,11 @@ export default function App() { useEffect(() => { setIsDrepLoading(true); - setDRep(data); + setVoter(data); const timer = setTimeout(() => setIsDrepLoading(false), 1000); return () => clearTimeout(timer); - }, [data?.isRegistered]); + }, [data?.isRegisteredAsDRep, data?.isRegisteredAsSoleVoter]); const checkTheWalletIsActive = useCallback(() => { const hrefCondition = @@ -115,6 +117,11 @@ export default function App() { } /> } /> + } + /> + } /> } /> } /> } /> diff --git a/govtool/frontend/src/components/atoms/VotingPowerChips.tsx b/govtool/frontend/src/components/atoms/VotingPowerChips.tsx index 897ec0ce8..ad707be93 100644 --- a/govtool/frontend/src/components/atoms/VotingPowerChips.tsx +++ b/govtool/frontend/src/components/atoms/VotingPowerChips.tsx @@ -13,7 +13,7 @@ import { correctAdaFormat } from "@utils"; import { Tooltip } from "@atoms"; export const VotingPowerChips = () => { - const { dRep, stakeKey, isDrepLoading } = useCardano(); + const { voter, stakeKey, isDrepLoading } = useCardano(); const { dRepVotingPower, isDRepVotingPowerLoading } = useGetDRepVotingPowerQuery(); const { votingPower, powerIsLoading } = @@ -33,7 +33,7 @@ export const VotingPowerChips = () => { alignItems="center" maxHeight={isMobile ? undefined : 48} > - {dRep?.isRegistered && ( + {voter?.isRegisteredAsDRep && ( { {t("votingPower")} )} - {(dRep?.isRegistered && isDRepVotingPowerLoading) || - (!dRep?.isRegistered && powerIsLoading) || + {(voter?.isRegisteredAsDRep && isDRepVotingPowerLoading) || + (!voter?.isRegisteredAsDRep && powerIsLoading) || isDrepLoading ? ( ) : ( @@ -67,7 +67,7 @@ export const VotingPowerChips = () => { sx={{ whiteSpace: "nowrap" }} > ₳{" "} - {dRep?.isRegistered + {voter?.isRegisteredAsDRep ? correctAdaFormat(dRepVotingPower) ?? 0 : correctAdaFormat(votingPower) ?? 0} diff --git a/govtool/frontend/src/components/molecules/ActionCard.tsx b/govtool/frontend/src/components/molecules/ActionCard.tsx index 63fc9eb03..3418c6471 100644 --- a/govtool/frontend/src/components/molecules/ActionCard.tsx +++ b/govtool/frontend/src/components/molecules/ActionCard.tsx @@ -94,7 +94,6 @@ export const ActionCard: FC = ({ ...props }) => { + ); + }, [isMobile]); + + const renderActionButton = useMemo(() => { + return ( + + {actionButtonText ?? t("continue")} + + ); + }, [isLoading, isMobile]); + + return ( + + {renderBackButton} + + {renderActionButton} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/CenteredBoxPageWrapper.tsx b/govtool/frontend/src/components/molecules/CenteredBoxPageWrapper.tsx new file mode 100644 index 000000000..dc9cbe0da --- /dev/null +++ b/govtool/frontend/src/components/molecules/CenteredBoxPageWrapper.tsx @@ -0,0 +1,108 @@ +import { FC, PropsWithChildren } from "react"; +import { Box, Link } from "@mui/material"; + +import { Background, Typography } from "@atoms"; +import { ICONS } from "@consts"; +import { DashboardTopNav } from "@organisms"; +import { useScreenDimension } from "@hooks"; +import { useNavigate } from "react-router-dom"; +import { theme } from "@/theme"; + +interface Props { + pageTitle: string; + backButtonText: string; + backButtonPath: string; + isVotingPowerHidden?: boolean; +} +export const CenteredBoxPageWrapper: FC> = ({ + pageTitle, + backButtonText, + backButtonPath, + isVotingPowerHidden, + children, +}) => { + const { isMobile, screenWidth, pagePadding } = useScreenDimension(); + const navigate = useNavigate(); + const { + palette: { boxShadow2 }, + } = theme; + + return ( + + + + + {isMobile && ( + + + {pageTitle} + + + )} + navigate(backButtonPath)} + > + arrow + + {backButtonText} + + + + + + {children} + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx index 3d3e9fc26..d394c12a4 100644 --- a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx +++ b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx @@ -2,7 +2,7 @@ import { Box, ButtonProps, Skeleton } from "@mui/material"; import { FC, ReactNode } from "react"; import { CopyButton, LoadingButton, Typography } from "@atoms"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { theme } from "@/theme"; type DashboardActionCardProps = { @@ -18,9 +18,7 @@ type DashboardActionCardProps = { firstButtonIsLoading?: boolean; firstButtonLabel?: string; firstButtonVariant?: ButtonProps["variant"]; - imageHeight?: number; imageURL?: string; - imageWidth?: number; inProgress?: boolean; isLoading?: boolean; secondButtonAction?: () => void; @@ -33,6 +31,7 @@ type DashboardActionCardProps = { export const DashboardActionCard: FC = ({ ...props }) => { + const { t } = useTranslation(); const { cardId, cardTitle, @@ -62,17 +61,15 @@ export const DashboardActionCard: FC = ({ return ( {inProgress && !isLoading && ( = ({ }} > - In progress + {t("inProgress").toLocaleUpperCase()} )} @@ -116,7 +113,7 @@ export const DashboardActionCard: FC = ({ ) : null} {inProgress && !isLoading ? ( - in progress + In Progress ) : null} {description ? ( diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 3c1645bd2..920021f24 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -25,7 +25,7 @@ export const VoteActionForm = ({ const [isContext, setIsContext] = useState(false); const { isMobile, screenWidth } = useScreenDimension(); const { openModal } = useModal(); - const { dRep } = useCardano(); + const { voter } = useCardano(); const { t } = useTranslation(); const { @@ -142,7 +142,7 @@ export const VoteActionForm = ({ /> - {dRep?.isRegistered && ( + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && ( + ); + }, [isMobile]); + + const renderContinueButton = useMemo(() => { + return ( + + {actionButtonLabel} + + ); + }, [isLoadingActionButton, isMobile, actionButtonLabel]); + + return ( + = 768 ? "center" : "inherit", + display: "flex", + flex: 1, + flexDirection: "column", + marginTop: isMobile ? "97px" : "137px", + }} + > + 768 ? 600 : undefined} + mb={isMobile ? undefined : 3} + pb={isMobile ? undefined : 10} + pt={isMobile ? 6 : 10} + px={isMobile ? 2 : 18.75} + sx={sx} + > + + {children} + + + {renderBackButton} + {renderContinueButton} + + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/DashboardCards.tsx b/govtool/frontend/src/components/organisms/DashboardCards.tsx index a4bde49da..34018f07a 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards.tsx @@ -1,5 +1,6 @@ +import { useCallback, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Box, CircularProgress, Typography } from "@mui/material"; +import { Box, CircularProgress } from "@mui/material"; import { Trans } from "react-i18next"; import { IMAGES, PATHS } from "@consts"; @@ -11,7 +12,6 @@ import { useTranslation, } from "@hooks"; import { DashboardActionCard } from "@molecules"; -import { useCallback, useMemo, useState } from "react"; import { correctAdaFormat, formHexToBech32, openInNewTab } from "@utils"; export const DashboardCards = () => { @@ -20,18 +20,19 @@ export const DashboardCards = () => { buildSignSubmitConwayCertTx, delegateTo, delegateTransaction, - dRep, + voter, dRepID, dRepIDBech32, isDrepLoading, isPendingTransaction, registerTransaction, + soleVoterTransaction, stakeKey, } = useCardano(); const navigate = useNavigate(); const { currentDelegation, isCurrentDelegationLoading } = useGetAdaHolderCurrentDelegationQuery(stakeKey); - const { screenWidth, isMobile } = useScreenDimension(); + const { screenWidth } = useScreenDimension(); const { openModal } = useModal(); const [isRetirementLoading, setIsRetirementLoading] = useState(false); @@ -223,7 +224,7 @@ export const DashboardCards = () => { default: return t("dashboard.registration.metadataUpdateInProgress"); } - } else if (dRep?.isRegistered || dRep?.wasRegistered) { + } else if (voter?.isRegisteredAsDRep || voter?.wasRegisteredAsDRep) { return t("dashboard.registration.holdersCanDelegate"); } else { return t("dashboard.registration.ifYouWant"); @@ -231,8 +232,30 @@ export const DashboardCards = () => { }, [ registerTransaction.transactionHash, registerTransaction.type, - dRep?.isRegistered, - dRep?.wasRegistered, + voter?.isRegisteredAsDRep, + voter?.wasRegisteredAsDRep, + ]); + + const soleVoterCardDescription = useMemo(() => { + if (soleVoterTransaction.transactionHash) { + switch (soleVoterTransaction.type) { + case "retirement": + return "dashboard.soleVoter.retirementInProgress"; + default: + return "dashboard.soleVoter.registrationInProgress"; + } + } else if (voter?.isRegisteredAsSoleVoter) { + return "dashboard.soleVoter.isRegisteredDescription"; + } else if (voter?.wasRegisteredAsSoleVoter && !voter?.isRegisteredAsDRep) { + return "dashboard.soleVoter.wasRegisteredDescription"; + } else { + return "dashboard.soleVoter.registerDescription"; + } + }, [ + soleVoterTransaction.transactionHash, + soleVoterTransaction.type, + voter?.isRegisteredAsSoleVoter, + voter?.wasRegisteredAsSoleVoter, ]); const registrationCardTitle = useMemo(() => { @@ -245,9 +268,9 @@ export const DashboardCards = () => { default: return t("dashboard.registration.dRepUpdate"); } - } else if (dRep?.isRegistered) { + } else if (voter?.isRegisteredAsDRep) { return t("dashboard.registration.youAreRegistered"); - } else if (dRep?.wasRegistered) { + } else if (voter?.wasRegisteredAsDRep) { return t("dashboard.registration.registerAgain"); } else { return t("dashboard.registration.registerAsDRep"); @@ -255,41 +278,31 @@ export const DashboardCards = () => { }, [ registerTransaction?.transactionHash, registerTransaction.type, - dRep?.isRegistered, - dRep?.wasRegistered, + voter?.isRegisteredAsDRep, + voter?.wasRegisteredAsDRep, ]); - const renderGovActionSection = useCallback(() => { - return ( - <> - - {t("dashboard.headingTwo")} - - - - navigate(PATHS.dashboard_governance_actions) - } - firstButtonLabel={t( - `dashboard.govActions.${ - dRep?.isRegistered ? "reviewAndVote" : "view" - }` - )} - imageURL={IMAGES.govActionListImage} - title={t("dashboard.govActions.title")} - /> - {screenWidth < 1024 ? null : ( - <> - - - - )} - - - ); - }, [screenWidth, isMobile, dRep?.isRegistered]); + const soleVoterCardTitle = useMemo(() => { + if (soleVoterTransaction?.transactionHash) { + switch (soleVoterTransaction.type) { + case "retirement": + return t("dashboard.soleVoter.retirement"); + default: + return t("dashboard.soleVoter.registration"); + } + } else if (voter?.isRegisteredAsSoleVoter) { + return t("dashboard.soleVoter.youAreSoleVoterTitle"); + } else if (voter?.wasRegisteredAsSoleVoter && !voter?.isRegisteredAsDRep) { + return t("dashboard.soleVoter.retireTitle"); + } else { + return t("dashboard.soleVoter.registerTitle"); + } + }, [ + soleVoterTransaction?.transactionHash, + soleVoterTransaction.type, + voter?.isRegisteredAsSoleVoter, + voter?.isRegisteredAsSoleVoter, + ]); return isDrepLoading ? ( { ) : ( - {dRep?.isRegistered && renderGovActionSection()} - - {t("dashboard.headingOne")} - - - navigateTo(PATHS.delegateTodRep)} - firstButtonLabel={ - delegateTransaction?.transactionHash - ? "" - : currentDelegation - ? t("dashboard.delegation.changeDelegation") - : t("delegate") - } - imageHeight={55} - imageWidth={65} - firstButtonVariant={currentDelegation ? "outlined" : "contained"} - imageURL={IMAGES.govActionDelegateImage} - cardId={displayedDelegationId} - inProgress={!!delegateTransaction?.transactionHash} - cardTitle={t("dashboard.delegation.dRepDelegatedTo")} - secondButtonAction={ - delegateTransaction?.transactionHash - ? () => openInNewTab("https://adanordic.com/latest_transactions") - : () => - openInNewTab( - "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power" - ) - } - secondButtonLabel={ - delegateTransaction?.transactionHash - ? t("seeTransaction") - : currentDelegation - ? "" - : t("learnMore") - } - title={ - delegateTransaction?.transactionHash ? ( - t("dashboard.delegation.votingPowerDelegation") - ) : currentDelegation ? ( - - ) : ( - t("dashboard.delegation.useYourVotingPower") - ) - } - /> - - navigateTo(PATHS.registerAsdRep) - } - firstButtonIsLoading={isRetirementLoading} - firstButtonLabel={ - registerTransaction?.transactionHash - ? "" - : t( - `dashboard.registration.${ - dRep?.isRegistered ? "retire" : "register" - }` + {/* DELEGATION CARD */} + navigateTo(PATHS.delegateTodRep)} + firstButtonLabel={ + delegateTransaction?.transactionHash + ? "" + : currentDelegation + ? t("dashboard.delegation.changeDelegation") + : t("delegate") + } + firstButtonVariant={currentDelegation ? "outlined" : "contained"} + imageURL={IMAGES.govActionDelegateImage} + cardId={displayedDelegationId} + inProgress={!!delegateTransaction?.transactionHash} + cardTitle={t("dashboard.delegation.dRepDelegatedTo")} + secondButtonAction={ + delegateTransaction?.transactionHash + ? () => openInNewTab("https://adanordic.com/latest_transactions") + : () => + openInNewTab( + "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power" ) - } - inProgress={!!registerTransaction?.transactionHash} - imageURL={IMAGES.govActionRegisterImage} - secondButtonAction={ - registerTransaction?.transactionHash - ? () => openInNewTab("https://adanordic.com/latest_transactions") - : dRep?.isRegistered - ? () => { - navigateTo(PATHS.updateMetadata); - } - : () => - openInNewTab( - "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep" - ) - } - secondButtonLabel={ - registerTransaction?.transactionHash - ? t("seeTransaction") - : dRep?.isRegistered - ? t("dashboard.registration.changeMetadata") - : t("learnMore") - } - cardId={dRep?.isRegistered || dRep?.wasRegistered ? dRepIDBech32 : ""} - cardTitle={ - dRep?.isRegistered || dRep?.wasRegistered ? t("myDRepId") : "" - } - title={registrationCardTitle} - /> - - {!dRep?.isRegistered && renderGovActionSection()} + } + secondButtonLabel={ + delegateTransaction?.transactionHash + ? t("seeTransaction") + : currentDelegation + ? "" + : t("learnMore") + } + title={ + delegateTransaction?.transactionHash ? ( + t("dashboard.delegation.votingPowerDelegation") + ) : currentDelegation ? ( + + ) : ( + t("dashboard.delegation.useYourVotingPower") + ) + } + /> + {/* DELEGATION CARD END*/} + {/* REGISTARTION AS DREP CARD */} + navigateTo(PATHS.registerAsdRep) + } + firstButtonIsLoading={isRetirementLoading} + firstButtonLabel={ + registerTransaction?.transactionHash + ? "" + : t( + `dashboard.registration.${ + voter?.isRegisteredAsDRep ? "retire" : "register" + }` + ) + } + inProgress={!!registerTransaction?.transactionHash} + imageURL={IMAGES.govActionRegisterImage} + secondButtonAction={ + registerTransaction?.transactionHash + ? () => openInNewTab("https://adanordic.com/latest_transactions") + : voter?.isRegisteredAsDRep + ? () => { + navigateTo(PATHS.updateMetadata); + } + : () => + openInNewTab( + "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep" + ) + } + secondButtonLabel={ + registerTransaction?.transactionHash + ? t("seeTransaction") + : voter?.isRegisteredAsDRep + ? t("dashboard.registration.changeMetadata") + : t("learnMore") + } + cardId={ + voter?.isRegisteredAsDRep || voter?.wasRegisteredAsDRep + ? dRepIDBech32 + : "" + } + cardTitle={ + voter?.isRegisteredAsDRep || voter?.wasRegisteredAsDRep + ? t("myDRepId") + : "" + } + title={registrationCardTitle} + /> + {/* DREP CARD END*/} + {/* SOLE VOTER CARD */} + + } + firstButtonLabel={ + soleVoterTransaction?.transactionHash + ? "" + : t( + voter?.isRegisteredAsSoleVoter + ? "dashboard.soleVoter.retire" + : voter?.wasRegisteredAsSoleVoter && + !voter?.isRegisteredAsDRep + ? "dashboard.soleVoter.reRegister" + : "dashboard.soleVoter.register" + ) + } + firstButtonAction={() => + navigateTo( + voter?.isRegisteredAsSoleVoter + ? PATHS.retireAsSoleVoter + : PATHS.registerAsSoleVoter + ) + } + firstButtonVariant={ + voter?.isRegisteredAsSoleVoter ? "outlined" : "contained" + } + secondButtonLabel={t("learnMore")} + secondButtonAction={() => + openInNewTab( + "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep" + ) + } + secondButtonVariant={"outlined"} + imageURL={IMAGES.soleVoterImage} + /> + {/* REGISTARTION AS SOLE VOTER CARD END*/} + {/* GOV ACTIONS LIST CARD */} + navigate(PATHS.dashboard_governance_actions)} + firstButtonLabel={t( + `dashboard.govActions.${ + voter?.isRegisteredAsDRep ? "reviewAndVote" : "view" + }` + )} + imageURL={IMAGES.govActionListImage} + title={t("dashboard.govActions.title")} + /> + {/* GOV ACTIONS LIST CARD END*/} ); }; diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx index a15148f94..413f76818 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx @@ -28,7 +28,7 @@ import { } from "@utils"; export const DashboardGovernanceActionDetails = () => { - const { dRep } = useCardano(); + const { voter } = useCardano(); const { state, hash } = useLocation(); const navigate = useNavigate(); const { isMobile, screenWidth } = useScreenDimension(); @@ -134,7 +134,7 @@ export const DashboardGovernanceActionDetails = () => { ? formatDisplayDate(state.expiryDate) : formatDisplayDate(data.proposal.expiryDate) } - isDRep={dRep?.isRegistered} + isDRep={voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter} noVotes={state ? state.noVotes : data.proposal.noVotes} type={ state diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index d942dd9a4..c8b18afb6 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -65,7 +65,7 @@ export const DashboardGovernanceActions = () => { state && state.isVotedListOnLoad ? 1 : 0 ); - const { dRep, isDrepLoading } = useCardano(); + const { voter, isDrepLoading } = useCardano(); const { isMobile } = useScreenDimension(); const { t } = useTranslation(); @@ -121,7 +121,7 @@ export const DashboardGovernanceActions = () => { sortingActive={Boolean(chosenSorting)} sortOpen={sortOpen} /> - {dRep?.isRegistered && ( + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && ( { const { isMobile, screenWidth } = useScreenDimension(); - const { dRep } = useCardano(); + const { voter } = useCardano(); const navigate = useNavigate(); const [isDrawerOpen, setIsDrawerOpen] = useState(false); const { t } = useTranslation(); @@ -42,11 +44,12 @@ export const DashboardTopNav = ({ alignItems={"center"} justifyContent={"space-between"} borderBottom={1} - borderColor={"#D6E2FF"} + borderColor="#D6E2FF" position="fixed" zIndex={100} flex={1} width={"fill-available"} + height={"48px"} > {imageSRC ? ( @@ -66,7 +69,7 @@ export const DashboardTopNav = ({ ) : null} - + {!isVotingPowerHidden && } {isMobile && ( - {dRep?.isRegistered && } + {(voter?.isRegisteredAsDRep || + voter?.isRegisteredAsSoleVoter) && } diff --git a/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx b/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx index 2e333a0c1..3f38f8ba8 100644 --- a/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx +++ b/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx @@ -21,7 +21,7 @@ interface DelegateProps { export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { const navigate = useNavigate(); const { - dRep, + voter, dRepID, buildSignSubmitConwayCertTx, buildVoteDelegationCert, @@ -133,7 +133,8 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { }, [ chosenOption, delegate, - dRep?.isRegistered, + voter?.isRegisteredAsDRep, + voter?.isRegisteredAsSoleVoter, dRepID, isDelegationLoading, isMobile, @@ -177,9 +178,9 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { @@ -211,24 +212,27 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { - {dRep?.isRegistered && currentDelegation !== dRepID && ( - - - - )} + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && + currentDelegation !== dRepID && ( + + + + )} { - const { dRep } = useCardano(); + const { voter } = useCardano(); const { t } = useTranslation(); return ( @@ -61,7 +61,7 @@ export const Drawer = () => { ))} - {dRep?.isRegistered && } + {voter?.isRegisteredAsDRep && } diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx index af7861853..4dba451c0 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx @@ -1,7 +1,7 @@ import { Box } from "@mui/material"; import { Button, Typography } from "../atoms"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { GovActionDetails, VoteActionForm, VotesSubmitted } from "../molecules"; +import { VoteActionForm, VotesSubmitted } from "../molecules"; import { useModal } from "@context"; import { useScreenDimension, useTranslation } from "@hooks"; import { ICONS } from "@consts"; @@ -164,22 +164,18 @@ export const GovernanceActionDetailsCard = ({ {t("govActions.details")} - {typeof details === "object" && details !== null ? ( - Object.entries(details).map(([key, value]) => { - return ( -
- {} -
- ); - }) - ) : ( + - {details} + {JSON.stringify(details, null, 1)} - )} +
- ); - }, [isMobile]); + const deposit = getItemFromLocalStorage(PROTOCOL_PARAMS_KEY); - const renderConfirmButton = useMemo(() => { - return ( - - ); - }, [isMobile, isValid, showSubmitButton]); + const onClickContinue = useCallback(() => setStep(2), []); + + const openLearMoreAboutDrep = useCallback( + () => openInNewTab("https://sancho.network/roles/drep"), + [] + ); return ( - - - - {t("registration.optional")} - - - {t("registration.headingStepOne")} - - - {t("registration.descriptionStepOne")} - - - - - - openInNewTab( - "https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor" - ) - } - alignSelf={"center"} - mt={5} - sx={{ cursor: "pointer" }} - > - - {t("forms.howCreateUrlAndHash")} - - - - + {t("registration.rolesAndResponsibilitiesTitle")} + + - {isMobile ? renderConfirmButton : renderCancelButton} - - {isMobile ? renderCancelButton : renderConfirmButton} - - + , + ]} + i18nKey={"registration.rolesAndResponsibilitiesDescription"} + values={{ deposit: correctAdaFormat(deposit.drep_deposit) }} + /> + + ); }; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx new file mode 100644 index 000000000..72baf20bc --- /dev/null +++ b/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx @@ -0,0 +1,50 @@ +import { Dispatch, SetStateAction, useCallback } from "react"; +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { + useRegisterAsdRepFormContext, + useScreenDimension, + useTranslation, +} from "@hooks"; + +import { BgCard } from "."; + +interface Props { + setStep: Dispatch>; +} + +export const RegisterAsdRepStepThree = ({ setStep }: Props) => { + const { isLoading, submitForm } = useRegisterAsdRepFormContext(); + const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); + + const onClickBackButton = useCallback(() => setStep(2), []); + + return ( + + + + {t("registration.headingStepTwo")} + + + {t("registration.descriptionStepTwo")} + + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx index b735d50b3..2e6acdebb 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx @@ -1,100 +1,84 @@ -import { Dispatch, SetStateAction, useMemo } from "react"; -import { Box } from "@mui/material"; +import { Dispatch, SetStateAction, useCallback } from "react"; +import { Box, Link } from "@mui/material"; -import { LoadingButton, Button, Typography } from "@atoms"; -import { theme } from "@/theme"; +import { Spacer, Typography } from "@atoms"; import { - useRegisterAsdRepFormContext, useScreenDimension, + useRegisterAsdRepFormContext, useTranslation, } from "@hooks"; +import { openInNewTab } from "@utils"; + +import { BgCard, ControlledField } from "."; interface Props { setStep: Dispatch>; } export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { - const { - palette: { boxShadow2 }, - } = theme; - const { isLoading, submitForm } = useRegisterAsdRepFormContext(); - const { isMobile, pagePadding, screenWidth } = useScreenDimension(); const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + const { showSubmitButton, control, errors } = useRegisterAsdRepFormContext(); - const renderBackButton = useMemo(() => { - return ( - - ); - }, [isMobile]); + const onClickContinue = useCallback(() => setStep(3), []); - const renderRegisterButton = useMemo(() => { - return ( - - {t("registration.register")} - - ); - }, [isLoading, isMobile, submitForm]); + const onClickBackButton = useCallback(() => setStep(1), []); return ( - - - - {t("registration.headingStepTwo")} - - - {t("registration.descriptionStepTwo")} - - - - {isMobile ? renderRegisterButton : renderBackButton} - - {isMobile ? renderBackButton : renderRegisterButton} + {t("registration.optional")} + + + {t("registration.addInformationTitle")} + + + {t("registration.addInformationDescription")} + + + + + + + openInNewTab( + "https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor" + ) + } + alignSelf={"center"} + my={5} + sx={{ cursor: "pointer" }} + > + + {t("forms.howCreateUrlAndHash")} + + - + ); }; diff --git a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx new file mode 100644 index 000000000..b2518b9e4 --- /dev/null +++ b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx @@ -0,0 +1,86 @@ +import { useCallback, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; + +import { PATHS } from "@consts"; +import { CenteredBoxBottomButtons } from "@molecules"; +import { useCardano, useModal } from "@context"; +import { RetireAsSoleVoterBoxContent } from "@organisms"; + +export const RetireAsSoleVoterBox = () => { + const [isLoading, setIsLoading] = useState(false); + + const navigate = useNavigate(); + const { + buildDRepRetirementCert, + buildSignSubmitConwayCertTx, + isPendingTransaction, + } = useCardano(); + const { openModal, closeModal } = useModal(); + const { t } = useTranslation(); + + const onRetire = useCallback(async () => { + try { + setIsLoading(true); + const isPendingTx = isPendingTransaction(); + if (isPendingTx) return; + const certBuilder = await buildDRepRetirementCert(); + const result = await buildSignSubmitConwayCertTx({ + certBuilder, + type: "soleVoterRegistration", + registrationType: "retirement", + }); + if (result) + openModal({ + type: "statusModal", + state: { + status: "success", + title: t("modals.retirement.title"), + message: t("modals.retirement.message"), + link: `https://adanordic.com/latest_transactions`, + buttonText: t("modals.common.goToDashboard"), + dataTestId: "retirement-transaction-submitted-modal", + onSubmit: () => { + navigate(PATHS.dashboard); + closeModal(); + }, + }, + }); + } catch (error: any) { + const errorMessage = error.info ? error.info : error; + + openModal({ + type: "statusModal", + state: { + status: "warning", + message: errorMessage, + buttonText: t("modals.common.goToDashboard"), + title: t("modals.common.oops"), + dataTestId: "retirement-transaction-error-modal", + onSubmit: () => { + navigate(PATHS.dashboard); + closeModal(); + }, + }, + }); + } finally { + setIsLoading(false); + } + }, [ + buildDRepRetirementCert, + buildSignSubmitConwayCertTx, + isPendingTransaction, + openModal, + ]); + + return ( + <> + + navigate(PATHS.dashboard)} + onActionButton={onRetire} + isLoading={isLoading} + /> + + ); +}; diff --git a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx new file mode 100644 index 000000000..bb371f0c4 --- /dev/null +++ b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBoxContent.tsx @@ -0,0 +1,44 @@ +import { Link } from "@mui/material"; +import { Trans } from "react-i18next"; + +import { Typography } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { correctAdaFormat, openInNewTab } from "@/utils"; +import { useCardano } from "@/context"; + +export const RetireAsSoleVoterBoxContent = () => { + const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); + const { voter } = useCardano(); + + return ( + <> + + {t("soleVoter.retirementHeading")} + + + openInNewTab("https://sancho.network/")} + sx={{ cursor: "pointer" }} + key="0" + />, + ]} + /> + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/index.ts b/govtool/frontend/src/components/organisms/index.ts index 8378c5d10..5387a425c 100644 --- a/govtool/frontend/src/components/organisms/index.ts +++ b/govtool/frontend/src/components/organisms/index.ts @@ -1,5 +1,7 @@ +export * from "./BgCard"; export * from "./ChooseStakeKeyPanel"; export * from "./ChooseWalletModal"; +export * from "./ControlledField"; export * from "./DashboardCards"; export * from "./DashboardCards"; export * from "./DashboardGovernanceActionDetails"; @@ -17,10 +19,13 @@ export * from "./GovernanceActionsToVote"; export * from "./Hero"; export * from "./HomeCards"; export * from "./RegisterAsdRepStepOne"; +export * from "./RegisterAsdRepStepThree"; export * from "./RegisterAsdRepStepTwo"; export * from "./Slider"; +export * from "./RegisterAsSoleVoterBoxContent"; +export * from "./RegisterAsSoleVoterBox"; +export * from "./RetireAsSoleVoterBoxContent"; +export * from "./RetireAsSoleVoterBox"; export * from "./StatusModal"; export * from "./TopNav"; export * from "./VotingPowerModal"; - -export * from "./ControlledField"; diff --git a/govtool/frontend/src/components/organisms/types.ts b/govtool/frontend/src/components/organisms/types.ts new file mode 100644 index 000000000..4c66d325a --- /dev/null +++ b/govtool/frontend/src/components/organisms/types.ts @@ -0,0 +1,11 @@ +import { SxProps } from "@mui/material"; + +export type BgCardProps = { + actionButtonLabel: string; + backButtonLabel?: string; + children: React.ReactNode; + isLoadingActionButton?: boolean; + onClickBackButton?: () => void; + onClickActionButton: () => void; + sx?: SxProps; +}; diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts index c6facbf60..93574c43b 100644 --- a/govtool/frontend/src/consts/icons.ts +++ b/govtool/frontend/src/consts/icons.ts @@ -2,6 +2,7 @@ export const ICONS = { appLogoIcon: "/icons/AppLogo.svg", arrowDownIcon: "/icons/ArrowDown.svg", arrowRightIcon: "/icons/ArrowRight.svg", + arrowLeftThinIcon: "/icons/ArrowLeftThin.svg", checkCircleIcon: "/icons/CheckCircle.svg", closeDrawerIcon: "/icons/CloseIcon.svg", closeIcon: "/icons/Close.svg", diff --git a/govtool/frontend/src/consts/images.ts b/govtool/frontend/src/consts/images.ts index f9a098d4f..72f60559d 100644 --- a/govtool/frontend/src/consts/images.ts +++ b/govtool/frontend/src/consts/images.ts @@ -1,15 +1,16 @@ export const IMAGES = { appLogo: "/images/SanchoLogo.png", appLogoWithoutText: "/images/AppLogoWithoutText.png", + bgBlue: "/images/BGBlue.png", + bgOrange: "/images/BGOrange.png", errorPageImage: "/images/ErrorPageImage.png", heroImage: "/images/HeroImage.png", + soleVoterImage: "/images/GovActionsSoleVoter.png", + govActionDefaultImage: "/images/GovActionDefault.png", govActionDelegateImage: "/images/GovActionDelegate.png", - govActionRegisterImage: "/images/GovActionRegister.png", govActionListImage: "/images/GovActionList.png", - govActionDefaultImage: "/images/GovActionDefault.png", + govActionRegisterImage: "/images/GovActionRegister.png", successImage: "/images/Success.png", warningImage: "/images/Warning.png", warningYellowImage: "/images/WarningYellow.png", - bgOrange: "/images/BGOrange.png", - bgBlue: "/images/BGBlue.png", }; diff --git a/govtool/frontend/src/consts/paths.ts b/govtool/frontend/src/consts/paths.ts index 645f4e695..4ac0cbde2 100644 --- a/govtool/frontend/src/consts/paths.ts +++ b/govtool/frontend/src/consts/paths.ts @@ -1,21 +1,23 @@ export const PATHS = { - home: "/", - dashboard: "/dashboard", - error: "/error", - governance_actions: "/governance_actions", - governance_actions_category: "/governance_actions/category/:category", - governance_actions_category_action: - "/governance_actions/category/:category/:proposalId", - governance_actions_action: "/governance_actions/:proposalId", - dashboard_governance_actions: "/connected/governance_actions", dashboard_governance_actions_action: "/connected/governance_actions/:proposalId", dashboard_governance_actions_category: "/connected/governance_actions/category/:category", - guides: "/guides", - faqs: "/faqs", + dashboard_governance_actions: "/connected/governance_actions", + dashboard: "/dashboard", delegateTodRep: "/delegate", + error: "/error", + faqs: "/faqs", + governance_actions_action: "/governance_actions/:proposalId", + governance_actions_category_action: + "/governance_actions/category/:category/:proposalId", + governance_actions_category: "/governance_actions/category/:category", + governance_actions: "/governance_actions", + guides: "/guides", + home: "/", registerAsdRep: "/register", + registerAsSoleVoter: "/register_sole_voter", + retireAsSoleVoter: "/retire_sole_voter", stakeKeys: "/stake_keys", updateMetadata: "/update_metadata", }; diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 3d49f7a25..1b29b1d5d 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -50,7 +50,7 @@ import { Trans } from "react-i18next"; import { useModal, useSnackbar } from "."; import { PATHS } from "@consts"; -import { CardanoApiWallet, DRepInfo, Protocol } from "@models"; +import { CardanoApiWallet, VoterInfo, Protocol } from "@models"; import type { StatusModalState } from "@organisms"; import { getPubDRepID, @@ -66,6 +66,7 @@ import { SANCHO_INFO_KEY, VOTE_TRANSACTION_KEY, checkIsMaintenanceOn, + REGISTER_SOLE_VOTER_TRANSACTION_KEY, } from "@utils"; import { getEpochParams, getTransactionStatus } from "@services"; import { @@ -97,15 +98,16 @@ interface CardanoContext { address?: string; disconnectWallet: () => Promise; enable: (walletName: string) => Promise; + isEnableLoading: string | null; error?: string; - dRep: DRepInfo | undefined; + voter: VoterInfo | undefined; isEnabled: boolean; pubDRepKey: string; dRepID: string; dRepIDBech32: string; isMainnet: boolean; stakeKey?: string; - setDRep: (key: undefined | DRepInfo) => void; + setVoter: (key: undefined | VoterInfo) => void; setStakeKey: (key: string) => void; stakeKeys: string[]; walletApi?: CardanoApiWallet; @@ -119,7 +121,7 @@ interface CardanoContext { }: { certBuilder?: CertificatesBuilder; votingBuilder?: VotingBuilder; - type?: "delegation" | "registration" | "vote"; + type?: "delegation" | "registration" | "soleVoterRegistration" | "vote"; proposalId?: string; registrationType?: DRepActionType; }) => Promise; @@ -142,6 +144,9 @@ interface CardanoContext { ) => Promise; delegateTransaction: TransactionHistoryItem; registerTransaction: TransactionHistoryItem & { type: DRepActionType }; + soleVoterTransaction: TransactionHistoryItem & { + type: Omit; + }; delegateTo: string; voteTransaction: TransactionHistoryItem & { proposalId: string }; isPendingTransaction: () => boolean; @@ -165,7 +170,8 @@ CardanoContext.displayName = "CardanoContext"; function CardanoProvider(props: Props) { const [isEnabled, setIsEnabled] = useState(false); - const [dRep, setDRep] = useState(undefined); + const [isEnableLoading, setIsEnableLoading] = useState(null); + const [voter, setVoter] = useState(undefined); const [walletApi, setWalletApi] = useState( undefined ); @@ -199,6 +205,9 @@ function CardanoProvider(props: Props) { const [registerTransaction, setRegisterTransaction] = useState< TransactionHistoryItem & { type: DRepActionType } >({ time: undefined, transactionHash: "", type: "" }); + const [soleVoterTransaction, setSoleVoterTransaction] = useState< + TransactionHistoryItem & { type: Omit } + >({ time: undefined, transactionHash: "", type: "" }); const [voteTransaction, setVoteTransaction] = useState< { proposalId: string } & TransactionHistoryItem >({ time: undefined, transactionHash: "", proposalId: "" }); @@ -210,6 +219,7 @@ function CardanoProvider(props: Props) { const isPendingTransaction = useCallback(() => { if ( registerTransaction?.transactionHash || + soleVoterTransaction?.transactionHash || delegateTransaction?.transactionHash || voteTransaction?.transactionHash ) { @@ -234,6 +244,7 @@ function CardanoProvider(props: Props) { delegateTransaction?.transactionHash, openModal, registerTransaction?.transactionHash, + soleVoterTransaction?.transactionHash, voteTransaction?.transactionHash, ]); @@ -244,6 +255,11 @@ function CardanoProvider(props: Props) { const registerTransaction = JSON.parse( getItemFromLocalStorage(REGISTER_TRANSACTION_KEY + `_${stakeKey}`) ); + const soleVoterTransaction = JSON.parse( + getItemFromLocalStorage( + REGISTER_SOLE_VOTER_TRANSACTION_KEY + `_${stakeKey}` + ) + ); const voteTransaction = JSON.parse( getItemFromLocalStorage(VOTE_TRANSACTION_KEY + `_${stakeKey}`) ); @@ -256,6 +272,9 @@ function CardanoProvider(props: Props) { if (registerTransaction?.transactionHash) { setRegisterTransaction(registerTransaction); } + if (soleVoterTransaction?.transactionHash) { + setSoleVoterTransaction(soleVoterTransaction); + } if (voteTransaction?.transactionHash) { setVoteTransaction(voteTransaction); } @@ -332,7 +351,7 @@ function CardanoProvider(props: Props) { 10, dRepID, registerTransaction.type, - setDRep + setVoter ).then((isRegistered) => { if (registerTransaction.type === "registration") { if (isRegistered) { @@ -376,6 +395,71 @@ function CardanoProvider(props: Props) { let interval = setInterval(checkRegisterTransaction, REFRESH_TIME); checkRegisterTransaction(); } + if (soleVoterTransaction?.transactionHash) { + const checkRegisterTransaction = async () => { + const resetRegisterTransaction = () => { + clearInterval(interval); + removeItemFromLocalStorage( + REGISTER_SOLE_VOTER_TRANSACTION_KEY + `_${stakeKey}` + ); + setSoleVoterTransaction({ + time: undefined, + transactionHash: "", + type: "", + }); + }; + const status = await getTransactionStatus( + soleVoterTransaction.transactionHash + ); + if (status.transactionConfirmed) { + if (isEnabled) { + await setLimitedRegistrationInterval( + 3000, + 10, + dRepID, + soleVoterTransaction.type, + setVoter + ).then((isRegistered) => { + if (soleVoterTransaction.type === "registration") { + if (isRegistered) { + addSuccessAlert(t("alerts.soleVoterRegistration.success")); + } else { + addWarningAlert( + t("alerts.soleVoterRegistration.refreshPage") + ); + } + } else if (soleVoterTransaction.type === "retirement") { + if (!isRegistered) { + addSuccessAlert(t("alerts.soleVoterRetirement.success")); + } else { + addWarningAlert(t("alerts.soleVoterRetirement.refreshPage")); + } + } + }); + } + resetRegisterTransaction(); + } + if ( + new Date().getTime() - + new Date(soleVoterTransaction?.time).getTime() > + TIME_TO_EXPIRE_TRANSACTION + ) { + resetRegisterTransaction(); + if (isEnabled) + addErrorAlert( + t( + `alerts.${ + soleVoterTransaction.type === "retirement" + ? "retirement.failed" + : "registration.failed" + }` + ) + ); + } + }; + let interval = setInterval(checkRegisterTransaction, REFRESH_TIME); + checkRegisterTransaction(); + } if (voteTransaction?.transactionHash) { const checkVoteTransaction = async () => { const resetVoteTransaction = () => { @@ -409,11 +493,17 @@ function CardanoProvider(props: Props) { isEnabled && (voteTransaction?.transactionHash || registerTransaction?.transactionHash || + soleVoterTransaction?.transactionHash || delegateTransaction?.transactionHash) ) { addWarningAlert(t("alerts.transactionInProgress"), 10000); } - }, [delegateTransaction, registerTransaction, voteTransaction]); + }, [ + delegateTransaction, + registerTransaction, + soleVoterTransaction, + voteTransaction, + ]); const getChangeAddress = async (enabledApi: CardanoApiWallet) => { try { @@ -517,6 +607,7 @@ function CardanoProvider(props: Props) { const enable = useCallback( async (walletName: string) => { + setIsEnableLoading(walletName); await checkIsMaintenanceOn(); // todo: use .getSupportedExtensions() to check if wallet supports CIP-95 @@ -650,6 +741,8 @@ function CardanoProvider(props: Props) { status: "ERROR", error: `${e == undefined ? t("errors.somethingWentWrong") : e}`, }; + } finally { + setIsEnableLoading(null); } } throw { status: "ERROR", error: t("errors.somethingWentWrong") }; @@ -714,7 +807,7 @@ function CardanoProvider(props: Props) { }: { certBuilder?: CertificatesBuilder; votingBuilder?: VotingBuilder; - type?: "delegation" | "registration" | "vote"; + type?: "delegation" | "registration" | "soleVoterRegistration" | "vote"; proposalId?: string; registrationType?: DRepActionType; }) => { @@ -755,9 +848,9 @@ function CardanoProvider(props: Props) { // Add output of 1 ADA to the address of our wallet let outputValue = BigNum.from_str("1000000"); - if (registrationType === "retirement" && dRep?.deposit) { + if (registrationType === "retirement" && voter?.deposit) { outputValue = outputValue.checked_add( - BigNum.from_str(`${dRep?.deposit}`) + BigNum.from_str(`${voter?.deposit}`) ); } @@ -837,6 +930,21 @@ function CardanoProvider(props: Props) { }) ); } + if (type === "soleVoterRegistration" && registrationType !== "update") { + setSoleVoterTransaction({ + time: new Date(), + transactionHash: resultHash, + type: registrationType ?? "", + }); + setItemToLocalStorage( + REGISTER_SOLE_VOTER_TRANSACTION_KEY + `_${stakeKey}`, + JSON.stringify({ + time: new Date(), + transactionHash: resultHash, + type: registrationType, + }) + ); + } if (type === "delegation") { setDelegateTransaction({ time: new Date(), @@ -887,11 +995,12 @@ function CardanoProvider(props: Props) { walletApi, getUtxos, registerTransaction.transactionHash, + soleVoterTransaction.transactionHash, delegateTransaction.transactionHash, voteTransaction.transactionHash, stakeKey, isPendingTransaction, - dRep, + voter, ] ); @@ -1040,7 +1149,7 @@ function CardanoProvider(props: Props) { const dRepRetirementCert = DrepDeregistration.new( dRepCred, - BigNum.from_str(`${dRep?.deposit}`) + BigNum.from_str(`${voter?.deposit}`) ); // add cert to tbuilder certBuilder.add( @@ -1052,7 +1161,7 @@ function CardanoProvider(props: Props) { console.log(e); throw e; } - }, [dRepID, dRep]); + }, [dRepID, voter]); const buildVote = useCallback( async ( @@ -1113,7 +1222,7 @@ function CardanoProvider(props: Props) { () => ({ address, enable, - dRep, + voter, isEnabled, isMainnet, disconnectWallet, @@ -1121,7 +1230,7 @@ function CardanoProvider(props: Props) { dRepIDBech32, pubDRepKey, stakeKey, - setDRep, + setVoter, setStakeKey, stakeKeys, walletApi, @@ -1136,16 +1245,18 @@ function CardanoProvider(props: Props) { buildVoteDelegationCert, delegateTransaction, registerTransaction, + soleVoterTransaction, delegateTo, voteTransaction, isPendingTransaction, isDrepLoading, setIsDrepLoading, + isEnableLoading, }), [ address, enable, - dRep, + voter, isEnabled, isMainnet, disconnectWallet, @@ -1153,7 +1264,7 @@ function CardanoProvider(props: Props) { dRepIDBech32, pubDRepKey, stakeKey, - setDRep, + setVoter, setStakeKey, stakeKeys, walletApi, @@ -1168,11 +1279,13 @@ function CardanoProvider(props: Props) { buildVoteDelegationCert, delegateTransaction, registerTransaction, + soleVoterTransaction, delegateTo, voteTransaction, isPendingTransaction, isDrepLoading, setIsDrepLoading, + isEnableLoading, ] ); diff --git a/govtool/frontend/src/context/walletUtils.ts b/govtool/frontend/src/context/walletUtils.ts index 5bf0be32a..d68d8e7cf 100644 --- a/govtool/frontend/src/context/walletUtils.ts +++ b/govtool/frontend/src/context/walletUtils.ts @@ -1,13 +1,13 @@ import { getAdaHolderCurrentDelegation, getDRepInfo } from "@services"; import { DRepActionType } from "./wallet"; -import { DRepInfo } from "@models"; +import { VoterInfo } from "@models"; export const setLimitedRegistrationInterval = ( intervalTime: number, attemptsNumber: number, dRepID: string, - transactionType: DRepActionType, - setDRep: (key: undefined | DRepInfo) => void + transactionType: DRepActionType | Omit, + setVoter: (key: undefined | VoterInfo) => void ): Promise => { return new Promise(async (resolve) => { const desiredResult = transactionType === "registration" ? true : false; @@ -20,8 +20,11 @@ export const setLimitedRegistrationInterval = ( try { const data = await getDRepInfo(dRepID); - if (data.isRegistered === desiredResult) { - setDRep(data); + if ( + data.isRegisteredAsDRep === desiredResult || + data.isRegisteredAsSoleVoter === desiredResult + ) { + setVoter(data); clearInterval(interval); resolve(desiredResult); } diff --git a/govtool/frontend/src/hooks/index.ts b/govtool/frontend/src/hooks/index.ts index e17b56d3a..84e427cfa 100644 --- a/govtool/frontend/src/hooks/index.ts +++ b/govtool/frontend/src/hooks/index.ts @@ -6,5 +6,4 @@ export * from "./useFetchNextPageDetector"; export * from "./useWalletConnectionListener"; export * from "./forms"; -export * from "./mutations"; export * from "./queries"; diff --git a/govtool/frontend/src/hooks/mutations/index.ts b/govtool/frontend/src/hooks/mutations/index.ts deleted file mode 100644 index 7ab26f25b..000000000 --- a/govtool/frontend/src/hooks/mutations/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./useDRepRegisterMutation"; -export * from "./useDRepRemoveVoteMutation"; -export * from "./useDRepRetireMutation"; -export * from "./useAdaHolderDelegateMutation"; -export * from "./useAdaHolderDelegateAbstainMutation"; -export * from "./useAdaHolderDelegateNoMutation"; -export * from "./useAdaHolderRemoveDelegationMutation"; diff --git a/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateAbstainMutation.ts b/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateAbstainMutation.ts deleted file mode 100644 index 9d37a5a01..000000000 --- a/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateAbstainMutation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { postAdaHolderDelegateAbstain } from "@services"; -import { useMutation } from "react-query"; - -export const useAdaHolderDelegateAbstainMutation = () => { - const { mutateAsync } = useMutation(postAdaHolderDelegateAbstain, {}); - - return { - delegateAbstainAsAdaHolder: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateMutation.ts b/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateMutation.ts deleted file mode 100644 index fe915c357..000000000 --- a/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateMutation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { postAdaHolderDelegate } from "@services"; -import { useMutation } from "react-query"; - -export const useAdaHolderDelegateMutation = () => { - const { mutateAsync } = useMutation(postAdaHolderDelegate, {}); - - return { - delegateAsAdaHolder: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateNoMutation.ts b/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateNoMutation.ts deleted file mode 100644 index 8491c4627..000000000 --- a/govtool/frontend/src/hooks/mutations/useAdaHolderDelegateNoMutation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { postAdaHolderDelegateNo } from "@services"; -import { useMutation } from "react-query"; - -export const useAdaHolderDelegateNoMutation = () => { - const { mutateAsync } = useMutation(postAdaHolderDelegateNo, {}); - - return { - delegateNoAsAdaHolder: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/mutations/useAdaHolderRemoveDelegationMutation.ts b/govtool/frontend/src/hooks/mutations/useAdaHolderRemoveDelegationMutation.ts deleted file mode 100644 index 739a6b5e4..000000000 --- a/govtool/frontend/src/hooks/mutations/useAdaHolderRemoveDelegationMutation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { postAdaHolderRemoveDelegation } from "@services"; -import { useMutation } from "react-query"; - -export const useAdaHolderRemoveDelegation = () => { - const { mutateAsync } = useMutation(postAdaHolderRemoveDelegation); - - return { - register: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/mutations/useDRepRegisterMutation.ts b/govtool/frontend/src/hooks/mutations/useDRepRegisterMutation.ts deleted file mode 100644 index 76b79287d..000000000 --- a/govtool/frontend/src/hooks/mutations/useDRepRegisterMutation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useMutation } from "react-query"; -import { useCardano } from "@context"; -import { postDRepRegister } from "@services"; - -export const useDRepRegisterMutation = () => { - const { setDRep } = useCardano(); - - const { mutateAsync, isLoading } = useMutation(postDRepRegister, { - onSuccess: () => { - setDRep({ deposit: 100, isRegistered: true, wasRegistered: false }); - }, - }); - - return { - isLoading: isLoading, - register: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/mutations/useDRepRemoveVoteMutation.ts b/govtool/frontend/src/hooks/mutations/useDRepRemoveVoteMutation.ts deleted file mode 100644 index 7102ad95a..000000000 --- a/govtool/frontend/src/hooks/mutations/useDRepRemoveVoteMutation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { postDRepRemoveVote } from "@services"; -import { useMutation } from "react-query"; - -export const useDRepRemoveVoteMutation = () => { - const { mutateAsync } = useMutation(postDRepRemoveVote); - - return { - removeVote: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/mutations/useDRepRetireMutation.ts b/govtool/frontend/src/hooks/mutations/useDRepRetireMutation.ts deleted file mode 100644 index b3697c714..000000000 --- a/govtool/frontend/src/hooks/mutations/useDRepRetireMutation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useMutation } from "react-query"; -import { useCardano, useSnackbar } from "@context"; -import { postDRepRetire } from "@services"; - -export const useDRepRetireMutation = () => { - const { setDRep } = useCardano(); - const { addSuccessAlert } = useSnackbar(); - - const { mutateAsync } = useMutation(postDRepRetire, { - onSuccess: () => { - setDRep({ deposit: 100, wasRegistered: true, isRegistered: false }); - addSuccessAlert("DRep retired."); - }, - }); - - return { - retire: mutateAsync, - }; -}; diff --git a/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts b/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts index aade95029..4accd00fb 100644 --- a/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts @@ -5,14 +5,21 @@ import { useCardano } from "@context"; import { getDRepVotingPower } from "@services"; export const useGetDRepVotingPowerQuery = () => { - const { dRepID } = useCardano(); + const { dRepID, voter } = useCardano(); const { data, isLoading } = useQuery({ - queryKey: QUERY_KEYS.useGetDRepVotingPowerKey, + queryKey: [ + QUERY_KEYS.useGetDRepVotingPowerKey, + dRepID, + voter?.isRegisteredAsDRep, + voter?.isRegisteredAsSoleVoter, + ], queryFn: async () => { return await getDRepVotingPower({ dRepID }); }, - enabled: !!dRepID, + enabled: + !!dRepID && + (!!voter?.isRegisteredAsDRep || !!voter?.isRegisteredAsSoleVoter), }); return { dRepVotingPower: data, isDRepVotingPowerLoading: isLoading }; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index aa6a71f3e..edf57f75d 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -23,6 +23,16 @@ export const en = { "You have successfully retired from being a DRep! Please refresh the page.", success: "You have successfully retired from being a DRep!", }, + soleVoterRegistration: { + refreshPage: + "You have successfully registered as a Sole Voter! Please refresh the page.", + success: "You have successfully registered as a Sole Voter!", + }, + soleVoterRetirement: { + refreshPage: + "You have successfully retired from being a Sole Voter! Please refresh the page.", + success: "You have successfully retired from being a SoleVoter!", + }, voting: { failed: "Vote transaction failed", success: "You have successfully voted!", @@ -37,11 +47,8 @@ export const en = { headingTwo: "See Active Governance Actions", delegation: { changeDelegation: "Change delegation", - connectToDelegate: "Connect to delegate", delegateOwnPower: "If you want to delegate your own voting power of ₳{{ada}}.", - description: - "If you want to delegate to a DRep or select a default option.", dRepDelegatedTo: "DRep you delegated to", toDRep: "You have delegated your voting power of ₳{{ada}} to a selected DRep.", @@ -74,12 +81,9 @@ export const en = { }, registration: { changeMetadata: "Change metadata", - connectToRegister: "Connect to register", dRepRegistration: "DRep Registration", dRepRetirement: "DRep Retirement", dRepUpdate: "DRep Update", - description: - "If you want to directly participate in voting and have other ada holders delegate their voting power to you.", holdersCanDelegate: "Ada holders can delegate their voting power to you.", ifYouWant: @@ -96,6 +100,26 @@ export const en = { "The retirement process is ongoing. This may take several minutes.", youAreRegistered: "You are Registered as a DRep", }, + soleVoter: { + isRegisteredDescription: + "Your Voting Power of ₳{{votingPower}} can be used to vote.", + register: "Register", + registerDescription: + "Vote on Governance Actions using your own voting power of ₳{{votingPower}}.", + registerTitle: "Become a Sole Voter", + reRegister: "Re-register", + registration: "Sole Voter Registration", + registrationInProgress: + "The registration process is ongoing. This may take several minutes.", + retire: "Retire", + retireTitle: "You Have Retired as a Sole Voter", + retirement: "Sole Voter Retirement", + wasRegisteredDescription: + "You cannot vote on Governance Actions using your own voting power of ₳{{votingPower}}. until you re-register.", + retirementInProgress: + "The retirement process is ongoing. This may take several minutes.", + youAreSoleVoterTitle: "You are a Sole Voter", + }, }, delegation: { description: @@ -211,10 +235,32 @@ export const en = { }, hero: { connectWallet: "Connect your wallet", - description: - "Interact with SanchoNet using GovTool - a friendly user\ninterface connected to SanchoNet. You can delegate\nyour voting power (tAda) or become a SanchoNet DRep\nto allow people to delegate voting power to you.", + description: { + mobile: + "You can either delegate your voting power or become a DRep to allow people to delegate voting power to you.", + wide: "Anyone with a wallet containing ADA can participate in governance on Sanchonet.\n\nYour ADA balance entitles you to an equal amount of Voting Power.\n\nFor more info see the guide entry for <0>Voting Power.", + }, headline: "SanchoNet \n Governance Tool", }, + home: { + cards: { + delegateDescription: "Find a DRep to vote on your behalf.", + delegateFirstButtonLabel: "View DRep Direcotry", + delegateTitle: "Delegate your Voting Power", + governaneActionsDescription: + "See all the Governance Actions submitted on chain. ", + governanceActionsFirstButtonLabel: "View Governance Actions", + governaneActionsTitle: "View Governance Actions", + registerAsDRepDescription: + "Accept delegated voting power from other ADA holders, and combine it with your own voting power. Vote with the accumulated Power on Governance Actions.", + registerAsDRepFirstButtonLabel: "Connect to Register", + registerAsDRepTitle: "Become a DRep", + registerAsSoleVoterDescription: + "Vote on Governance Actions using your own voting power", + registerAsSoleVoterFirstButtonLabel: "Connect to Register", + registerAsSoleVoterTitle: "Become a Sole Voter", + }, + }, menu: { faqs: "FAQs", guides: "Guides", @@ -269,20 +315,33 @@ export const en = { }, }, registration: { - descriptionStepOne: + addInformationDescription: "You can include extra information about yourself by adding a URL and its hash.", + addInformationTitle: "Add Information", + becomeADRep: "Become a DRep", descriptionStepTwo: "By clicking register you create your DRep ID within your wallet and become a DRep.\n\nOnce the registration has completed your DRep ID will be shown on your dashboard. You will be able to share your DRep ID so that other ada holders can delegate their voting power to you.", - headingStepOne: "Add Information", headingStepTwo: "Confirm DRep registration", optional: "OPTIONAL", register: "Register", - registerAsDRep: "Register as a DRep", + rolesAndResponsibilitiesDescription: + "DReps are fundamental users that govern the Cardano network. This is an important role which requires work and dedication to fulfil.\n\nA DRep is expected to actively participate in governance and act as a representative of other Cardano members in governance matters. Therefore, DReps will be expected to keep abreast of Governance Actions so they can make informed and wise decisions.\n<0>Learn More about DRep.\n\nPlease register as a DRep if you have time to dedicate to making Cardano a better and more well-governed place.\n\nBecoming a DRep will require a refundable deposit of ₳{{deposit}}.\n\nYou will be refunded your deposit when you retire.", + rolesAndResponsibilitiesTitle: "Roles & Responsibilities", }, slider: { showAll: "Show all", viewAll: "View all", }, + soleVoter: { + becomeSoleVoter: "Become a Sole Voter", + registerDescription: + "A Sole Voter is someone that can vote on any Governance Action with their own Voting Power, which is equal to the balance of ADA in their connected wallet. <0>Learn More about Sole Voter.\n\nBecoming a Sole Voter will require a refundable deposit of ₳{{deposit}}.\n\nYour deposit will be refunded if you either retire or delegate your voting power to someone else (a DRep)", + registerHeading: "What this Means", + retirementDescription: + "By Retiring you are giving up your Voting Power. You will not be able to vote on any Governance Actions. Your deposit of {{deposit}} ADA will be refunded.\n\nYou can at any time in the future re-register to become a Sole Voter, or you can delegate your Voting Power to someone else, or become a DRep.\n\nThese options are listed in our Guides here: <0>Voting options and Roles", + retirementHeading: "What Retirement Means", + retireSoleVoter: "Retire as a Sole Voter", + }, system: { sanchoNet: "SanchoNet", sanchoNetIsBeta: @@ -339,7 +398,7 @@ export const en = { "Can’t see your wallet? Check what wallets are currently compatible with GovTool ", chooseWallet: "Choose the wallet you want to connect with:", connect: "Connect", - connectWallet: "Connect wallet", + connectWallet: "Connect Wallet", connectYourWallet: "Connect your Wallet", connectYourWalletButton: "Connect your wallet", connectedWallet: "Connected Wallet:", @@ -355,6 +414,7 @@ export const en = { }, abstain: "Abstain", back: "Back", + backToDashboard: "Back to dashboard", backToList: "Back to the list", cancel: "Cancel", clear: "Clear", @@ -371,6 +431,7 @@ export const en = { ok: "Ok", select: "Select", seeTransaction: "See transaction", + submit: "Submit", skip: "Skip", sortBy: "Sort by", thisLink: "this link", diff --git a/govtool/frontend/src/models/api.ts b/govtool/frontend/src/models/api.ts index 03fec9e2b..9f16b07ff 100644 --- a/govtool/frontend/src/models/api.ts +++ b/govtool/frontend/src/models/api.ts @@ -1,6 +1,8 @@ -export interface DRepInfo { - isRegistered: boolean; - wasRegistered: boolean; +export interface VoterInfo { + isRegisteredAsDRep: boolean; + wasRegisteredAsDRep: boolean; + isRegisteredAsSoleVoter: boolean; + wasRegisteredAsSoleVoter: boolean; deposit: number; } diff --git a/govtool/frontend/src/pages/Dashboard.tsx b/govtool/frontend/src/pages/Dashboard.tsx index db72fd2a2..7899270cc 100644 --- a/govtool/frontend/src/pages/Dashboard.tsx +++ b/govtool/frontend/src/pages/Dashboard.tsx @@ -7,7 +7,7 @@ import { useCardano } from "@context"; import { Background, ScrollToManage } from "@atoms"; import { useScreenDimension } from "@hooks"; import { DashboardTopNav, Drawer, Footer } from "@organisms"; -import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage"; +import { checkIsWalletConnected } from "@/utils"; const getPageTitle = (pathname: string) => { if (pathname === PATHS.dashboard) { @@ -33,10 +33,7 @@ export const Dashboard = () => { }, [pathname, divRef]); useEffect(() => { - if ( - !getItemFromLocalStorage(`${WALLET_LS_KEY}_stake_key`) || - !getItemFromLocalStorage(`${WALLET_LS_KEY}_name`) - ) { + if (checkIsWalletConnected()) { if (window.location.pathname === PATHS.dashboard) { navigate(PATHS.home); } else { diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index 5097fd091..1fffa539d 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -38,7 +38,7 @@ export const DashboardGovernanceActionsCategory = () => { const [chosenSorting, setChosenSorting] = useState(""); const { isMobile, screenWidth } = useScreenDimension(); const navigate = useNavigate(); - const { dRep, isDrepLoading, voteTransaction } = useCardano(); + const { voter, isDrepLoading, voteTransaction } = useCardano(); const { t } = useTranslation(); const { @@ -88,7 +88,12 @@ export const DashboardGovernanceActionsCategory = () => { .toLowerCase() .includes(searchText.toLowerCase()) ); - }, [proposals, dRep?.isRegistered, searchText, isProposalsFetchingNextPage]); + }, [ + proposals, + voter?.isRegisteredAsDRep, + searchText, + isProposalsFetchingNextPage, + ]); const closeSorts = useCallback(() => { setSortOpen(false); diff --git a/govtool/frontend/src/pages/DelegateTodRep.tsx b/govtool/frontend/src/pages/DelegateTodRep.tsx index 8749bb225..05b11cc44 100644 --- a/govtool/frontend/src/pages/DelegateTodRep.tsx +++ b/govtool/frontend/src/pages/DelegateTodRep.tsx @@ -10,8 +10,8 @@ import { Footer, } from "@organisms"; import { useScreenDimension, useTranslation } from "@hooks"; -import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage"; import { useNavigate } from "react-router-dom"; +import { checkIsWalletConnected } from "@/utils"; export const DelegateTodRep = () => { const [step, setStep] = useState(1); @@ -20,10 +20,7 @@ export const DelegateTodRep = () => { const { t } = useTranslation(); useEffect(() => { - if ( - !getItemFromLocalStorage(`${WALLET_LS_KEY}_stake_key`) || - !getItemFromLocalStorage(`${WALLET_LS_KEY}_name`) - ) { + if (checkIsWalletConnected()) { navigate(PATHS.home); } }, []); diff --git a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx index b7cd4982e..ef6c40a50 100644 --- a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx @@ -36,7 +36,7 @@ export const GovernanceActionsCategory = () => { const { isMobile, pagePadding, screenWidth } = useScreenDimension(); const { isEnabled } = useCardano(); const navigate = useNavigate(); - const { dRep } = useCardano(); + const { voter } = useCardano(); const { t } = useTranslation(); const { @@ -86,7 +86,12 @@ export const GovernanceActionsCategory = () => { .toLowerCase() .includes(searchText.toLowerCase()) ); - }, [dRep?.isRegistered, isProposalsFetchingNextPage, proposals, searchText]); + }, [ + voter?.isRegisteredAsDRep, + isProposalsFetchingNextPage, + proposals, + searchText, + ]); useEffect(() => { if (isEnabled && getItemFromLocalStorage(`${WALLET_LS_KEY}_stake_key`)) { diff --git a/govtool/frontend/src/pages/Home.tsx b/govtool/frontend/src/pages/Home.tsx index 6ca5689f2..25942471c 100644 --- a/govtool/frontend/src/pages/Home.tsx +++ b/govtool/frontend/src/pages/Home.tsx @@ -1,12 +1,12 @@ import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; +import { Box } from "@mui/material"; import { Background } from "@atoms"; -import { Box } from "@mui/material"; -import { TopNav, Hero, Footer, HomeCards } from "@organisms"; +import { PATHS } from "@consts"; import { useCardano } from "@context"; -import { PATHS } from "@/consts"; -import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage"; +import { TopNav, Hero, Footer, HomeCards } from "@organisms"; +import { WALLET_LS_KEY, getItemFromLocalStorage } from "@utils"; export const Home = () => { const { isEnabled } = useCardano(); diff --git a/govtool/frontend/src/pages/RegisterAsSoleVoter.tsx b/govtool/frontend/src/pages/RegisterAsSoleVoter.tsx new file mode 100644 index 000000000..822d809ce --- /dev/null +++ b/govtool/frontend/src/pages/RegisterAsSoleVoter.tsx @@ -0,0 +1,30 @@ +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; + +import { PATHS } from "@consts"; +import { RegisterAsSoleVoterBox } from "@organisms"; +import { useTranslation } from "@hooks"; +import { CenteredBoxPageWrapper } from "@molecules"; +import { checkIsWalletConnected } from "@/utils"; + +export const RegisterAsSoleVoter = () => { + const navigate = useNavigate(); + const { t } = useTranslation(); + + useEffect(() => { + if (checkIsWalletConnected()) { + navigate(PATHS.home); + } + }, []); + + return ( + + + + ); +}; diff --git a/govtool/frontend/src/pages/RegisterAsdRep.tsx b/govtool/frontend/src/pages/RegisterAsdRep.tsx index 79e3b1c88..3653b806d 100644 --- a/govtool/frontend/src/pages/RegisterAsdRep.tsx +++ b/govtool/frontend/src/pages/RegisterAsdRep.tsx @@ -8,6 +8,7 @@ import { DashboardTopNav, Footer, RegisterAsdRepStepOne, + RegisterAsdRepStepThree, RegisterAsdRepStepTwo, } from "@organisms"; import { @@ -16,7 +17,7 @@ import { useTranslation, } from "@hooks"; import { useNavigate } from "react-router-dom"; -import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage"; +import { checkIsWalletConnected } from "@/utils"; export const RegisterAsdRep = () => { const [step, setStep] = useState(1); @@ -27,35 +28,25 @@ export const RegisterAsdRep = () => { const registerAsdRepFormMethods = useRegisterAsdRepFormController(); useEffect(() => { - if ( - !getItemFromLocalStorage(`${WALLET_LS_KEY}_stake_key`) || - !getItemFromLocalStorage(`${WALLET_LS_KEY}_name`) - ) { + if (checkIsWalletConnected()) { navigate(PATHS.home); } }, []); return ( - + - - - {step === 1 && } - {step === 2 && } - - + + {step === 1 && } + {step === 2 && } + {step === 3 && } + {isMobile &&