Skip to content

Commit

Permalink
Merge pull request #97 from MinaFoundation/feature/consideration-ocv-…
Browse files Browse the repository at this point in the history
…already-voted

Feature/consideration ocv already voted
  • Loading branch information
iluxonchik authored Dec 19, 2024
2 parents b71bfee + 7bf9715 commit b8fa546
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 30 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pgt-web-app",
"version": "0.1.27",
"version": "0.1.28",
"private": true,
"type": "module",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ function parseOCVVoteData(data: JsonValue | null | undefined): OCVVoteData {
elegible: Boolean(voteData.elegible),
votes: Array.isArray(voteData.votes) ? voteData.votes.map(vote => ({
account: String(vote.account || ''),
timestamp: Number(vote.timestamp || 0)
timestamp: Number(vote.timestamp || 0),
hash: String(vote.hash || '')
})) : []
};
}
Expand Down Expand Up @@ -145,7 +146,8 @@ export async function GET(
isEligible: ocvVotes.elegible || false,
voters: ocvVotes.votes?.map((v: OCVVote) => ({
address: v.account,
timestamp: v.timestamp
timestamp: v.timestamp,
hash: v.hash
})) || []
},
reviewerEligible: approved >= minReviewerApprovals,
Expand Down
24 changes: 20 additions & 4 deletions src/components/ConsiderationProposalList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,16 @@ export function ConsiderationProposalList({ fundingRoundId, fundingRoundName }:
if (!proposal.isReviewerEligible) {
return (
<div className="flex gap-2">
<OCVVoteButton proposalId={proposal.id.toString()} useWallet={true} />
<OCVVoteButton proposalId={proposal.id.toString()} useWallet={false} />
<OCVVoteButton
proposalId={proposal.id.toString()}
useWallet={true}
voteStats={proposal.voteStats}
/>
<OCVVoteButton
proposalId={proposal.id.toString()}
useWallet={false}
voteStats={proposal.voteStats}
/>
</div>
);
}
Expand Down Expand Up @@ -329,8 +337,16 @@ export function ConsiderationProposalList({ fundingRoundId, fundingRoundName }:
if (!proposal.isReviewerEligible) {
return (
<div className="flex gap-2">
<OCVVoteButton proposalId={proposal.id.toString()} useWallet={true} />
<OCVVoteButton proposalId={proposal.id.toString()} useWallet={false} />
<OCVVoteButton
proposalId={proposal.id.toString()}
useWallet={true}
voteStats={proposal.voteStats}
/>
<OCVVoteButton
proposalId={proposal.id.toString()}
useWallet={false}
voteStats={proposal.voteStats}
/>
</div>
);
}
Expand Down
80 changes: 69 additions & 11 deletions src/components/web3/OCVVoteButton.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
"use client"

import { useState } from "react"
import { useState, useMemo } from "react"
import { Button } from "@/components/ui/button"
import { useWallet } from "@/contexts/WalletContext"
import { WalletConnectorDialog } from "./WalletConnectorDialog"
import { ManualVoteDialog } from "./dialogs/OCVManualInstructions"
import { OCVTransactionDialog } from "./dialogs/OCVTransactionDialog"
import { Icons } from "@/components/icons"
import { TARGET_NETWORK } from "@/contexts/WalletContext"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
TooltipProvider
} from "@/components/ui/tooltip"
import { formatDistanceToNow } from "date-fns"
import type { ConsiderationVoteStats } from "@/types/consideration"

interface OCVVoteButtonProps {
proposalId: string
useWallet?: boolean
voteStats: ConsiderationVoteStats
}

export function OCVVoteButton({ proposalId, useWallet: isWalletEnabled = true }: OCVVoteButtonProps) {
// Add type for voter
interface Voter {
address: string
timestamp: number
hash: string
}

export function OCVVoteButton({
proposalId,
useWallet: isWalletEnabled = true,
voteStats
}: OCVVoteButtonProps) {
const { state, enforceTargetNetwork } = useWallet()
const [showWalletDialog, setShowWalletDialog] = useState(false)
const [showManualDialog, setShowManualDialog] = useState(false)
const [showTransactionDialog, setShowTransactionDialog] = useState(false)

const existingVote = useMemo(() => {
if (!state.wallet?.address) return null
return voteStats.communityVotes.voters.find(
(voter: Voter) => voter.address.toLowerCase() === state.wallet!.address.toLowerCase()
)
}, [voteStats.communityVotes.voters, state.wallet])

const handleVoteClick = async () => {
if (!isWalletEnabled) {
setShowManualDialog(true)
Expand All @@ -43,20 +70,50 @@ export function OCVVoteButton({ proposalId, useWallet: isWalletEnabled = true }:

const buttonText = isWalletEnabled
? state.wallet
? "Vote with Wallet"
? existingVote
? "Already Voted"
: "Vote with Wallet"
: "Connect Wallet to Vote"
: "Vote Without Wallet"

const walletButton = (
<Button
variant={isWalletEnabled ? "default" : "outline"}
onClick={handleVoteClick}
className="gap-2"
disabled={Boolean(isWalletEnabled && state.wallet && existingVote)}
>
{isWalletEnabled && <Icons.wallet className="h-4 w-4" />}
{buttonText}
</Button>
)

return (
<>
<Button
variant={isWalletEnabled ? "default" : "outline"}
onClick={handleVoteClick}
className="gap-2"
>
{isWalletEnabled && <Icons.wallet className="h-4 w-4" />}
{buttonText}
</Button>
{isWalletEnabled && state.wallet && existingVote ? (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<div>{walletButton}</div>
</TooltipTrigger>
<TooltipContent
side="top"
align="center"
className="max-w-[300px] p-4 bg-popover text-popover-foreground"
>
<div className="space-y-2">
<p className="font-medium">You have already voted on this proposal</p>
<div className="text-xs text-muted-foreground space-y-1">
<p>Time: {formatDistanceToNow(existingVote.timestamp)} ago</p>
<p className="break-all">Transaction: {existingVote.hash}</p>
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
walletButton
)}

<WalletConnectorDialog
open={showWalletDialog}
Expand All @@ -68,6 +125,7 @@ export function OCVVoteButton({ proposalId, useWallet: isWalletEnabled = true }:
onOpenChange={setShowManualDialog}
voteId={proposalId}
voteType="YES"
existingVote={existingVote}
/>

{state.wallet && (
Expand Down
60 changes: 49 additions & 11 deletions src/components/web3/dialogs/OCVManualInstructions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,27 @@ import {
DialogTitle,
} from "@/components/ui/dialog"
import { cn } from "@/lib/utils"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { formatDistanceToNow } from "date-fns"

interface ManualVoteDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
voteId: string
voteType: "YES" | "NO"
existingVote?: {
address: string
timestamp: number
hash: string
} | null
}

export function ManualVoteDialog({
open,
onOpenChange,
voteId,
voteType
voteType,
existingVote
}: ManualVoteDialogProps) {
const [copied, setCopied] = useState(false)
const memo = `${voteType} ${voteId}`
Expand All @@ -45,21 +53,47 @@ export function ManualVoteDialog({
How do I cast my vote?
</DialogTitle>
</DialogHeader>
<div className="space-y-6 py-4">

<div className="space-y-4">
{existingVote && (
<Alert
className={cn(
"border-orange-200 dark:border-orange-900",
"bg-orange-100 dark:bg-orange-900/30",
"text-orange-900 dark:text-orange-200",
"[&>svg]:text-orange-900 dark:[&>svg]:text-orange-200"
)}
>
<AlertDescription>
<div className="space-y-2">
<p className="font-semibold">
Your connected wallet has already voted on this proposal
</p>
<div className="text-xs space-y-1 text-orange-800 dark:text-orange-200/90">
<p>Time: {formatDistanceToNow(existingVote.timestamp)} ago</p>
<p className="break-all">
Transaction: {existingVote.hash}
</p>
</div>
</div>
</AlertDescription>
</Alert>
)}

<div className="space-y-4">
<p className="text-center text-muted-foreground">
<p className="text-sm text-center text-muted-foreground">
Send a transaction to yourself with the following text in the memo field:
</p>

<div className="relative">
<div className="flex items-center justify-between rounded-lg border bg-muted p-4">
<code className="text-lg font-mono">{memo}</code>
<div className="flex items-center gap-2 rounded-lg border bg-muted/50 p-3">
<code className="flex-1 text-sm font-mono break-all">{memo}</code>
<Button
variant="ghost"
size="icon"
onClick={copyToClipboard}
className={cn(
"transition-colors",
"flex-shrink-0 transition-colors",
copied && "text-green-500"
)}
>
Expand All @@ -73,18 +107,22 @@ export function ManualVoteDialog({
</div>
</div>

<div className="space-y-2 text-sm text-muted-foreground">
<p className="font-medium text-foreground">Instructions:</p>
<ol className="list-decimal pl-4 space-y-1">
<div className="space-y-3">
<p className="font-medium text-sm">Instructions:</p>
<ol className="text-sm space-y-2 list-decimal list-inside text-muted-foreground">
<li>Copy the memo shown above</li>
<li>Use your preferred wallet (or a CLI) to create a transaction</li>
<li>Set the recipient address to your own address</li>
<li>Set the transaction amount (can be 0 MINA)</li>
<li>Set the memo field to the copied text</li>
<li>Send the transaction to cast your vote</li>
</ol>
<p className="mt-4 text-sm">
Note: Make sure to follow the exact format of the memo to ensure your vote is properly recorded. The transaction must be sent to your own address, and the amount can be 0 MINA.
</div>

<div className="rounded-lg bg-muted/50 p-3">
<p className="text-xs text-muted-foreground">
Note: Make sure to follow the exact format of the memo to ensure your vote is properly recorded.
The transaction must be sent to your own address, and the amount can be 0 MINA.
</p>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/types/consideration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface ConsiderationVoteStats {
voters: Array<{
address: string;
timestamp: number;
hash: string;
}>;
};
reviewerEligible: boolean;
Expand Down Expand Up @@ -39,6 +40,7 @@ export interface ConsiderationProposal {
export interface OCVVote {
account: string;
timestamp: number;
hash: string;
}

export interface OCVVoteData {
Expand Down

0 comments on commit b8fa546

Please sign in to comment.