Skip to content

Commit

Permalink
fix: voting polish (#967)
Browse files Browse the repository at this point in the history
* feat: voting polish

* feat: voting polish

* feat: voting polish

* feat: voting polish
  • Loading branch information
sandren authored Jan 10, 2025
1 parent ced3322 commit 1011dba
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 46 deletions.
44 changes: 34 additions & 10 deletions apps/web/app/home/accept-or-reject-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
'use client';

import cx from 'classnames';

import { useState } from 'react';

import { useSmartAccount } from '~/core/hooks/use-smart-account';
import { useVote } from '~/core/hooks/use-vote';
import { Proposal } from '~/core/io/dto/proposals';
import { SubstreamVote } from '~/core/io/schema';

import { SmallButton } from '~/design-system/button';
import { Pending } from '~/design-system/pending';

import { Execute } from '~/partials/active-proposal/execute';

Expand All @@ -29,13 +34,30 @@ export function AcceptOrRejectEditor({
onchainProposalId,
votingContractAddress,
}: Props) {
const { vote } = useVote({
const { vote, status: voteStatus } = useVote({
address: votingContractAddress,
onchainProposalId,
});

const [hasApproved, setHasApproved] = useState<boolean>(false);
const [hasRejected, setHasRejected] = useState<boolean>(false);

const hasVoted = voteStatus === 'success';
const isPendingApproval = hasApproved && voteStatus === 'pending';
const isPendingRejection = hasRejected && voteStatus === 'pending';

const smartAccount = useSmartAccount();

const onApprove = () => {
setHasApproved(true);
vote('ACCEPT');
};

const onReject = () => {
setHasRejected(true);
vote('REJECT');
};

if (isProposalExecutable) {
return (
<Execute contractAddress={votingContractAddress} onchainProposalId={onchainProposalId}>
Expand All @@ -44,8 +66,8 @@ export function AcceptOrRejectEditor({
);
}

if (userVote) {
if (userVote.vote === 'ACCEPT') {
if (userVote || hasVoted) {
if (userVote?.vote === 'ACCEPT' || hasApproved) {
return <div className="rounded bg-successTertiary px-3 py-2 text-button text-green">You accepted</div>;
}

Expand All @@ -62,13 +84,15 @@ export function AcceptOrRejectEditor({

if (!isProposalEnded && smartAccount) {
return (
<div className="flex items-center gap-2">
<SmallButton variant="secondary" onClick={() => vote('REJECT')}>
Reject
</SmallButton>
<SmallButton variant="secondary" onClick={() => vote('ACCEPT')}>
Approve
</SmallButton>
<div className="relative">
<div className="flex items-center gap-2">
<SmallButton variant="secondary" onClick={onReject} disabled={voteStatus !== 'idle'}>
<Pending isPending={isPendingRejection}>Reject</Pending>
</SmallButton>
<SmallButton variant="secondary" onClick={onApprove} disabled={voteStatus !== 'idle'}>
<Pending isPending={isPendingApproval}>Approve</Pending>
</SmallButton>
</div>
</div>
);
}
Expand Down
81 changes: 56 additions & 25 deletions apps/web/app/home/accept-or-reject-member.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
'use client';

import { MemberAccessAbi } from '@geogenesis/sdk/abis';
import cx from 'classnames';
import { Effect, Either } from 'effect';
import { encodeFunctionData } from 'viem';

import { useState } from 'react';

import { useSmartAccountTransaction } from '~/core/hooks/use-smart-account-transaction';

import { SmallButton } from '~/design-system/button';
import { Pending } from '~/design-system/pending';

function useApproveOrReject(membershipContractAddress: string | null) {
const tx = useSmartAccountTransaction({
Expand Down Expand Up @@ -36,40 +40,67 @@ interface Props {
}

export function AcceptOrRejectMember(props: Props) {
const [isPendingApproval, setIsPendingApproval] = useState<boolean>(false);
const [isPendingRejection, setIsPendingRejection] = useState<boolean>(false);
const [hasVoted, setHasVoted] = useState<boolean>(false);

const approveOrReject = useApproveOrReject(props.membershipContractAddress);

const onApprove = async () => {
const hash = await approveOrReject(
encodeFunctionData({
abi: MemberAccessAbi,
functionName: 'approve',
args: [BigInt(props.onchainProposalId)],
})
);

console.log('transaction successful', hash);
try {
setIsPendingApproval(true);
const hash = await approveOrReject(
encodeFunctionData({
abi: MemberAccessAbi,
functionName: 'approve',
args: [BigInt(props.onchainProposalId)],
})
);
console.log('transaction successful', hash);
setHasVoted(true);
setIsPendingApproval(false);
} catch (error) {
console.error(error);
setHasVoted(false);
setIsPendingApproval(false);
}
};

const onReject = async () => {
const hash = await approveOrReject(
encodeFunctionData({
abi: MemberAccessAbi,
functionName: 'reject',
args: [BigInt(props.onchainProposalId)],
})
);

console.log('transaction successful', hash);
try {
setIsPendingRejection(true);
const hash = await approveOrReject(
encodeFunctionData({
abi: MemberAccessAbi,
functionName: 'reject',
args: [BigInt(props.onchainProposalId)],
})
);
console.log('transaction successful', hash);
setHasVoted(true);
setIsPendingRejection(false);
} catch (error) {
console.error(error);
setHasVoted(false);
setIsPendingRejection(false);
}
};

return (
<div className="flex items-center gap-2">
<SmallButton variant="secondary" onClick={onReject}>
Reject
</SmallButton>
<SmallButton variant="secondary" onClick={onApprove}>
Approve
</SmallButton>
<div className="relative">
<div className={cx('flex items-center gap-2', hasVoted && 'invisible')}>
<SmallButton variant="secondary" onClick={onReject}>
<Pending isPending={isPendingRejection}>Reject</Pending>
</SmallButton>
<SmallButton variant="secondary" onClick={onApprove}>
<Pending isPending={isPendingApproval}>Approve</Pending>
</SmallButton>
</div>
{hasVoted && (
<div className="absolute inset-0 flex h-full w-full items-center justify-center">
<div className="text-smallButton">Vote registered</div>
</div>
)}
</div>
);
}
43 changes: 32 additions & 11 deletions apps/web/partials/active-proposal/accept-or-reject.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
'use client';

import * as React from 'react';
import { useState } from 'react';

import { useSmartAccount } from '~/core/hooks/use-smart-account';
import { useVote } from '~/core/hooks/use-vote';
import { Proposal } from '~/core/io/dto/proposals';
import { SubstreamVote } from '~/core/io/schema';

import { Button } from '~/design-system/button';
import { Pending } from '~/design-system/pending';

import { Execute } from './execute';

Expand All @@ -31,13 +33,30 @@ export function AcceptOrReject({
onchainProposalId,
votingContractAddress,
}: Props) {
const { vote } = useVote({
const { vote, status: voteStatus } = useVote({
address: votingContractAddress,
onchainProposalId,
});

const [hasApproved, setHasApproved] = useState<boolean>(false);
const [hasRejected, setHasRejected] = useState<boolean>(false);

const hasVoted = voteStatus === 'success';
const isPendingApproval = hasApproved && voteStatus === 'pending';
const isPendingRejection = hasRejected && voteStatus === 'pending';

const smartAccount = useSmartAccount();

const onApprove = () => {
setHasApproved(true);
vote('ACCEPT');
};

const onReject = () => {
setHasRejected(true);
vote('REJECT');
};

if (isProposalExecutable) {
return (
<Execute contractAddress={votingContractAddress} onchainProposalId={onchainProposalId}>
Expand All @@ -46,8 +65,8 @@ export function AcceptOrReject({
);
}

if (userVote) {
if (userVote.vote === 'ACCEPT') {
if (userVote || hasVoted) {
if (userVote?.vote === 'ACCEPT' || hasApproved) {
return <div className="rounded bg-successTertiary px-3 py-2 text-button text-green">You accepted</div>;
}

Expand All @@ -64,14 +83,16 @@ export function AcceptOrReject({

if (!isProposalEnded && smartAccount) {
return (
<div className="inline-flex items-center gap-4">
<Button onClick={() => vote('REJECT')} variant="error">
Reject
</Button>
<span>or</span>
<Button onClick={() => vote('ACCEPT')} variant="success">
Accept
</Button>
<div className="relative">
<div className="inline-flex items-center gap-4">
<Button onClick={onReject} variant="error" disabled={voteStatus !== 'idle'}>
<Pending isPending={isPendingRejection}>Reject</Pending>
</Button>
<span>or</span>
<Button onClick={onApprove} variant="success" disabled={voteStatus !== 'idle'}>
<Pending isPending={isPendingApproval}>Accept</Pending>
</Button>
</div>
</div>
);
}
Expand Down

0 comments on commit 1011dba

Please sign in to comment.