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] added voting logic #179

Merged
merged 2 commits into from
Nov 3, 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
21 changes: 0 additions & 21 deletions frontend/gostarkme-web/app/app/fund/[fundId]/page.tsx

This file was deleted.

26 changes: 26 additions & 0 deletions frontend/gostarkme-web/app/app/fund/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client';

import hex2ascii from "@/app/utils";
import Fund from "@/components/modules/Fund/Fund";
import Bounded from "@/components/ui/Bounded";
import Divider from "@/components/ui/Divider";
import { FUND_MANAGER_ADDR } from "@/constants";
import { fundAbi } from "@/contracts/abis/fund";
import { fundManager } from "@/contracts/abis/fundManager";
import { walletStarknetkitLatestAtom } from "@/state/connectedWallet";
import { useAtomValue } from "jotai";
import { useEffect, useState } from "react";
import { Contract } from "starknet";

const FundDetailsPage = () => {

return (
<>
<Bounded className="px-60 text-lg">
<Fund></Fund>
</Bounded>
</>
);
};

export default FundDetailsPage;
36 changes: 21 additions & 15 deletions frontend/gostarkme-web/app/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import FundCards from "@/components/dashboard/fundCard";
import Footer from "@/components/ui/Footer";
import Navbar from "@/components/ui/Navbar";
import { FUND_MANAGER_ADDR } from "@/constants";
import { fund } from "@/contracts/abis/fund";
import { fundAbi } from "@/contracts/abis/fund";
import { fundManager } from "@/contracts/abis/fundManager";
import { walletStarknetkitLatestAtom } from "@/state/connectedWallet";
import { useAtomValue } from "jotai";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import React, { useEffect, useState } from "react";
import { byteArray, Contract, InvokeFunctionResponse } from "starknet";
import { navItems } from "@/constants";
import hex2ascii from "../utils";

