Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sdk: add claim rewards #932

Merged
merged 2 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/extension/src/Approvals/Commitment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const IconMap: Record<TxType, React.ReactNode> = {
[TxType.EthBridgeTransfer]: <FaWallet />,
[TxType.VoteProposal]: <FaVoteYea />,
[TxType.Batch]: <PiDotsNineBold />,
[TxType.ClaimRewards]: <GoStack />,
};

const TitleMap: Record<TxType, string> = {
Expand All @@ -45,6 +46,7 @@ const TitleMap: Record<TxType, string> = {
[TxType.EthBridgeTransfer]: "ETH Transfer",
[TxType.VoteProposal]: "Vote",
[TxType.Batch]: "Batch",
[TxType.ClaimRewards]: "Claim Rewards",
};

const formatAddress = (address: string): string =>
Expand Down
18 changes: 13 additions & 5 deletions apps/namadillo/src/App/Governance/AllProposalsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { useAtomValue } from "jotai";
import { useEffect, useState } from "react";
import { GoCheckCircleFill, GoInfo } from "react-icons/go";
import { useNavigate } from "react-router-dom";
import { showProposalStatus, showProposalTypeString } from "utils";
import {
proposalStatusToString,
proposalTypeStringToString,
secondsToDateString,
secondsToTimeString,
} from "utils";
import { StatusLabel, TypeLabel } from "./ProposalLabels";
import GovernanceRoutes from "./routes";

Expand Down Expand Up @@ -171,7 +176,7 @@ export const AllProposalsTable: React.FC<ExtensionConnectedProps> = (props) => {
id: status,
value: (
<TableSelectOption>
{showProposalStatus(status)}
{proposalStatusToString(status)}
</TableSelectOption>
),
ariaLabel: status,
Expand Down Expand Up @@ -207,7 +212,7 @@ export const AllProposalsTable: React.FC<ExtensionConnectedProps> = (props) => {
id: type,
value: (
<TableSelectOption>
{showProposalTypeString(type)}
{proposalTypeStringToString(type)}
</TableSelectOption>
),
ariaLabel: type,
Expand Down Expand Up @@ -299,6 +304,9 @@ const Status: React.FC<CellProps> = ({ proposal }) => (
<StatusLabel status={proposal.status} className="ml-auto" />
);

const VotingEnd: React.FC<CellProps> = ({ proposal }) => (
<div className="text-right">Epoch {proposal.endEpoch.toString()}</div>
const VotingEnd: React.FC<CellProps> = ({ proposal: { endTime } }) => (
<div className="text-right">
<div>{secondsToTimeString(endTime)}</div>
<div className="text-neutral-450">{secondsToDateString(endTime)}</div>
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import BigNumber from "bignumber.js";
import clsx from "clsx";
import { GoInfo } from "react-icons/go";
import { useNavigate } from "react-router-dom";
import { secondsToDateTimeString } from "utils";
import { StatusLabel, TypeLabel, VotedLabel } from "./ProposalLabels";
import GovernanceRoutes from "./routes";
import { colors } from "./types";
Expand Down Expand Up @@ -41,7 +42,7 @@ const ProposalListItem: React.FC<{
<div className="flex items-center justify-between gap-4">
<StatusLabel className="text-[10px] min-w-38" status={status} />
<div className="text-xs text-neutral-400">
Voting End on epoch {proposal.endEpoch.toString()}
Voting End on {secondsToDateTimeString(proposal.endTime)}
</div>
</div>
<div className="flex items-center justify-between gap-4">
Expand Down
54 changes: 39 additions & 15 deletions apps/namadillo/src/App/Governance/ProposalHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ import { FaChevronLeft } from "react-icons/fa";
import { SiWebassembly } from "react-icons/si";
import { VscJson } from "react-icons/vsc";
import { Link, useNavigate } from "react-router-dom";
import { showEpoch, showProposalId } from "utils";
import {
proposalIdToString,
secondsToDateTimeString,
secondsToTimeRemainingString,
} from "utils";
import {
StatusLabel as StatusLabelComponent,
TypeLabel as TypeLabelComponent,
Expand Down Expand Up @@ -68,7 +72,7 @@ const Title: React.FC<{
proposalId: bigint;
}> = ({ proposal, proposalId }) => (
<div className="text-xl">
{showProposalId(proposalId)}{" "}
{proposalIdToString(proposalId)}{" "}
{proposal.status === "pending" || proposal.status === "error" ? null : (
proposal.data.content.title
)}
Expand All @@ -88,7 +92,7 @@ const Breadcrumbs: React.FC<{ proposalId: bigint }> = ({ proposalId }) => (
className="transition-colors hover:text-white"
to={GovernanceRoutes.proposal(proposalId).url}
>
Proposal {showProposalId(proposalId)}
Proposal {proposalIdToString(proposalId)}
</Link>
</div>
);
Expand Down Expand Up @@ -141,7 +145,7 @@ const WasmButton: React.FC<{
proposal: AtomWithQueryResult<StoredProposal>;
}> = ({ proposal }) => {
const { disabled, href, filename } = (() => {
if (proposal.status !== "pending" && proposal.status !== "error") {
if (proposal.status === "success") {
const { proposalType } = proposal.data;
const wasmCode =
proposalType.type === "default_with_wasm" ?
Expand Down Expand Up @@ -193,12 +197,9 @@ const StatusLabel: React.FC<{
cachedProposal: AtomWithQueryResult<StoredProposal>;
}> = ({ proposal, cachedProposal }) => {
const status: ProposalStatus | undefined = (() => {
if (proposal.status !== "pending" && proposal.status !== "error") {
if (proposal.status === "success") {
return proposal.data.status;
} else if (
cachedProposal.status !== "pending" &&
cachedProposal.status !== "error"
) {
} else if (cachedProposal.status === "success") {
return cachedProposal.data.status;
} else {
return undefined;
Expand All @@ -211,37 +212,60 @@ const StatusLabel: React.FC<{
};

const Progress: React.FC<{
proposal: AtomWithQueryResult<StoredProposal>;
proposal: AtomWithQueryResult<Proposal>;
}> = ({ proposal }) => {
return (
<div className="w-full grid grid-cols-2 text-xs">
<span>Progress</span>
<TimeRemaining proposalQuery={proposal} />
<div className="col-span-2 mt-3 mb-2">
<ProgressBar cachedProposal={proposal} />
</div>
<ProgressStartEnd
proposal={proposal}
epochKey="startEpoch"
timeKey="startTime"
className="col-start-1"
/>
<ProgressStartEnd
proposal={proposal}
epochKey="endEpoch"
timeKey="endTime"
className="text-right"
/>
</div>
);
};

const TimeRemaining: React.FC<{
proposalQuery: AtomWithQueryResult<Proposal>;
}> = ({ proposalQuery }) => {
const text = (() => {
if (proposalQuery.status === "success") {
const proposal = proposalQuery.data;

if (proposal.status === "ongoing") {
const timeRemaining = secondsToTimeRemainingString(
proposal.currentTime,
proposal.endTime
);
return `${timeRemaining} Remaining`;
}
}

return "";
})();

return <span className="text-right">{text}</span>;
};

const ProgressStartEnd: React.FC<{
proposal: AtomWithQueryResult<StoredProposal>;
epochKey: "startEpoch" | "endEpoch";
timeKey: "startTime" | "endTime";
className: string;
}> = ({ proposal, epochKey, className }) => (
}> = ({ proposal, timeKey, className }) => (
<div className={className}>
{proposal.status === "pending" || proposal.status === "error" ?
"..."
: showEpoch(proposal.data[epochKey])}
: secondsToDateTimeString(proposal.data[timeKey])}
</div>
);

Expand Down
6 changes: 3 additions & 3 deletions apps/namadillo/src/App/Governance/ProposalLabels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ProposalStatus, ProposalType } from "@namada/types";
import { assertNever } from "@namada/utils";
import { GoCheckCircleFill } from "react-icons/go";
import { twMerge } from "tailwind-merge";
import { showProposalStatus, showProposalTypeString } from "utils";
import { proposalStatusToString, proposalTypeStringToString } from "utils";

export const StatusLabel: React.FC<
{
Expand All @@ -19,7 +19,7 @@ export const StatusLabel: React.FC<

return (
<RoundedLabel className={twMerge(className, statusClassName)} {...rest}>
{showProposalStatus(status)}
{proposalStatusToString(status)}
</RoundedLabel>
);
};
Expand Down Expand Up @@ -50,6 +50,6 @@ export const TypeLabel: React.FC<
} & React.ComponentProps<typeof InsetLabel>
> = ({ proposalType, ...rest }) => (
<InsetLabel className="text-xs leading-[1.65em]" {...rest}>
{showProposalTypeString(proposalType.type)}
{proposalTypeStringToString(proposalType.type)}
</InsetLabel>
);
8 changes: 4 additions & 4 deletions apps/namadillo/src/App/Governance/VoteInfoCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AddRemove, PgfActions } from "@namada/types";

import { proposalFamilyPersist, StoredProposal } from "atoms/proposals";
import { useAtomValue } from "jotai";
import { showEpoch } from "utils";
import { epochToString, secondsToDateTimeString } from "utils";

const InfoCard: React.FC<
{
Expand Down Expand Up @@ -120,17 +120,17 @@ const Loaded: React.FC<{
<>
<InfoCard
title="Voting Start"
content={showEpoch(proposal.startEpoch)}
content={secondsToDateTimeString(proposal.startTime)}
className="col-span-2"
/>
<InfoCard
title="Voting End"
content={showEpoch(proposal.endEpoch)}
content={secondsToDateTimeString(proposal.endTime)}
className="col-span-2"
/>
<InfoCard
title="Activation Epoch"
content={showEpoch(proposal.activationEpoch)}
content={epochToString(proposal.activationEpoch)}
className="col-span-2"
/>
<InfoCard
Expand Down
59 changes: 55 additions & 4 deletions apps/namadillo/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ProposalStatus, ProposalTypeString } from "@namada/types";
import * as fns from "date-fns";
import { DateTime } from "luxon";
import { EventData, TransactionEvent } from "types/events";

export const showProposalStatus = (status: ProposalStatus): string => {
export const proposalStatusToString = (status: ProposalStatus): string => {
const statusText: Record<ProposalStatus, string> = {
pending: "Upcoming",
ongoing: "Ongoing",
Expand All @@ -12,7 +14,9 @@ export const showProposalStatus = (status: ProposalStatus): string => {
return statusText[status];
};

export const showProposalTypeString = (type: ProposalTypeString): string => {
export const proposalTypeStringToString = (
type: ProposalTypeString
): string => {
const typeText: Record<ProposalTypeString, string> = {
default: "Default",
default_with_wasm: "Default with Wasm",
Expand All @@ -23,9 +27,10 @@ export const showProposalTypeString = (type: ProposalTypeString): string => {
return typeText[type];
};

export const showEpoch = (epoch: bigint): string => `Epoch ${epoch.toString()}`;
export const epochToString = (epoch: bigint): string =>
`Epoch ${epoch.toString()}`;

export const showProposalId = (proposalId: bigint): string =>
export const proposalIdToString = (proposalId: bigint): string =>
`#${proposalId.toString()}`;

export const addTransactionEvent = <T>(
Expand All @@ -34,3 +39,49 @@ export const addTransactionEvent = <T>(
): void => {
window.addEventListener(handle, callback as EventListener, false);
};

const secondsToDateTime = (seconds: bigint): DateTime =>
DateTime.fromSeconds(Number(seconds));

export const secondsToTimeString = (seconds: bigint): string =>
secondsToDateTime(seconds).toLocaleString(DateTime.TIME_24_SIMPLE);

export const secondsToDateString = (seconds: bigint): string =>
secondsToDateTime(seconds).toLocaleString(DateTime.DATE_MED);

export const secondsToDateTimeString = (seconds: bigint): string =>
`${secondsToDateString(seconds)}, ${secondsToTimeString(seconds)}`;

export const secondsToTimeRemainingString = (
startTimeInSeconds: bigint,
endTimeInSeconds: bigint
): string => {
if (endTimeInSeconds < startTimeInSeconds) {
throw new Error(
`endTimeInSeconds ${endTimeInSeconds} is before startTimeInSeconds ${startTimeInSeconds}`
);
}

const toMilliNumber = (n: bigint): number =>
fns.secondsToMilliseconds(Number(n));
const interval = {
start: toMilliNumber(startTimeInSeconds),
end: toMilliNumber(endTimeInSeconds),
};
const format: fns.DurationUnit[] = ["days", "hours", "minutes"];
const timeLeft = fns.intervalToDuration(interval);
const formatted = fns.formatDuration(timeLeft, {
format,
zero: true,
delimiter: ": ",
});

if (formatted === "") {
return "< 1 Min";
}

return formatted
.replace("day", "Day")
.replace("hour", "Hr")
.replace("minute", "Min");
};
Loading
Loading