Skip to content

Commit

Permalink
Merge pull request #292 from adrianvrj/feat-287
Browse files Browse the repository at this point in the history
[feat-287] vote pop up confirmation
  • Loading branch information
EmmanuelAR authored Dec 13, 2024
2 parents de9f4fc + 3e7577c commit a53b9db
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 121 deletions.
7 changes: 4 additions & 3 deletions contracts/src/fund.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub trait IFund<TContractState> {
fn get_current_goal_state(self: @TContractState) -> u256;
fn set_state(ref self: TContractState, state: u8);
fn get_state(self: @TContractState) -> u8;
fn get_voter(self: @TContractState) -> u32;
fn get_voter(self: @TContractState, user: ContractAddress) -> u32;
fn withdraw(ref self: TContractState);
fn set_evidence_link(ref self: TContractState, evidence: ByteArray);
fn get_evidence_link(self: @TContractState) -> ByteArray;
Expand Down Expand Up @@ -226,8 +226,9 @@ pub mod Fund {
fn get_state(self: @ContractState) -> u8 {
return self.state.read();
}
fn get_voter(self: @ContractState) -> u32 {
return self.voters.read(get_caller_address());
fn get_voter(self: @ContractState, user: ContractAddress) -> u32 {
let voter = self.voters.read(user);
return voter;
}
fn withdraw(ref self: ContractState) {
let caller = get_caller_address();
Expand Down
12 changes: 6 additions & 6 deletions frontend/gostarkme-web/app/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,28 @@ const Dashboard = () => {
async function getFunds() {
const fundManagerContract = new Contract(fundManager, FUND_MANAGER_ADDR);

const id = await fundManagerContract.getCurrentId();
const id = await fundManagerContract.get_current_id();
let fundings = [];
for (let i = 1; i < id; i++) {
// GET FUND ADDRESS
let fundaddr = await fundManagerContract.getFund(i);
let fundaddr = await fundManagerContract.get_fund(i);
fundaddr = "0x" + fundaddr.toString(16);
const fundContract = new Contract(fundAbi, fundaddr);
// GET FUND STATE
let state = await fundContract.getState();
let state = await fundContract.get_state();
if (state == 4 || state == 0) {
continue;
}
// GET FUND NAME
let name = await fundContract.getName();
let name = await fundContract.get_name();
// GET FUND DESCRIPTION
let desc = await fundContract.getReason();
let desc = await fundContract.get_reason();
let desclen = desc.length;
if (desclen > 50) {
desc = desc.substring(0, 50) + "...";
}
// GET FUND ID
let fund_id = await fundContract.getId();
let fund_id = await fundContract.get_id();
fundings.push({
type: "Project",
title: name,
Expand Down
2 changes: 1 addition & 1 deletion frontend/gostarkme-web/components/dashboard/fundCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface FundCardProps {
type: string;
title: string;
description: string;
fund_id: string
fund_id: string;
};
index: number;
}
Expand Down
22 changes: 12 additions & 10 deletions frontend/gostarkme-web/components/modules/Fund/Fund.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,29 @@ const Fund = () => {
const clickedFund = useAtomValue(clickedFundState);

async function getDetails() {
let addr = await fundManagerContract.getFund(clickedFund?.id);
let addr = await fundManagerContract.get_fund(clickedFund?.id);
addr = "0x" + addr.toString(16);
const fundContract = new Contract(fundAbi, addr, wallet?.account);
try {
// Fetch fund details
let name = await fundContract.getName();
let desc = await fundContract.getReason();
let name = await fundContract.get_name();
let desc = await fundContract.get_reason();
if (desc == " ") {
desc = "No description provided";
}
let state = await fundContract.getState();
let state = await fundContract.get_state();
let currentBalance = await fundContract.get_current_goal_state();
currentBalance = BigInt(currentBalance) / BigInt(10 ** 18);
let goal = await fundContract.getGoal();
let goal = await fundContract.get_goal();
goal = BigInt(goal) / BigInt(10 ** 18);
let upVotes = await fundContract.getUpVotes();
let upVotes = await fundContract.get_up_votes();
let evidenceLink = await fundContract.get_evidence_link();
let contactHandle = await fundContract.get_contact_handle();

console.log(wallet?.account?.address.toLowerCase());
// Fetch owner
const owner = (await fundContract.getOwner()).toString();
const owner = (await fundContract.get_owner()).toString();
setIsOwner(owner.toLowerCase() === wallet?.account?.address.toLowerCase());
// USER VOTED?
let voted = await fundContract.get_voter(wallet?.account.address);

setFund({
name: name,
Expand All @@ -63,6 +63,7 @@ const Fund = () => {
addr: addr,
evidenceLink: evidenceLink,
contactHandle: contactHandle,
voted: voted
});
} catch (error) {
console.error("Error fetching fund details:", error);
Expand Down Expand Up @@ -102,11 +103,12 @@ const Fund = () => {
{Number(fund.state) === 0 && <p>Fund is currently inactive.</p>}
{Number(fund.state) === 1 && (
<FundVote
name={fund.name}
upVotes={fund.upVotes}
upVotesNeeded={upVotesNeeded}
addr={fund.addr}
setLoading={setLoading}
getDetails={getDetails}
voted={fund.voted}
/>
)}
{Number(fund.state) === 2 && (
Expand Down
154 changes: 84 additions & 70 deletions frontend/gostarkme-web/components/modules/Fund/FundVote.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,87 @@
import { calculatePorcentage } from "@/app/utils";
import { Button } from "@/components/ui/Button";
import ProgressBar from "@/components/ui/ProgressBar";
import { fundAbi } from "@/contracts/abis/fund";
import {
walletStarknetkitLatestAtom,
} from "@/state/connectedWallet";
import { latestTxAtom } from "@/state/latestTx";
import { useAtomValue, useSetAtom } from "jotai";
import { Contract } from "starknet";
import { useRouter } from "next/navigation";
import { useState, useEffect } from "react";
import { useAtom, useAtomValue } from "jotai";
import { useState } from "react";
import ShareXButton from "@/components/ui/ShareOnX";
import { provider } from "@/constants";
import { CallData } from "starknet";

interface FundVoteProps {
name: String,
upVotes: number,
upVotesNeeded: number,
addr: string,
voted: any,
setLoading: (load: boolean) => void,
getDetails: () => void,
}

export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteProps) => {

export const FundVote = ({ name, upVotes, upVotesNeeded, addr, voted, setLoading }: FundVoteProps) => {
const wallet = useAtomValue(walletStarknetkitLatestAtom);
const [network, setNetwork] = useState(wallet?.chainId);
const networkEnvironment = process.env.NEXT_PUBLIC_CHAIN_ID;

const progress = calculatePorcentage(upVotes, upVotesNeeded);

const setLatestTx = useSetAtom(latestTxAtom);

const router = useRouter();
const [progress, setProgress] = useState(calculatePorcentage(upVotes, upVotesNeeded));
const [currentUpvotes, setCurrentUpvotes] = useState(upVotes);
const voteMessage = ` 🗳️ Just cast my vote for an amazing cause called ${name} on Go Stark Me! This fund needs more votes to start raising funds—every vote counts! Let’s support projects that make a difference at https://web3wagers.github.io/gostarkme/ @undefined_org_ 🙌💫 #GoStarkMe #Starknet #CommunityPower`;

const [isChecking, setIsChecking] = useState(true);
const [voteStatus, setVoteStatus] = useState(false);
const [isVoting, setIsVoting] = useState(false);
const [showSuccessPopup, setShowSuccessPopup] = useState(false);
const [latestTx, setLatestTx] = useAtom(latestTxAtom);
const [canVote, setCanVote] = useState((voted != BigInt(0) ? false : true));

const handleNetwork = (chainId?: string, accounts?: string[]) => {
setNetwork(wallet?.chainId);
};
wallet?.on('networkChanged', handleNetwork);

useEffect(() => {
const checkVoteStatus = async () => {
if (!wallet?.account) {
setIsChecking(false);
return;
}

setIsChecking(true);
try {
const fundContract = new Contract(fundAbi, addr, wallet.account);

try {
await fundContract.estimate('receiveVote');
setVoteStatus(false);
} catch (error: any) {
if (error?.toString().includes('User already voted')) {
setVoteStatus(true);
}
}
} catch (error) {
console.error("Contract interaction error:", error);
} finally {
setIsChecking(false);
}
};
const waitForTransaction = async (hash: string) => {
try {
await provider.waitForTransaction(hash);
return true;
} catch (error) {
console.error("Error waiting for transaction:", error);
return false;
}
};

checkVoteStatus();
}, [wallet?.account, addr]);
const handleVoteClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();

const handleVote = async () => {
if (!wallet?.account) return;
setLoading(true);
setIsVoting(true);

try {
const fundContract = new Contract(fundAbi, addr, wallet.account);
const resp = await fundContract.invoke('receiveVote');
setLatestTx({ txHash: resp.transaction_hash, type: "vote" });
setVoteStatus(true);
router.push("/app/confirmation");
} catch (error: any) {
console.error("Vote failed:", error);
if (error?.toString().includes('User already voted')) {
console.log("User has already voted");
setVoteStatus(true);
const tx = await wallet?.account.execute([
{
contractAddress: addr,
entrypoint: 'receive_vote',
calldata: CallData.compile({
}),
},
]);

if (tx) {
const isConfirmed = await waitForTransaction(tx.transaction_hash);

if (isConfirmed) {
setLatestTx(tx.transaction_hash);
setCanVote(false);
setShowSuccessPopup(true);
setCurrentUpvotes(prev => Number(BigInt(prev) + BigInt(1)));
setProgress(calculatePorcentage(Number(BigInt(upVotes) + BigInt(1)), Number(upVotesNeeded)))
setTimeout(() => {
}, 3000);
} else {
console.log('tx not successfull')
}
}
} catch (error: any) {
console.log(error.message || "Transaction failed. Please try again.");
} finally {
setLoading(false);
setIsVoting(false);
}
};
Expand All @@ -95,20 +90,20 @@ export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteP
<div className="flex flex-col">
<ProgressBar progress={progress} />
<div className="flex justify-center my-2">
<p className="text-center mx-2">{upVotes.toString()} / {upVotesNeeded.toString()} </p>
<p className="text-center mx-2">{currentUpvotes.toString()} / {upVotesNeeded.toString()} </p>
<p>&#127775;</p>
</div>
{isChecking ? ( //if isChecking is true render a button that checks the vote status
{isVoting ? (
<div className="text-center">
<Button
label="Checking vote status..."
label="Voting..."
onClick={() => { }}
className="opacity-50 cursor-not-allowed"
disabled={true}
/>
</div>
) : wallet ? ( // Check if a wallet is connected by evaluating 'wallet' condition
voteStatus ? ( //if voteStatus is true button is disabled
) : wallet ? (
!canVote ? (
<div className="text-center">
<Button
label="Vote"
Expand All @@ -120,15 +115,13 @@ export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteP
</div>
) : (
<div className="text-center">
<Button
label={isVoting ? "Voting..." : "Vote"}
onClick={handleVote}
<button
onClick={handleVoteClick}
disabled={isVoting || network !== networkEnvironment}
className={
isVoting || network !== networkEnvironment ? "opacity-50 cursor-not-allowed" : ""
}
/>
{/* // If the wallet is connected, and voteStatus is false render a button that allows voting */}
className={`self-center bg-darkblue text-white py-2 px-6 md:py-3 md:px-10 rounded-md
text-xs md:text-sm shadow-xl hover:bg-starkorange active:bg-darkblue ease-in-out
duration-500 active:duration-0 shadow-gray-400 ${network !== networkEnvironment ? "opacity-50 cursor-not-allowed" : ""}`}
>Vote</button>
{network !== networkEnvironment && (
<p className="text-sm text-gray-500 mt-2">
Your wallet is currently connected to the wrong network. Please
Expand All @@ -137,7 +130,7 @@ export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteP
)}
</div>
)
) : ( // If the wallet is not connected, render a disabled vote button with instructions
) : (
<div className="text-center">
<Button
label="Vote"
Expand All @@ -150,6 +143,27 @@ export const FundVote = ({ upVotes, upVotesNeeded, addr, setLoading }: FundVoteP
</p>
</div>
)}
{showSuccessPopup && (
<div className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex justify-center items-center">
<div className="w-[600px] rounded-md flex flex-col items-center justify-center gap-4 text-center bg-white drop-shadow p-7">
<button
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
onClick={(e) => {
e.stopPropagation();
setShowSuccessPopup(false);
}}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<h1 className="text-xl">Success</h1>
<p className="text-l font-light m-5">Your vote was submitted, take a look at the transaction <a className="text-blue-600" target="_blank" href={"https://sepolia.voyager.online/tx/" + latestTx}>here.</a></p>
<p className="text-l font-light m-5">Share your contribution via X to tell everyone how cool you are</p>
<ShareXButton message={voteMessage} />
</div>
</div>
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
'use client';
import React, { useEffect } from "react";
import React from "react";
import CreationConfirmation from "./CreationConfirmation";
import VoteConfirmation from "./VoteConfirmation";
import DonationConfirmation from "./DonationConfirmation";
import { useAtom, useAtomValue } from "jotai";
import { useAtomValue } from "jotai";
import { latestTxAtom } from "@/state/latestTx";
import Navbar from "@/components/ui/Navbar";
import { navItems } from "@/constants";
import { clickedFundState } from "@/state/nFunds";
import { walletStarknetkitLatestAtom } from "@/state/connectedWallet";
import WithdrawConfirmation from "./WithdrawConfirmation";

const Confirmation = () => {
const tx = useAtomValue(latestTxAtom);
const actualFund = useAtomValue(clickedFundState);
const voteMessage = ` 🗳️ Just cast my vote for an amazing cause called ${actualFund?.name} on Go Stark Me! This fund needs more votes to start raising funds—every vote counts! Let’s support projects that make a difference at https://web3wagers.github.io/gostarkme/ @undefined_org_ 🙌💫 #GoStarkMe #Starknet #CommunityPower`;
const donationMessage = `🙌 Proud to support ${actualFund?.name} on Go Stark Me! Donations make a difference. 💪 Go ahead and donate at https://web3wagers.github.io/gostarkme/ @undefined_org_ #Starknet #GoStarkMe #Web3Wagers`;
const newFundMessage = `🚀 Just launched a new fund on Go Stark Me called ${actualFund?.name}! I’m raising support for an important cause, and every contribution makes a difference. Join me in making an impact at https://web3wagers.github.io/gostarkme/! 💪🌍 Check it out on @undefined_org_ #GoStarkMe #Starknet #BlockchainForGood`;
const withdrawnMessage = `🎉 We did it! The goal for ${actualFund?.name} on Go Stark Me has been reached, and funds have been successfully withdrawn! 🙌 Huge thanks to everyone who contributed and made this possible. Let’s keep making an impact! 🌍💪 Check it out at https://web3wagers.github.io/gostarkme/ #GoStarkMe #Starknet #CommunitySuccess`;
Expand Down Expand Up @@ -44,10 +41,6 @@ const Confirmation = () => {
<CreationConfirmation message={newFundMessage} txHash={tx.txHash} />
}

{tx?.type === "vote" &&
<VoteConfirmation message={voteMessage} txHash={tx.txHash} />
}

{tx?.type === "donation" &&
<DonationConfirmation message={donationMessage} txHash={tx.txHash} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const Stages = () => {

async function newFund() {
const fundManagerContract = new Contract(fundManager, FUND_MANAGER_ADDR, wallet?.account);
fundManagerContract.newFund(fundingName, cairo.uint256(Number(goal) * Number(10) ** Number(18) ) , evidenceLink, contactHandle, fundingDescription)
fundManagerContract.new_fund(fundingName, cairo.uint256(Number(goal) * Number(10) ** Number(18) ) , evidenceLink, contactHandle, fundingDescription)
.then(async (resp: InvokeFunctionResponse) => {
setLatesTx({ txHash: resp.transaction_hash, type: "newfund" });
setActualFund({id: 0, name: fundingName});
Expand Down
Loading

0 comments on commit a53b9db

Please sign in to comment.