const Dashboard = () => {

Expand All @@ -19,13 +20,7 @@ const Dashboard = () => {

const [funds, setFunds] = useState<any>([]);

function hex2ascii(hexx: string) {
var hex = hexx.toString();//force conversion
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
const [loading, setLoading] = useState(true);

async function getFunds() {
const id = await fundManagerContract.getCurrentId();
Expand All @@ -34,20 +29,23 @@ const Dashboard = () => {
// GET FUND ADDRESS
let fundaddr = await fundManagerContract.getFund(i);
fundaddr = "0x" + fundaddr.toString(16);
const fundContract = new Contract(fund, fundaddr, wallet?.account);
const fundContract = new Contract(fundAbi, fundaddr, wallet?.account);
// GET FUND NAME
let name = await fundContract.getName();
name = hex2ascii(name.toString(16));
// GET FUND DESCRIPTION
let desc = await fundContract.getReason();
// GET FUND ID
let fund_id = await fundContract.getId();
fundings.push({
type: "Project",
title: name,
description: desc,
fund_id: fund_id.toString(),
});
}
console.log(fundings);
setFunds(fundings);
setLoading(false);
}

useEffect(() => {
Expand All @@ -70,19 +68,27 @@ const Dashboard = () => {
Latest Funds
<span className="ml-2 text-yellow-400">&#x2728;</span>
</h1>
{funds.length !== 0 ? (

{loading && <div className="text-center text-gray-500">
Loading funds ...
</div>}

{funds.length !== 0 && !loading &&
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-6 md:gap-x-[138px] md:gap-y-[84px]">
{funds.map((fund: { type: string; title: string; description: string; }, index: number) => (
{funds.map((fund: { type: string; title: string; description: string; fund_id: string }, index: number) => (
<FundCards key={index} fund={fund} index={index} />
))}
</div>
) : (
}

{funds.length === 0 && !loading &&
<div className="flex justify-center items-center h-64">
<div className="text-center text-gray-500">
There is no fundings to display.
</div>
</div>
)}
}

<Footer></Footer>
</div>
);
Expand Down
11 changes: 11 additions & 0 deletions frontend/gostarkme-web/app/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function hex2ascii(hexx: string) {
var hex = hexx.toString();//force conversion
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}

export function calculatePorcentage(qty: number, goal: number): number {
return (Number(qty) / Number(goal)) * 100;
}
55 changes: 34 additions & 21 deletions frontend/gostarkme-web/components/dashboard/fundCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,56 @@

import { StardustAnimation } from "@/animations/StardustAnimation";
import useComponentSize from "@/hooks/useComponentSize.hook";
import { clickedFundState } from "@/state/nFunds";
import { useSetAtom } from "jotai";
import Link from "next/link";
import React from "react";

interface FundCardProps {
fund: {
type: string;
title: string;
description: string;
fund_id: string
};
index: number;
}

const FundCards = ({ fund, index }: FundCardProps) => {
const [ref, width, height] = useComponentSize();

const setClickedFund = useSetAtom(clickedFundState);

function handleNav() {
setClickedFund(Number(fund.fund_id));
}

return (
<div className="relative" ref={ref}>
<div
key={index}
className="min-w-[30rem] bg-gray-950 shadow-[0px_4px_4px_0px_#00000040] text-white rounded-[10px] py-[32px] md:py-[48px] md:px-[48px] lg:py-[64px] lg:px-[72px] gap-8 md:gap-10 lg:gap-14 flex flex-col items-start justify-between"
>
<div className="flex flex-col items-start justify-between gap-4 md:gap-6">
<p className=" text-sm md:text-base lg:text-lg text-white font-light leading-[22px] md:leading-[25px] lg:leading-[27.6px]">
{fund.type} {fund.type === "Project" ? <span>&#x1f680;</span> : <span>&#x1FAC0;</span>}
</p>
<h1 className="text-lg md:text-lg lg:text-[30px] font-bold">
{fund.title}
</h1>
</div>
<div>
{fund.description !== " " ? (
<p className="text-lg md:text-lg lg:text-[25px] text-white">{fund.description}</p>
) :
(
<p className="text-lg md:text-lg lg:text-[25px] text-white">No description provided</p>
)}
<Link onClick={handleNav} href={"/app/fund"}>
<div
key={index}
className="min-w-[30rem] bg-gray-950 shadow-[0px_4px_4px_0px_#00000040] text-white rounded-[10px] py-[32px] md:py-[48px] md:px-[48px] lg:py-[64px] lg:px-[72px] gap-8 md:gap-10 lg:gap-14 flex flex-col items-start justify-between"
>
<div className="flex flex-col items-start justify-between gap-4 md:gap-6">
<p className=" text-sm md:text-base lg:text-lg text-white font-light leading-[22px] md:leading-[25px] lg:leading-[27.6px]">
{fund.type} {fund.type === "Project" ? <span>&#x1f680;</span> : <span>&#x1FAC0;</span>}
</p>
<h1 className="text-lg md:text-lg lg:text-[30px] font-bold">
{fund.title}
</h1>
</div>
<div>
{fund.description !== " " ? (
<p className="text-lg md:text-lg lg:text-[25px] text-white">{fund.description}</p>
) :
(
<p className="text-lg md:text-lg lg:text-[25px] text-white">No description provided</p>
)}
</div>
<StardustAnimation height={height} width={width} />
</div>
<StardustAnimation height={height} width={width} />
</div>
</Link>
</div>
);
};
Expand Down
64 changes: 54 additions & 10 deletions frontend/gostarkme-web/components/modules/Fund/Fund.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,65 @@
import FundDonate from "./FundDonate";
import starknetlogo from "@/public/icons/starklogo.png";
import { FundVote } from "./FundVote";
import { useState } from "react";
import { useEffect, useState } from "react";
import { FUND_MANAGER_ADDR, upVotesNeeded } from "@/constants";
import Divider from "@/components/ui/Divider";
import hex2ascii from "@/app/utils";
import { fundAbi } from "@/contracts/abis/fund";
import { fundManager } from "@/contracts/abis/fundManager";
import { walletStarknetkitLatestAtom } from "@/state/connectedWallet";
import { useAtomValue } from "jotai";
import { Contract } from "starknet";
import { clickedFundState } from "@/state/nFunds";

interface FundProps {
message: string;
}
const Fund = () => {

const Fund = ({ message }: FundProps) => {
const [type, setType] = useState<string>("donate");
const wallet = useAtomValue(walletStarknetkitLatestAtom);

const [fundManagerContract, _setFundManagerContract] = useState<Contract>(new Contract(fundManager, FUND_MANAGER_ADDR, wallet?.account));

const [fund, setFund] = useState<any>({});

const clickedFund = useAtomValue(clickedFundState);

async function getDetails() {
let addr = await fundManagerContract.getFund(clickedFund);
addr = "0x" + addr.toString(16);
const fundContract = new Contract(fundAbi, addr, wallet?.account);

// GET FUND NAME
let name = await fundContract.getName();
name = hex2ascii(name.toString(16));
// GET FUND DESCRIPTION
let desc = await fundContract.getReason();
if (desc == " ") {
desc = "No description provided";
}
let state = await fundContract.getState();

let currentBalance = await fundContract.getCurrentGoalState();

let goal = await fundContract.getGoal();

let upVotes = await fundContract.getUpVotes();

setFund({ name: name, desc: desc, state: state, currentBalance: currentBalance, goal: goal, upVotes: upVotes, addr: addr });
}

useEffect(() => {
getDetails();
}, []);

return (
<section>
<p className="mb-40">{message}</p>

{type === "donate" ? <FundDonate icon={starknetlogo} /> : <FundVote />}
{/* For Vote, there is no logo, but when you already have it, just pass it through the prop */}
<h2 className="font-bold">{fund.name}</h2>
<Divider />
<p className="mb-40">{fund.desc}</p>
{ Number(fund.state) === 0 && <p>Fund is currently innactive.</p>}
{ Number(fund.state) === 1 && <FundVote upVotes={fund.upVotes} upVotesNeeded={upVotesNeeded} addr={fund.addr}/>}
{ Number(fund.state) === 2 && <FundDonate icon={starknetlogo} />}
{ Number(fund.state) === 3 && <p>Fund is currently closed.</p>}
{ Number(fund.state) === 4 && <p>Fund was already withdrawed.</p>}
</section>
);
};
Expand Down
36 changes: 29 additions & 7 deletions frontend/gostarkme-web/components/modules/Fund/FundVote.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
import { calculatePorcentage } from "@/app/utils";
import { Button } from "@/components/ui/Button";
import { LinkButton } from "@/components/ui/LinkButton";
import ProgressBar from "@/components/ui/ProgressBar";
import Image, { StaticImageData } from "next/image";
import { fundAbi } from "@/contracts/abis/fund";
import { walletStarknetkitLatestAtom } from "@/state/connectedWallet";
import { useAtomValue } from "jotai";
import { useState } from "react";
import { Contract, wallet, InvokeFunctionResponse } from "starknet";

interface FundVoteProps {
icon?: StaticImageData;
upVotes: number,
upVotesNeeded: number,
addr: string,
}

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

const wallet = useAtomValue(walletStarknetkitLatestAtom);

const progress = calculatePorcentage(upVotes, upVotesNeeded);

function vote() {
const fundContract = new Contract(fundAbi, addr, wallet?.account);
const myCall = fundContract.populate("receiveVote", []);
wallet?.account?.execute(myCall)
.then(async (resp: InvokeFunctionResponse) => {
console.log("increaseBalance txH =", resp.transaction_hash);
})
.catch((e: any) => { console.log("error increase balance =", e) });
}

return (
<div className="flex flex-col">
<ProgressBar progress={34} />
<ProgressBar progress={progress} />
<div className="flex justify-center my-2">
<p className="text-center mx-2">200 / 300 </p>
{/* <Image src={icon || ""} alt="icon" width={24} height={24} /> */}
<p className="text-center mx-2">{upVotes.toString()} / {upVotesNeeded.toString()} </p>
<p>&#127775;</p>
</div>
<LinkButton label="Vote" href="/" />
<Button label="Vote" onClick={vote}></Button>
</div>
);
};
2 changes: 2 additions & 0 deletions frontend/gostarkme-web/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ export const navItems = [
{ label: 'My Profile', href: '/app/myprofile' },
{ label: 'My funds', href: '/app/myfunds' }
];

export const upVotesNeeded = 100;
2 changes: 1 addition & 1 deletion frontend/gostarkme-web/contracts/abis/fund.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const fund = [
export const fundAbi = [
{
"type": "impl",
"name": "FundImpl",
Expand Down
5 changes: 5 additions & 0 deletions frontend/gostarkme-web/state/nFunds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { atomWithReset } from "jotai/utils"

export const clickedFundState = atomWithReset<
Number | null | undefined
>(undefined)
Loading