Skip to content

Commit

Permalink
Merge pull request #20 from aragon/f/ui-improvements-3
Browse files Browse the repository at this point in the history
F/UI improvements 3
  • Loading branch information
brickpop authored Jul 29, 2024
2 parents 1df76cc + f792d23 commit a947305
Show file tree
Hide file tree
Showing 24 changed files with 291 additions and 119 deletions.
Binary file modified bun.lockb
Binary file not shown.
22 changes: 19 additions & 3 deletions components/proposalActions/proposalActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
AccordionItemContent,
AccordionItemHeader,
AvatarIcon,
Button,
IconType,
InputText,
} from "@aragon/ods";
Expand All @@ -26,10 +27,11 @@ interface IProposalActionsProps {
description?: string;
emptyListDescription?: string;
actions?: RawAction[];
onRemove?: (index: number) => any;
}

export const ProposalActions: React.FC<IProposalActionsProps> = (props) => {
const { actions, description, emptyListDescription } = props;
const { actions, description, emptyListDescription, onRemove } = props;

let message: string;
if (actions?.length) {
Expand All @@ -51,14 +53,21 @@ export const ProposalActions: React.FC<IProposalActionsProps> = (props) => {
{/* Content */}
<If condition={actions?.length}>
<AccordionContainer isMulti={true} className="border-t border-t-neutral-100">
{actions?.map((action, index) => <ActionItem key={index} index={index} rawAction={action} />)}
{actions?.map((action, index) => (
<ActionItem
key={index}
index={index}
rawAction={action}
onRemove={onRemove ? () => onRemove?.(index) : undefined}
/>
))}
</AccordionContainer>
</If>
</div>
);
};

const ActionItem = ({ index, rawAction }: { index: number; rawAction: RawAction }) => {
const ActionItem = ({ index, rawAction, onRemove }: { index: number; rawAction: RawAction; onRemove?: () => any }) => {
const action = useAction(rawAction);
const title = `Action ${index + 1}`;
const coinName = PUB_CHAIN.nativeCurrency.symbol;
Expand Down Expand Up @@ -129,6 +138,13 @@ const ActionItem = ({ index, rawAction }: { index: number; rawAction: RawAction
</If>
</Else>
</If>
<If condition={!!onRemove}>
<div className="mt-2">
<Button variant="tertiary" size="sm" iconLeft={IconType.CLOSE} onClick={onRemove}>
Remove action
</Button>
</div>
</If>
</div>
</AccordionItemContent>
</AccordionItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const VotingStageStatus: React.FC<IVotingStageStatus> = (props) => {
<AvatarIcon size="sm" variant="success" icon={IconType.CHECKMARK} className="shrink-0" />
</>
)}
{status === "rejected" && (
{["rejected", "vetoed"].includes(status) && (
<>
<div className="flex flex-grow items-center gap-x-0.5">
<span className="shrink-0 text-neutral-500">The proposal has been</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { iVotesAbi } from "../artifacts/iVotes.sol";
import { iVotesAbi } from "../plugins/members/artifacts/iVotes.sol";
import { PUB_CHAIN, PUB_TOKEN_ADDRESS } from "@/constants";
import { type Address } from "viem";
import { useReadContracts } from "wagmi";

/** Returns the delegate (if any) for the given address */
export const useGovernanceToken = (address?: Address) => {
export const useTokenVotes = (address?: Address) => {
const { data, isLoading, isError, refetch } = useReadContracts({
contracts: [
{
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
]
},
"dependencies": {
"@aragon/ods": "1.0.39",
"@aragon/ods": "1.0.40",
"@hookform/resolvers": "^3.9.0",
"@react-native-async-storage/async-storage": "^1.23.1",
"@shazow/whatsabi": "^0.13.2",
Expand Down
4 changes: 4 additions & 0 deletions plugins/emergency-multisig/hooks/usePublicKeyRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useQuery } from "@tanstack/react-query";
import { uint8ArrayToHex } from "@/utils/hex";
import { useDerivedWallet } from "../../../hooks/useDerivedWallet";
import { useAlerts } from "@/context/Alerts";
import { debounce } from "@/utils/debounce";

export function usePublicKeyRegistry() {
const config = useConfig();
Expand Down Expand Up @@ -71,6 +72,9 @@ export function usePublicKeyRegistry() {
useEffect(() => {
if (registrationStatus === "idle" || registrationStatus === "pending") return;
else if (registrationStatus === "error") {
// Refetch the status, just in case
debounce(() => refetch(), 800);

if (error?.message?.startsWith("User rejected the request")) {
addAlert("Transaction rejected by the user", {
timeout: 4 * 1000,
Expand Down
15 changes: 12 additions & 3 deletions plugins/emergency-multisig/pages/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export default function Create() {
setActions(actions.concat(newAction));
setAddActionType("");
};
const onRemoveAction = (idx: number) => {
actions.splice(idx, 1);
setActions([].concat(actions as any));
};
const removeResource = (idx: number) => {
resources.splice(idx, 1);
setResources([].concat(resources as any));
Expand All @@ -74,8 +78,9 @@ export default function Create() {
return (
<MainSection narrow>
<div className="w-full justify-between">
<h1 className="mb-10 text-3xl font-semibold text-neutral-900">Create Proposal</h1>

<h1 className="mb-8 line-clamp-1 flex flex-1 shrink-0 text-2xl font-normal leading-tight text-neutral-800 md:text-3xl">
Create Proposal
</h1>
<PlaceHolderOr selfAddress={selfAddress} canCreate={canCreate} isConnected={isConnected}>
<div className="mb-6">
<InputText
Expand Down Expand Up @@ -161,6 +166,7 @@ export default function Create() {
variant="tertiary"
size="lg"
iconLeft={IconType.PLUS}
disabled={isCreating}
onClick={() => {
setResources(resources.concat({ url: "", name: "" }));
}}
Expand All @@ -175,17 +181,20 @@ export default function Create() {
<ProposalActions
actions={actions}
emptyListDescription="The proposal has no actions defined yet. Select a type of action to add to the proposal."
onRemove={(idx) => onRemoveAction(idx)}
/>

<div className="mt-8 grid w-full grid-cols-2 gap-4 md:grid-cols-4">
<AddActionCard
title="Add a payment"
icon={IconType.WITHDRAW}
disabled={isCreating}
onClick={() => setAddActionType("withdrawal")}
/>
<AddActionCard
title="Select a function"
icon={IconType.BLOCKCHAIN_BLOCKCHAIN}
disabled={isCreating}
onClick={() => setAddActionType("select-abi-function")}
/>
<AddActionCard
Expand All @@ -196,8 +205,8 @@ export default function Create() {
/>
<AddActionCard
title="Copy the calldata"
disabled
icon={IconType.COPY}
disabled={isCreating}
onClick={() => setAddActionType("calldata")}
/>
</div>
Expand Down
10 changes: 4 additions & 6 deletions plugins/members/components/DelegateAnnouncementDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ export const DelegateAnnouncementDialog: React.FC<IDelegateAnnouncementDialogPro
const { fields, append, remove } = useFieldArray({ name: DELEGATE_RESOURCES, control });

const onSuccessfulAnnouncement = () => {
setTimeout(() => {
router.push("#/delegates/" + address!);
}, 2000);
router.push("#/delegates/" + address!);
};
const { isConfirming, status, announceDelegation } = useAnnounceDelegation(onSuccessfulAnnouncement);

Expand Down Expand Up @@ -212,6 +210,9 @@ export const DelegateAnnouncementDialog: React.FC<IDelegateAnnouncementDialogPro
</span>
</div>
<div className="mt-4 flex justify-between">
<Button variant="secondary" size="lg" onClick={onClose} disabled={isConfirming || status === "pending"}>
Cancel
</Button>
<Button
variant="primary"
size="lg"
Expand All @@ -220,9 +221,6 @@ export const DelegateAnnouncementDialog: React.FC<IDelegateAnnouncementDialogPro
>
{ctaLabel}
</Button>
<Button variant="secondary" size="lg" onClick={onClose} disabled={isConfirming || status === "pending"}>
Cancel
</Button>
</div>
</DialogContent>
<DialogFooter />
Expand Down
4 changes: 2 additions & 2 deletions plugins/members/components/DelegateListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { formatHexString, equalAddresses } from "@/utils/evm";
import { type IDataListItemProps, DataList, MemberAvatar, Tag } from "@aragon/ods";
import { useAccount } from "wagmi";
import { Address, formatEther } from "viem";
import { useGovernanceToken } from "../hooks/useGovernanceToken";
import { useTokenVotes } from "../../../hooks/useTokenVotes";
import VerifiedDelegates from "../../../verified-delegates.json";
import { useDelegateAnnounce } from "../hooks/useDelegateAnnounce";

Expand All @@ -20,7 +20,7 @@ export const DelegateListItem: React.FC<IDelegateListItemProps> = (props) => {
const { isMyDelegate, avatarSrc, address, ...otherProps } = props;
const { address: currentUserAddress, isConnected } = useAccount();
const isCurrentUser = isConnected && address && equalAddresses(currentUserAddress, address);
const { votingPower } = useGovernanceToken(address);
const { votingPower } = useTokenVotes(address);
const isVerified = VerifiedDelegates.findIndex((d) => equalAddresses(d.address, address)) >= 0;
const { announce } = useDelegateAnnounce(address);

Expand Down
4 changes: 2 additions & 2 deletions plugins/members/components/DelegateMemberList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DataList, IllustrationHuman } from "@aragon/ods";
import { DelegateListItem } from "./DelegateListItem";
import { equalAddresses } from "@/utils/evm";
import { useDelegates } from "../hooks/useDelegates";
import { useGovernanceToken } from "../hooks/useGovernanceToken";
import { useTokenVotes } from "../../../hooks/useTokenVotes";
import { useAccount } from "wagmi";
import VerifiedDelegates from "../../../verified-delegates.json";
import { PleaseWaitSpinner } from "@/components/please-wait";
Expand All @@ -20,7 +20,7 @@ export const DelegateMemberList: React.FC<IDelegateMemberListProps> = ({ verifie
const [searchValue, setSearchValue] = useState<string>();
// const [activeSort, setActiveSort] = useState<string>();
const { delegates: fetchedDelegates, status: loadingStatus } = useDelegates();
const { delegatesTo } = useGovernanceToken(address);
const { delegatesTo } = useTokenVotes(address);
const delegates = (fetchedDelegates || []).filter((item) => {
if (!verifiedOnly) return true;
return VerifiedDelegates.findIndex((d) => equalAddresses(d.address, item)) >= 0;
Expand Down
6 changes: 3 additions & 3 deletions plugins/members/components/HeaderMember.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { formatEther, type Address } from "viem";
import { mainnet } from "viem/chains";
import { useAccount, useEnsName } from "wagmi";
import { useGovernanceToken } from "../hooks/useGovernanceToken";
import { useTokenVotes } from "../../../hooks/useTokenVotes";
import { Else, ElseIf, If, Then } from "@/components/if";
import { useWeb3Modal } from "@web3modal/wagmi/react";
import { useDelegateVotingPower } from "../hooks/useDelegateVotingPower";
Expand All @@ -31,8 +31,8 @@ export const HeaderMember: React.FC<IHeaderMemberProps> = (props) => {
const { open } = useWeb3Modal();
const { address: myAddress, isConnected } = useAccount();
const { data: ensName } = useEnsName({ chainId: mainnet.id, address: delegateAddress });
const { votingPower, balance: delegateTokenBalance, refetch } = useGovernanceToken(delegateAddress);
const { delegatesTo } = useGovernanceToken(myAddress);
const { votingPower, balance: delegateTokenBalance, refetch } = useTokenVotes(delegateAddress);
const { delegatesTo } = useTokenVotes(myAddress);
const { delegateVotingPower, isLoading: isConfirming } = useDelegateVotingPower(delegateAddress, refetch);
const formattedAddress = formatHexString(delegateAddress);
const isVerified = VerifiedDelegates.findIndex((d) => equalAddresses(d.address, delegateAddress)) >= 0;
Expand Down
31 changes: 21 additions & 10 deletions plugins/members/hooks/useAnnounceDelegation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { PUB_DELEGATION_WALL_CONTRACT_ADDRESS } from "@/constants";
import { useAlerts } from "@/context/Alerts";
import { logger } from "@/services/logger";
import { uploadToPinata } from "@/utils/ipfs";
import { useCallback, useEffect } from "react";
import { useCallback, useEffect, useState } from "react";
import { toHex } from "viem";
import { useWaitForTransactionReceipt, useWriteContract } from "wagmi";
import { type IAnnouncementMetadata } from "../utils/types";

export function useAnnounceDelegation(onSuccess?: () => void) {
const { addAlert } = useAlerts();
const { writeContract, data: hash, error, status } = useWriteContract();
const [uploading, setUploading] = useState(false);
const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({ hash });

useEffect(() => {
Expand Down Expand Up @@ -50,18 +51,28 @@ export function useAnnounceDelegation(onSuccess?: () => void) {
async (metadata: IAnnouncementMetadata) => {
if (!metadata) return;

setUploading(true);

try {
const ipfsUrl = await uploadToPinata(JSON.stringify(metadata));

if (ipfsUrl) {
writeContract({
abi: DelegateAnnouncerAbi,
address: PUB_DELEGATION_WALL_CONTRACT_ADDRESS,
functionName: "register",
args: [toHex(ipfsUrl)],
});
}
if (!ipfsUrl) throw new Error("Empty IPFS URL");
setTimeout(() => setUploading(false), 1000);

writeContract({
abi: DelegateAnnouncerAbi,
address: PUB_DELEGATION_WALL_CONTRACT_ADDRESS,
functionName: "register",
args: [toHex(ipfsUrl)],
});
} catch (error) {
setUploading(false);

addAlert("Could not upload the details", {
description: "The profile details could not be pinned on IPFS",
type: "error",
});

logger.error("Could not upload delegate profile metadata to IPFS", error);
}
},
Expand All @@ -71,7 +82,7 @@ export function useAnnounceDelegation(onSuccess?: () => void) {
return {
announceDelegation,
isConfirmed,
isConfirming,
isConfirming: uploading || isConfirming,
status,
};
}
13 changes: 12 additions & 1 deletion plugins/multisig/pages/new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export default function Create() {
setActions(actions.concat(newAction));
setAddActionType("");
};
const onRemoveAction = (idx: number) => {
actions.splice(idx, 1);
setActions([].concat(actions as any));
};
const removeResource = (idx: number) => {
resources.splice(idx, 1);
setResources([].concat(resources as any));
Expand All @@ -63,7 +67,9 @@ export default function Create() {
return (
<MainSection narrow>
<div className="w-full justify-between">
<h1 className="mb-10 text-3xl font-semibold text-neutral-900">Create Proposal</h1>
<h1 className="mb-8 line-clamp-1 flex flex-1 shrink-0 text-2xl font-normal leading-tight text-neutral-800 md:text-3xl">
Create Proposal
</h1>

<PlaceHolderOr selfAddress={selfAddress} canCreate={canCreate} isConnected={isConnected}>
<div className="mb-6">
Expand Down Expand Up @@ -150,6 +156,7 @@ export default function Create() {
variant="tertiary"
size="lg"
iconLeft={IconType.PLUS}
disabled={isCreating}
onClick={() => {
setResources(resources.concat({ url: "", name: "" }));
}}
Expand All @@ -164,17 +171,20 @@ export default function Create() {
<ProposalActions
actions={actions}
emptyListDescription="The proposal has no actions defined yet. Select a type of action to add to the proposal."
onRemove={(idx) => onRemoveAction(idx)}
/>

<div className="mt-8 grid w-full grid-cols-2 gap-4 md:grid-cols-4">
<AddActionCard
title="Add a payment"
icon={IconType.WITHDRAW}
disabled={isCreating}
onClick={() => setAddActionType("withdrawal")}
/>
<AddActionCard
title="Select a function"
icon={IconType.BLOCKCHAIN_BLOCKCHAIN}
disabled={isCreating}
onClick={() => setAddActionType("select-abi-function")}
/>
<AddActionCard
Expand All @@ -186,6 +196,7 @@ export default function Create() {
<AddActionCard
title="Copy the calldata"
icon={IconType.COPY}
disabled={isCreating}
onClick={() => setAddActionType("calldata")}
/>
</div>
Expand Down
Loading

0 comments on commit a947305

Please sign in to comment.