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

[feat-287] vote pop up confirmation #292

Merged
merged 1 commit into from
Dec 13, 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
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
Loading