From a309f15a0c936e53a181641859f91c84f7f1d53a Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Mon, 13 May 2024 15:57:27 +0400 Subject: [PATCH 01/40] handle zero applications --- src/pages/Pot/NavPages/Projects/Projects.tsx | 116 +++++++++++-------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/src/pages/Pot/NavPages/Projects/Projects.tsx b/src/pages/Pot/NavPages/Projects/Projects.tsx index fb780080..d0a275a5 100644 --- a/src/pages/Pot/NavPages/Projects/Projects.tsx +++ b/src/pages/Pot/NavPages/Projects/Projects.tsx @@ -1,4 +1,4 @@ -import { useState, Social, context, useParams, createDebounce } from "alem"; +import { useState, Social, context, useParams, createDebounce, useEffect } from "alem"; import PotSDK from "@app/SDK/pot"; import Card from "@app/components/Card/Card"; import ListSection from "@app/pages/Projects/components/ListSection"; @@ -15,7 +15,7 @@ type Props = { const Projects = (props: Props) => { const [filteredProjects, setFilteredProjects] = useState([]); const [projects, setProjects] = useState(null); - const [flaggedAddresses, setFlaggedAddresses] = useState(null); + const [flaggedAddresses, setFlaggedAddresses] = useState(null); const [payouts, setPayouts] = useState(null); // get projects @@ -24,10 +24,16 @@ const Projects = (props: Props) => { const { potDetail, allDonations } = props; if (!projects) { - PotSDK.asyncGetApprovedApplications(potId).then((projects: any) => { - setProjects(projects); - setFilteredProjects(projects); - }); + PotSDK.asyncGetApprovedApplications(potId) + .then((projects: any) => { + setProjects(projects); + setFilteredProjects(projects); + }) + .catch((err: any) => { + console.log("error fetching projects ", err); + setProjects([]); + setFilteredProjects([]); + }); } const Loading = () => ( @@ -56,24 +62,28 @@ const Projects = (props: Props) => { .catch((err) => console.log("error getting the flagged accounts ", err)); } - if (!payouts) { - if (allDonations.length && flaggedAddresses) - calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses) - .then((payouts: any) => { - setPayouts(payouts ?? []); - }) - .catch((err) => { - console.log("error while calculating payouts ", err); - setPayouts([]); - }); - } + useEffect(() => { + if (!payouts) { + if (allDonations.length && flaggedAddresses) + calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses) + .then((payouts: any) => { + setPayouts(payouts ?? []); + }) + .catch((err) => { + console.log("error while calculating payouts ", err); + setPayouts([]); + }); + else if (allDonations.length === 0 && flaggedAddresses?.length === 0) { + setPayouts([]); + } + } + }, [allDonations, flaggedAddresses]); if (!flaggedAddresses || !payouts) return ; const searchByWords = (searchTerm: string) => { if (projects.length) { searchTerm = searchTerm.toLowerCase().trim(); - // setSearchTerm(searchTerm); const updatedProjects = projects.filter((project: any) => { const profile: any = Social.getr(`${project.project_id}/profile`); const fields = [ @@ -113,39 +123,43 @@ const Projects = (props: Props) => { className="search-input" /> - { - return ( - - ); - }} - /> + {filteredProjects.length > 0 ? ( + { + return ( + + ); + }} + /> + ) : ( +
No projects
+ )} ); }; From cc1d9461afaf6d8061cca541eb42a18ff1180810 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Mon, 13 May 2024 16:53:31 +0400 Subject: [PATCH 02/40] fix applications image --- src/pages/Pot/NavPages/Applications/Applications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Pot/NavPages/Applications/Applications.tsx b/src/pages/Pot/NavPages/Applications/Applications.tsx index 3eeb72ac..b1b8a5ad 100644 --- a/src/pages/Pot/NavPages/Applications/Applications.tsx +++ b/src/pages/Pot/NavPages/Applications/Applications.tsx @@ -197,7 +197,7 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => {
- + {profile?.name &&
{_address(profile?.name, 10)}
} {project_id}}> From 4d293878e913145794af2dfff9277de8987758b6 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Tue, 14 May 2024 10:41:23 +0400 Subject: [PATCH 03/40] fix apllication applyement condition --- src/pages/Pot/components/Header/Header.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/Pot/components/Header/Header.tsx b/src/pages/Pot/components/Header/Header.tsx index b580f0ad..5a39de23 100644 --- a/src/pages/Pot/components/Header/Header.tsx +++ b/src/pages/Pot/components/Header/Header.tsx @@ -30,6 +30,7 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation application_end_ms, cooldown_end_ms: _cooldown_end_ms, all_paid_out, + registry_provider, } = potDetail; const { IPFS_BASE_URL, NADA_BOT_URL } = constants; @@ -152,6 +153,8 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation const registrationApproved = registryStatus === "Approved"; + const registrationApprovedOrNoRegistryProvider = registrationApproved || !registry_provider; + return ( @@ -191,12 +194,12 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation )} {canApply && ( )} {now > public_round_end_ms && now < cooldown_end_ms && ( From 842c6d88ce0915f9905869a948e759eefd4ca768 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Tue, 14 May 2024 17:32:29 +0400 Subject: [PATCH 04/40] fix flag action logic --- .../Pot/components/DonationsTable/DonationsTable.tsx | 12 +----------- src/pages/Pot/components/FlagModal/FlagModal.tsx | 6 ++++-- .../components/FlagSuccessModal/FlagSuccessModal.tsx | 3 +-- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/pages/Pot/components/DonationsTable/DonationsTable.tsx b/src/pages/Pot/components/DonationsTable/DonationsTable.tsx index 8df5c882..a88ff86e 100644 --- a/src/pages/Pot/components/DonationsTable/DonationsTable.tsx +++ b/src/pages/Pot/components/DonationsTable/DonationsTable.tsx @@ -106,17 +106,7 @@ const DonationsTable = (props: any) => {
-
{ - setSuccessFlag({ - address: "re.near", - reason: "test tEST Tetset", - }); - }} - > - Donor -
+
Donor
Project
sortDonation("price")}> Amount diff --git a/src/pages/Pot/components/FlagModal/FlagModal.tsx b/src/pages/Pot/components/FlagModal/FlagModal.tsx index c8d4dfa0..e3b5851d 100644 --- a/src/pages/Pot/components/FlagModal/FlagModal.tsx +++ b/src/pages/Pot/components/FlagModal/FlagModal.tsx @@ -1,4 +1,4 @@ -import { Big, Near, context, useState } from "alem"; +import { Big, Near, context, useParams, useState } from "alem"; import BannerBg from "@app/assets/svgs/banner-bg"; import Button from "@app/components/Button"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; @@ -10,10 +10,12 @@ const FlagModal = (props: any) => { const SOCIAL_CONTRACT_ID = "social.near"; const accountId = context.accountId || ""; + const { potId } = useParams(); + const [reason, setReason] = useState(""); const [reasonErr, setReasonErr] = useState(""); - const { onClose, flagAddress, potId, setSuccessFlag } = props; + const { onClose, flagAddress, setSuccessFlag } = props; const onCancel = () => { onClose(); diff --git a/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx b/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx index 0b1d99f4..dce3f7a8 100644 --- a/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx +++ b/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx @@ -1,4 +1,3 @@ -import React from "react"; import ModalOverlay from "@app/modals/ModalOverlay"; import { Container } from "./styles"; @@ -15,7 +14,7 @@ const FlagSuccessModal = (props: any) => { />
- {successFlag.address} has been flagged + {successFlag.account} has been flagged
{successFlag.reason}
From 4260682bcddbcc7a060ad0c95b3de07a542fec81 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Tue, 14 May 2024 18:52:53 +0400 Subject: [PATCH 05/40] flag modal update --- src/hooks/useModals.tsx | 2 +- src/modals/ModalOverlay.tsx | 4 +- .../DonationsTable/DonationsTable.tsx | 57 ++++++++++++++----- .../Pot/components/FlagModal/FlagModal.tsx | 12 ++-- .../FlagSuccessModal/FlagSuccessModal.tsx | 3 +- src/utils/calculatePayouts.ts | 22 +++---- src/utils/getTransactionsFromHashes.ts | 23 ++++++++ 7 files changed, 87 insertions(+), 36 deletions(-) create mode 100644 src/utils/getTransactionsFromHashes.ts diff --git a/src/hooks/useModals.tsx b/src/hooks/useModals.tsx index 8081051a..c71bc90e 100644 --- a/src/hooks/useModals.tsx +++ b/src/hooks/useModals.tsx @@ -17,7 +17,7 @@ const useModals = () => { return () => ( <> - {(successfulDonation || _transactionHashes) && } + {/* {(successfulDonation || _transactionHashes) && } */} {donationModalProps && } ); diff --git a/src/modals/ModalOverlay.tsx b/src/modals/ModalOverlay.tsx index be9494eb..48859eaa 100644 --- a/src/modals/ModalOverlay.tsx +++ b/src/modals/ModalOverlay.tsx @@ -3,8 +3,8 @@ import styled from "styled-components"; type Props = { children: JSX.Element | JSX.Element[]; onOverlayClick?: (event: any) => void; - contentStyle?: any; - overlayStyle?: any; + contentStyle?: React.CSSProperties; + overlayStyle?: React.CSSProperties; }; const ModalOverlay = ({ children, onOverlayClick, contentStyle }: Props) => { diff --git a/src/pages/Pot/components/DonationsTable/DonationsTable.tsx b/src/pages/Pot/components/DonationsTable/DonationsTable.tsx index 8df5c882..719b7736 100644 --- a/src/pages/Pot/components/DonationsTable/DonationsTable.tsx +++ b/src/pages/Pot/components/DonationsTable/DonationsTable.tsx @@ -6,17 +6,23 @@ import ProfileImage from "@app/components/mob.near/ProfileImage"; import _address from "@app/utils/_address"; import calcNetDonationAmount from "@app/utils/calcNetDonationAmount"; import getTimePassed from "@app/utils/getTimePassed"; +import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; import hrefWithParams from "@app/utils/hrefWithParams"; import FlagModal from "../FlagModal/FlagModal"; import FlagSuccessModal from "../FlagSuccessModal/FlagSuccessModal"; import FlagBtn from "./FlagBtn"; import { Container, FlagTooltipWrapper, SearchBar, SearchBarContainer, SearchIcon, TrRow } from "./styles"; +type FlagSuccess = { + account: string; + reason: string; +}; + const DonationsTable = (props: any) => { const accountId = context.accountId; const { filteredDonations, filter, handleSearch, sortDonation, currentFilter, potDetail } = props; - const { potId } = useParams(); + const { potId, transactionHashes } = useParams(); const { admins, owner, chef, all_paid_out } = potDetail; @@ -24,7 +30,7 @@ const DonationsTable = (props: any) => { const [currentPage, setCurrentPage] = useState(1); const [flagAddress, setFlagAddress] = useState(null); - const [successFlag, setSuccessFlag] = useState(null); + const [successFlag, setSuccessFlag] = useState(null); const [updateFlaggedAddresses, setUpdateFlaggedAddresses] = useState(false); const [flaggedAddresses, setFlaggedAddresses] = useState([]); const perPage = 30; @@ -42,6 +48,37 @@ const DonationsTable = (props: any) => { const potAdmins = [owner, chef, ...admins]; const hasAuthority = potAdmins.includes(accountId) && !all_paid_out; + // Handle flag success for extention wallet + useEffect(() => { + if (accountId) { + getTransactionsFromHashes(transactionHashes, accountId).then((trxs) => { + const transaction = trxs[0].body.result.transaction; + + const methodName = transaction.actions[0].FunctionCall.method_name; + const signer_id = transaction.signer_id; + const receiver_id = transaction.receiver_id; + + const { data } = JSON.parse(Buffer.from(transaction.actions[0].FunctionCall.args, "base64").toString("utf-8")); + + if (methodName === "set" && receiver_id === "social.near" && data) { + try { + const pLBlacklistedAccounts = JSON.parse(data[signer_id].profile.pLBlacklistedAccounts); + const pLBlacklistedAccountsForPot = pLBlacklistedAccounts[potId]; + const allPotFlaggedAccounts = Object.keys(pLBlacklistedAccountsForPot); + const account = allPotFlaggedAccounts[allPotFlaggedAccounts.length - 1]; + const reason = pLBlacklistedAccountsForPot[account]; + setSuccessFlag({ + account, + reason, + }); + } catch (err) { + console.log("error parsing flag transaction ", err); + } + } + }); + } + }, []); + const checkIfIsFlagged = (address: string) => flaggedAddresses.find((obj: any) => obj.potFlaggedAcc[address]); const FlagTooltip = ({ flag, href, address }: any) => ( @@ -106,17 +143,7 @@ const DonationsTable = (props: any) => {
-
{ - setSuccessFlag({ - address: "re.near", - reason: "test tEST Tetset", - }); - }} - > - Donor -
+
Donor
Project
sortDonation("price")}> Amount @@ -202,7 +229,7 @@ const DonationsTable = (props: any) => { perPage: perPage, }} /> - {flagAddress != null && ( + {flagAddress && ( { }} /> )} - {successFlag != null && ( + {accountId && successFlag && ( { const SOCIAL_CONTRACT_ID = "social.near"; const accountId = context.accountId || ""; + const { potId } = useParams(); + const [reason, setReason] = useState(""); const [reasonErr, setReasonErr] = useState(""); - const { onClose, flagAddress, potId, setSuccessFlag } = props; + const { onClose, flagAddress, setSuccessFlag } = props; const onCancel = () => { onClose(); @@ -93,7 +95,7 @@ const FlagModal = (props: any) => { }; return ( - +
@@ -156,9 +158,9 @@ const FlagModal = (props: any) => {
Flagging this account will remove their donations when calculating payouts for this pot
- + diff --git a/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx b/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx index 0b1d99f4..dce3f7a8 100644 --- a/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx +++ b/src/pages/Pot/components/FlagSuccessModal/FlagSuccessModal.tsx @@ -1,4 +1,3 @@ -import React from "react"; import ModalOverlay from "@app/modals/ModalOverlay"; import { Container } from "./styles"; @@ -15,7 +14,7 @@ const FlagSuccessModal = (props: any) => { />
- {successFlag.address} has been flagged + {successFlag.account} has been flagged
{successFlag.reason}
diff --git a/src/utils/calculatePayouts.ts b/src/utils/calculatePayouts.ts index 209641ac..73b38fa8 100644 --- a/src/utils/calculatePayouts.ts +++ b/src/utils/calculatePayouts.ts @@ -6,8 +6,8 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis // * QF/CLR logic taken from https://github.com/gitcoinco/quadratic-funding/blob/master/quadratic-funding/clr.py * return new Promise((resolve, reject) => { - console.log("Calculting payouts; ignoring blacklisted donors &/or projects: ", blacklistedAccounts.join(", ")); - console.log("totalMatchingPool: ", totalMatchingPool); + // console.log("Calculting payouts; ignoring blacklisted donors &/or projects: ", blacklistedAccounts.join(", ")); + // console.log("totalMatchingPool: ", totalMatchingPool); // first, flatten the list of donations into a list of contributions const projectContributions: any = []; const allDonors = new Set(); @@ -50,7 +50,7 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis console.error("error fetching human scores. Continuing anyway: ", e); }) .finally(() => { - console.log("human scores: ", humanScores); + // console.log("human scores: ", humanScores); // take the flattened list of contributions and aggregate // the amounts contributed by each user to each project. // create a dictionary where each key is a projectId and its value @@ -59,7 +59,7 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis const contributions: any = {}; for (const [proj, user, amount] of projectContributions) { if (!humanScores[user] || !humanScores[user].is_human) { - console.log("skipping non-human: ", user); + // console.log("skipping non-human: ", user); continue; } if (!contributions[proj]) { @@ -67,7 +67,7 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis } contributions[proj][user] = Big(contributions[proj][user] || 0).plus(amount); } - console.log("contributions: ", contributions); + // console.log("contributions: ", contributions); // calculate the total overlapping contribution amounts between pairs of users for each project. // create a nested dictionary where the outer keys are userIds and the inner keys are also userIds, // and the inner values are the total overlap between these two users' contributions. @@ -119,10 +119,10 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis matching_amount_str: tot.toFixed(0), }); } - console.log("totals before: ", totals); + // console.log("totals before: ", totals); // if we reach saturation, we need to normalize if (bigtot.gte(totalPot)) { - console.log("NORMALIZING"); + // console.log("NORMALIZING"); for (const t of totals) { t.matching_amount_str = Big(t.matching_amount_str).div(bigtot).times(totalPot).toFixed(0); } @@ -136,7 +136,7 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis } let residual = totalPot.minus(totalAllocatedBeforeRounding); - console.log("first round residual: ", residual.toFixed(0)); + // console.log("first round residual: ", residual.toFixed(0)); // Check if there is a residual due to rounding if (residual.abs().gt(Big("0"))) { @@ -150,14 +150,14 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis totals[i].matching_amount_str = Big(totals[i].matching_amount_str).plus(additionalAllocation).toFixed(0); } - console.log("CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION"); + // console.log("CALCULATING TOTALS AFTER RESIDUAL DISTRIBUTION"); totalAllocatedBeforeRounding = Big(0); // Initialize the accumulator as a Big object for (const t of totals) { const currentMatchingAmount = Big(t.matching_amount_str); totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); - console.log("second round residual: ", residual.toFixed(0)); + // console.log("second round residual: ", residual.toFixed(0)); // OLD RESIDUAL ADJUSTMENT LOGIC // if (residual.abs().gt(Big("0"))) { @@ -215,7 +215,7 @@ const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklis totalAllocatedBeforeRounding = totalAllocatedBeforeRounding.plus(currentMatchingAmount); } residual = totalPot.minus(totalAllocatedBeforeRounding); - console.log("Residual after final adjustment: ", residual.toFixed(0)); + // console.log("Residual after final adjustment: ", residual.toFixed(0)); } } diff --git a/src/utils/getTransactionsFromHashes.ts b/src/utils/getTransactionsFromHashes.ts new file mode 100644 index 00000000..73994c68 --- /dev/null +++ b/src/utils/getTransactionsFromHashes.ts @@ -0,0 +1,23 @@ +import { asyncFetch } from "alem"; + +export default function getTransactionsFromHashes(transactionHashes: string, accountId: string) { + const transactionHashesList = transactionHashes.split(","); + + const transactions = transactionHashesList.map((transaction) => { + const body = JSON.stringify({ + jsonrpc: "2.0", + id: "dontcare", + method: "tx", + params: [transaction, accountId], + }); + return asyncFetch("https://rpc.mainnet.near.org", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body, + }); + }); + + return Promise.all(transactions); +} From e910181222772ccbfc640c212cf488706ec69700 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Tue, 14 May 2024 18:53:15 +0400 Subject: [PATCH 06/40] fix profile github duplicate issue --- src/pages/CreateProject/utils/helpers.ts | 2 +- src/pages/Pot/components/FlagModal/styles.ts | 7 +------ src/pages/Project/NavPages/About/About.tsx | 4 +++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/pages/CreateProject/utils/helpers.ts b/src/pages/CreateProject/utils/helpers.ts index 12971d11..c1d6a3cf 100644 --- a/src/pages/CreateProject/utils/helpers.ts +++ b/src/pages/CreateProject/utils/helpers.ts @@ -65,7 +65,7 @@ export const projectDisabled = () => export function extractRepoPath(url: string) { if (url) { // Define a regular expression pattern to match GitHub repository URLs - const pattern = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^\/]+\/[^\/]+(?:\/.*)?)\/?$/; + const pattern = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^\/]+(?:\/[^\/]+)?)\/?$/; // Execute the regular expression on the URL const match = url.match(pattern); // If a match is found, return the extracted repository path; otherwise, return null diff --git a/src/pages/Pot/components/FlagModal/styles.ts b/src/pages/Pot/components/FlagModal/styles.ts index 6a0f1d28..034f8fd3 100644 --- a/src/pages/Pot/components/FlagModal/styles.ts +++ b/src/pages/Pot/components/FlagModal/styles.ts @@ -97,11 +97,6 @@ export const ButtonsWrapper = styled.div` gap: 1.5rem; grid-template-columns: repeat(2, 1fr); button { - font-weight: 500; - } - .cancel { - border: none; - background: none; - color: #dd3345; + width: 100%; } `; diff --git a/src/pages/Project/NavPages/About/About.tsx b/src/pages/Project/NavPages/About/About.tsx index a9a8e8cb..2349d698 100644 --- a/src/pages/Project/NavPages/About/About.tsx +++ b/src/pages/Project/NavPages/About/About.tsx @@ -30,7 +30,9 @@ const About = ({ projectId, accountId }: Props) => { }, []) : []; - const githubRepos = profile.plGithubRepos ? JSON.parse(profile.plGithubRepos) : []; + const githubRepos = (profile.plGithubRepos ? JSON.parse(profile.plGithubRepos) : []).map((url: string) => + url.replace("github.com/github.com/", "github.com/"), + ); function convertToGitHubURL(path: string) { const prefix = "https://github.com/"; From df7101590561b4c6a03c64c3e586b6875289bd25 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Tue, 14 May 2024 19:00:57 +0400 Subject: [PATCH 07/40] bring back successfulDonationModal --- src/hooks/useModals.tsx | 2 +- src/modals/ModalSuccess/ModalSuccess.tsx | 19 +++++++++++++------ .../DonationsTable/DonationsTable.tsx | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/hooks/useModals.tsx b/src/hooks/useModals.tsx index c71bc90e..8081051a 100644 --- a/src/hooks/useModals.tsx +++ b/src/hooks/useModals.tsx @@ -17,7 +17,7 @@ const useModals = () => { return () => ( <> - {/* {(successfulDonation || _transactionHashes) && } */} + {(successfulDonation || _transactionHashes) && } {donationModalProps && } ); diff --git a/src/modals/ModalSuccess/ModalSuccess.tsx b/src/modals/ModalSuccess/ModalSuccess.tsx index 2d6464d2..c284c79f 100644 --- a/src/modals/ModalSuccess/ModalSuccess.tsx +++ b/src/modals/ModalSuccess/ModalSuccess.tsx @@ -163,17 +163,20 @@ const ModalSuccess = () => { }, })); }) - .catch((err) => console.log(err)); + .catch((err) => { + console.log(err); + return ""; + }); } else { - onClose(); + return ""; } } else { - onClose(); + return ""; } }) .catch((err) => { console.log(err); - onClose(); + return ""; }); } } @@ -219,7 +222,7 @@ const ModalSuccess = () => { const needsToVerify = isUserHumanVerified === false; - return ( + return successfulDonation || successfulApplication ? ( <> {successfulApplication ? ( @@ -322,9 +325,13 @@ const ModalSuccess = () => { /> {needsToVerify && !successfulDonationVals[0]?.recipient_id && } - ) : null} + ) : ( + "" + )} + ) : ( + "" ); }; diff --git a/src/pages/Pot/components/DonationsTable/DonationsTable.tsx b/src/pages/Pot/components/DonationsTable/DonationsTable.tsx index 719b7736..6f5e70b5 100644 --- a/src/pages/Pot/components/DonationsTable/DonationsTable.tsx +++ b/src/pages/Pot/components/DonationsTable/DonationsTable.tsx @@ -50,7 +50,7 @@ const DonationsTable = (props: any) => { // Handle flag success for extention wallet useEffect(() => { - if (accountId) { + if (accountId && transactionHashes) { getTransactionsFromHashes(transactionHashes, accountId).then((trxs) => { const transaction = trxs[0].body.result.transaction; From 6b10aa920d85c7d5389ccc20b76ea37efa2c24d7 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 15 May 2024 13:10:55 +0400 Subject: [PATCH 08/40] Fund modal success --- package.json | 2 +- .../BreakdownSummary/BreakdownSummary.tsx | 7 +- .../Pot/components/FundModal/FundModal.tsx | 37 +++++- src/pages/Pot/components/Header/Header.tsx | 47 ++++++- .../PoolAllocationTable.tsx | 1 + .../SponsorsBoard/SponsorsBoard.tsx | 2 +- .../SuccessFundModal/SuccessFundModal.tsx | 125 ++++++++++++++++++ .../Pot/components/SuccessFundModal/styles.ts | 93 +++++++++++++ src/types.ts | 36 ++++- yarn.lock | 28 +++- 10 files changed, 354 insertions(+), 24 deletions(-) create mode 100644 src/pages/Pot/components/SuccessFundModal/SuccessFundModal.tsx create mode 100644 src/pages/Pot/components/SuccessFundModal/styles.ts diff --git a/package.json b/package.json index 7f985567..5642ffd4 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test": "npx playwright test" }, "dependencies": { - "alem": "1.1.2" + "alem": "1.1.3" }, "devDependencies": { "@playwright/test": "^1.38.1", diff --git a/src/components/Cart/BreakdownSummary/BreakdownSummary.tsx b/src/components/Cart/BreakdownSummary/BreakdownSummary.tsx index 16d0a9de..8f539314 100644 --- a/src/components/Cart/BreakdownSummary/BreakdownSummary.tsx +++ b/src/components/Cart/BreakdownSummary/BreakdownSummary.tsx @@ -1,7 +1,6 @@ import { State, props, state } from "alem"; import DonateSDK from "@app/SDK/donate"; import NearIcon from "@app/assets/svgs/near-icon"; -import constants from "@app/constants"; import basisPointsToPercent from "@app/utils/basisPointsToPercent"; import { BreakdownAmount, @@ -18,11 +17,10 @@ const BreakdownSummary = ({ referrerId, totalAmount, bypassProtocolFee, - recipientId, potRferralFeeBasisPoints, ftIcon, bypassChefFee, - chef, + label, chefFeeBasisPoints, }: any) => { const donationContractConfig = DonateSDK.getConfig(); @@ -36,6 +34,7 @@ const BreakdownSummary = ({ const { protocol_fee_basis_points } = donationContractConfig; const protocolFeeBasisPoints = props.protocolFeeBasisPoints ?? protocol_fee_basis_points; + const referralFeeBasisPoints = potRferralFeeBasisPoints || props.referralFeeBasisPoints; const TOTAL_BASIS_POINTS = 10_000; @@ -81,7 +80,7 @@ const BreakdownSummary = ({ show: true, }, { - label: "Project allocation", + label: `${label ?? "Project"} allocation`, percentage: projectAllocationPercent, amount: projectAllocationAmount, show: true, diff --git a/src/pages/Pot/components/FundModal/FundModal.tsx b/src/pages/Pot/components/FundModal/FundModal.tsx index 016f1113..ff3a0df7 100644 --- a/src/pages/Pot/components/FundModal/FundModal.tsx +++ b/src/pages/Pot/components/FundModal/FundModal.tsx @@ -1,4 +1,5 @@ import { State, state, useParams, Big, Social, Near, context } from "alem"; +import PotSDK from "@app/SDK/pot"; import Button from "@app/components/Button"; import CheckBox from "@app/components/Inputs/Checkbox/Checkbox"; import Text from "@app/components/Inputs/Text/Text"; @@ -6,21 +7,25 @@ import TextArea from "@app/components/Inputs/TextArea/TextArea"; import ProfileImage from "@app/components/mob.near/ProfileImage"; import constants from "@app/constants"; import ModalOverlay from "@app/modals/ModalOverlay"; -import { PotDetail } from "@app/types"; +import { PotDetail, PotDonation } from "@app/types"; import _address from "@app/utils/_address"; import doesUserHaveDaoFunctionCallProposalPermissions from "@app/utils/doesUserHaveDaoFunctionCallProposalPermissions"; import hrefWithParams from "@app/utils/hrefWithParams"; import yoctosToNear from "@app/utils/yoctosToNear"; +import { ExtendedFundDonation } from "../SuccessFundModal/SuccessFundModal"; import { FeeText, Label, ModalTitle, Row, TextBold, UserChipLink } from "./styles"; type Props = { potDetail: PotDetail; onClose: () => void; + setFundDonation: (fundDonation: ExtendedFundDonation) => void; }; -const FundModal = ({ potDetail, onClose }: Props) => { +const FundModal = ({ potDetail, onClose, setFundDonation }: Props) => { const { referrerId, potId } = useParams(); + const accountId = context.accountId; + const { MAX_DONATION_MESSAGE_LENGTH, SUPPORTED_FTS, ONE_TGAS } = constants; const { @@ -80,6 +85,29 @@ const FundModal = ({ potDetail, onClose }: Props) => { ? (matchingPoolDonationAmountNear * referral_fee_matching_pool_basis_points) / 10_000 || 0 : 0; + const handleSuccess = (afterTs: number) => { + const donateSuccess = setInterval(() => { + PotSDK.asyncGetDonationsForDonor(potId, accountId).then((donations: PotDonation[]) => { + const fundDonation = donations.find((donation) => donation.donated_at > afterTs && !donation.project_id); + if (fundDonation) { + setFundDonation({ + ...fundDonation, + potId, + potDetail, + }); + clearInterval(donateSuccess); + onClose(); + } + }); + }, 1000); + + // Clear the interval after 60 seconds + setTimeout(() => { + onClose(); + clearInterval(donateSuccess); + }, 60000); + }; + const handleMatchingPoolDonation = () => { const args: any = { message: matchingPoolDonationMessage, @@ -139,8 +167,7 @@ const FundModal = ({ potDetail, onClose }: Props) => { } Near.call(transactions); - // NB: we won't get here if user used a web wallet, as it will redirect to the wallet - // <---- EXTENSION WALLET HANDLING ----> // TODO: implement + handleSuccess(Date.now()); }; const disabled = @@ -179,7 +206,7 @@ const FundModal = ({ potDetail, onClose }: Props) => { if (!policy) { State.update({ daoAddressError: "Invalid DAO address" }); } - if (!doesUserHaveDaoFunctionCallProposalPermissions(context.accountId || "", policy)) { + if (!doesUserHaveDaoFunctionCallProposalPermissions(accountId || "", policy)) { State.update({ daoAddressError: "Your account does not have permission to create proposals", }); diff --git a/src/pages/Pot/components/Header/Header.tsx b/src/pages/Pot/components/Header/Header.tsx index 5a39de23..146ddd5b 100644 --- a/src/pages/Pot/components/Header/Header.tsx +++ b/src/pages/Pot/components/Header/Header.tsx @@ -7,6 +7,7 @@ import useModals from "@app/hooks/useModals"; import CopyIcon from "@app/pages/Project/components/CopyIcon"; import { PotDetail } from "@app/types"; import calculatePayouts from "@app/utils/calculatePayouts"; +import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; import nearToUsd from "@app/utils/nearToUsd"; import yoctosToNear from "@app/utils/yoctosToNear"; import yoctosToUsdWithFallback from "@app/utils/yoctosToUsdWithFallback"; @@ -14,6 +15,7 @@ import ChallengeModal from "../ChallengeModal/ChallengeModal"; import FundModal from "../FundModal/FundModal"; import NewApplicationModal from "../NewApplicationModal/NewApplicationModal"; import PoolAllocationTable from "../PoolAllocationTable/PoolAllocationTable"; +import SuccessFundModal, { ExtendedFundDonation } from "../SuccessFundModal/SuccessFundModal"; import { ButtonsWrapper, Container, Description, Fund, HeaderWrapper, Referral, Title } from "./styles"; const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonations: any }) => { @@ -35,7 +37,7 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation const { IPFS_BASE_URL, NADA_BOT_URL } = constants; - const { potId } = useParams(); + const { potId, transactionHashes } = useParams(); // Start Modals provider const Modals = useModals(); @@ -53,6 +55,8 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation const [isDao, setIsDao] = useState(null); const [applicationSuccess, setApplicationSuccess] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); + // set fund mathcing pool success + const [fundDonation, setFundDonation] = useState(null); const verifyIsOnRegistry = (address: any) => { Near.asyncView("lists.potlock.near", "get_registrations_for_registrant", { @@ -73,6 +77,28 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation } }, []); + // Handle fund success for web wallet + useEffect(() => { + if (accountId && transactionHashes) { + getTransactionsFromHashes(transactionHashes, accountId).then((trxs) => { + const transaction = trxs[0].body.result.transaction; + + const methodName = transaction.actions[0].FunctionCall.method_name; + const receiver_id = transaction.receiver_id; + const successVal = trxs[0].body.result.status?.SuccessValue; + const result = JSON.parse(Buffer.from(successVal, "base64").toString("utf-8")); // atob not working + + if (methodName === "donate" && receiver_id === potId && result) { + setFundDonation({ + ...result, + potId, + potDetail, + }); + } + }); + } + }, []); + const projectNotRegistered = registryStatus === null; const userIsAdminOrGreater = admins.includes(accountId) || owner === accountId; const userIsChefOrGreater = userIsAdminOrGreater || chef === accountId; @@ -229,18 +255,29 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation potDetail={potDetail} /> )} + {showChallengePayoutsModal && ( + setShowChallengePayoutsModal(false)} + /> + )} + {/* Fund Matching Pool Modal */} {isMatchingPoolModalOpen && ( { setIsMatchingPoolModalOpen(false); }} /> )} - {showChallengePayoutsModal && ( - setShowChallengePayoutsModal(false)} + {/* Fund Matching Pool Success Modal */} + {fundDonation && ( + { + setFundDonation(null); + }} /> )} diff --git a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx index 2fe15c59..f6692ef6 100644 --- a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx +++ b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx @@ -31,6 +31,7 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { } let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); + if (sponsorshipDonations) sponsorshipDonations.sort((a: any, b: any) => b.net_amount - a.net_amount); const calcMatchedAmount = (donations: any) => { diff --git a/src/pages/Pot/components/SponsorsBoard/SponsorsBoard.tsx b/src/pages/Pot/components/SponsorsBoard/SponsorsBoard.tsx index fa3617d6..eac88d19 100644 --- a/src/pages/Pot/components/SponsorsBoard/SponsorsBoard.tsx +++ b/src/pages/Pot/components/SponsorsBoard/SponsorsBoard.tsx @@ -13,7 +13,7 @@ const Sponsor = ({ donation: { amount, donor_id, percentage_share }, colIdx }: a return (
- + {_address(profile.name || donor_id, 15)} diff --git a/src/pages/Pot/components/SuccessFundModal/SuccessFundModal.tsx b/src/pages/Pot/components/SuccessFundModal/SuccessFundModal.tsx new file mode 100644 index 00000000..f0dcb23b --- /dev/null +++ b/src/pages/Pot/components/SuccessFundModal/SuccessFundModal.tsx @@ -0,0 +1,125 @@ +import { Social, context, useMemo } from "alem"; +import BreakdownSummary from "@app/components/Cart/BreakdownSummary/BreakdownSummary"; +import constants from "@app/constants"; +import ModalOverlay from "@app/modals/ModalOverlay"; +import { PotDetail, PotDonation } from "@app/types"; +import formatWithCommas from "@app/utils/formatWithCommas"; +import hrefWithParams from "@app/utils/hrefWithParams"; +import yoctosToUsd from "@app/utils/yoctosToUsd"; +import { + Amount, + AmountUsd, + HeaderIcon, + ModalHeader, + ModalMain, + ModalMiddel, + ProjectName, + TwitterShare, +} from "./styles"; + +export type ExtendedFundDonation = PotDonation & { + potId: string; + potDetail: PotDetail; +}; + +const SuccessFundModal = ({ fundDonation, onClose }: { fundDonation: ExtendedFundDonation; onClose: () => void }) => { + const DEFAULT_GATEWAY = "https://bos.potlock.org/"; + const POTLOCK_TWITTER_ACCOUNT_ID = "PotLock_"; + const DEFAULT_SHARE_HASHTAGS = ["BOS", "PublicGoods", "Fund"]; + + const { ownerId, SUPPORTED_FTS } = constants; + + const twitterIntent = useMemo(() => { + const twitterIntentBase = "https://twitter.com/intent/tweet?text="; + + let url = + DEFAULT_GATEWAY + `${ownerId}/widget/Index?tab=pot&potId=${fundDonation.potId}&referrerId=${context.accountId}`; + let text = `I just funded ${fundDonation.potDetail.pot_name} on @${POTLOCK_TWITTER_ACCOUNT_ID}! Support public goods at `; + text = encodeURIComponent(text); + url = encodeURIComponent(url); + return twitterIntentBase + text + `&url=${url}` + `&hashtags=${DEFAULT_SHARE_HASHTAGS.join(",")}`; + }, [fundDonation]); + + const baseCurrency = fundDonation.potDetail.base_currency.toUpperCase() as "NEAR"; + + const nearAmount = formatWithCommas(SUPPORTED_FTS[baseCurrency].fromIndivisible(fundDonation.total_amount)); + const usdAmount = nearToUsd ? yoctosToUsd(fundDonation.total_amount) : null; + + return fundDonation ? ( + + + + + + + + +
Donation Successful
+ +
Share to
+
+ + + +
+
+
+ +
+ + + + + + + + + + + + + {nearAmount} + NEAR + + {usdAmount && {usdAmount} } +
+ + + + {fundDonation.potDetail.pot_name} + +
Round has been funded with
+
+
+ +
+
+ ) : ( + "" + ); +}; + +export default SuccessFundModal; diff --git a/src/pages/Pot/components/SuccessFundModal/styles.ts b/src/pages/Pot/components/SuccessFundModal/styles.ts new file mode 100644 index 00000000..c09de0aa --- /dev/null +++ b/src/pages/Pot/components/SuccessFundModal/styles.ts @@ -0,0 +1,93 @@ +import styled from "styled-components"; + +export const ModalMain = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + gap: 2rem; + padding: 40px 32px; +`; + +export const ModalHeader = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +`; + +export const HeaderIcon = styled.div` + padding: 12px; + width: 48px; + height: 48px; + border-radius: 50%; + background: #dd3345; + box-shadow: 0px 0px 0px 6px #fee6e5; + svg { + width: 100%; + height: 100%; + } +`; + +export const TwitterShare = styled.a` + display: flex; + gap: 8px; + color: white; + border-radius: 4px; + padding: 6px 1rem; + background: rgb(41, 41, 41); + align-items: center; + font-size: 14px; + cursor: pointer; + :hover { + text-decoration: none; + } +`; +export const ModalMiddel = styled.div` + display: flex; + flex-direction: column; + gap: 4px; + .amount-wrapper { + display: flex; + align-items: center; + gap: 8px; + justify-content: center; + img, + svg { + width: 20px; + height: 20px; + } + img { + border-radius: 50%; + } + } +`; + +export const Amount = styled.div` + font-size: 22px; + font-weight: 500; + letter-spacing: -0.33px; + text-transform: uppercase; +`; + +export const AmountUsd = styled.div` + color: #7b7b7b; + font-size: 22px; +`; + +export const ProjectName = styled.div` + display: flex; + align-items: center; + gap: 3px; + font-size: 14px; + div { + color: #7b7b7b; + } + a { + color: #525252; + &:hover { + text-decoration: none; + } + } +`; diff --git a/src/types.ts b/src/types.ts index d3b2aa6d..3ea3e1e0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,7 +25,7 @@ export type PotDetail = { pot_name: string; pot_description: string; max_projects: number; - base_currency: string; + base_currency: "near"; application_start_ms: number; application_end_ms: number; public_round_start_ms: number; @@ -43,7 +43,7 @@ export type PotDetail = { total_public_donations: string; public_donations_count: number; payouts: []; - cooldown_end_ms?: number; + cooldown_end_ms: number | null; all_paid_out: boolean; protocol_config_provider: string; }; @@ -53,3 +53,35 @@ export type Pot = { deployed_by: string; deployed_at_ms: number; }; + +export type PotDonation = { + id: string; + donor_id: string; + total_amount: string; + net_amount: string; + message: string; + donated_at: number; + project_id: null | "string"; + referrer_id: null | "string"; + referrer_fee: null | "string"; + protocol_fee: string; + matching_pool: boolean; + chef_id: null | "string"; + chef_fee: null | "string"; +}; + +export type FundDonation = { + id: string; + donor_id: string; + total_amount: string; + net_amount: string; + message: string; + donated_at: number; + project_id: null; + referrer_id: null | "string"; + referrer_fee: null | "string"; + protocol_fee: string; + matching_pool: true; + chef_id: null | "string"; + chef_fee: null | "string"; +}; diff --git a/yarn.lock b/yarn.lock index 7d7fe774..576d1fa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1229,10 +1229,10 @@ accepts@~1.3.4, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -alem@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/alem/-/alem-1.1.2.tgz#40c79e983f2c262b1095a46138e1e1a9ba0b957b" - integrity sha512-D2Mg9vUQQPgDEcZKErNRuKUfZg8yiFr8HzLiVotMTO+qvGNpD7hbOiP8gJ29WE8+46JLFwLVzPT4/lnm5N1xzQ== +alem@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/alem/-/alem-1.1.3.tgz#0f1375824a886a9b7f69e478f9938621aa3564e0" + integrity sha512-Lhprl34/dWuZ091y+g5duIwCBI0znnfuIugsrpqNS+YeG/c0CE6UW3ZOnPkEi3PgccZrLpN0im0ZWa2fQwvP/Q== dependencies: "@babel/core" "^7.24.3" "@babel/plugin-syntax-jsx" "^7.24.1" @@ -2811,7 +2811,16 @@ string-argv@0.3.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2829,7 +2838,14 @@ string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From 53eab077d05d2345e6d8396392b24afa24927456 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 15 May 2024 17:11:44 +0400 Subject: [PATCH 09/40] setup toast notification & add notification to update proeject status --- src/assets/svgs/SuccessfulIcon.tsx | 9 +++ .../ToastNotification/getToastContainer.tsx | 27 +++++++++ src/components/ToastNotification/styles.ts | 42 ++++++++++++++ src/contexts/ToastProvider.ts | 41 ++++++++++++++ src/hooks/useToast.ts | 4 ++ src/pages/Profile/components/Body/Body.tsx | 55 +++++++++++++++++-- .../components/BodyHeader/BodyHeader.tsx | 3 +- src/pages/Project/Project.tsx | 1 - src/types.ts | 14 +++++ src/utils/getTransactionsFromHashes.ts | 5 +- 10 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 src/assets/svgs/SuccessfulIcon.tsx create mode 100644 src/components/ToastNotification/getToastContainer.tsx create mode 100644 src/components/ToastNotification/styles.ts create mode 100644 src/contexts/ToastProvider.ts create mode 100644 src/hooks/useToast.ts diff --git a/src/assets/svgs/SuccessfulIcon.tsx b/src/assets/svgs/SuccessfulIcon.tsx new file mode 100644 index 00000000..ce3d93b1 --- /dev/null +++ b/src/assets/svgs/SuccessfulIcon.tsx @@ -0,0 +1,9 @@ +const SuccessfulIcon = (props: React.SVGProps) => ( + + + +); +export default SuccessfulIcon; diff --git a/src/components/ToastNotification/getToastContainer.tsx b/src/components/ToastNotification/getToastContainer.tsx new file mode 100644 index 00000000..4b918200 --- /dev/null +++ b/src/components/ToastNotification/getToastContainer.tsx @@ -0,0 +1,27 @@ +import { useEffect } from "alem"; +import SuccessfulIcon from "@app/assets/svgs/SuccessfulIcon"; +import ToastProvider from "@app/contexts/ToastProvider"; +import { useToastNotification } from "@app/hooks/useToast"; +import { Container, Description, Header } from "./styles"; + +const getToastContainer = () => { + ToastProvider(); + + const { toastContent } = useToastNotification(); + + const ToastContainer = () => { + return ( + +
+ + {toastContent.title} +
+ {toastContent.description} +
+ ); + }; + + return () => ; +}; + +export default getToastContainer; diff --git a/src/components/ToastNotification/styles.ts b/src/components/ToastNotification/styles.ts new file mode 100644 index 00000000..64bda572 --- /dev/null +++ b/src/components/ToastNotification/styles.ts @@ -0,0 +1,42 @@ +import styled from "styled-components"; + +export const Container = styled.div` + position: fixed; + right: -310px; + top: 10%; + opacity: 0; + display: flex; + flex-direction: column; + max-width: 300px; + padding: 1rem; + background: #fff; + box-shadow: 0px 0px 0px 1px rgba(5, 5, 5, 0.08), 0px 8px 8px -4px rgba(15, 15, 15, 0.15), + 0px 4px 15px -2px rgba(5, 5, 5, 0.08); + border-radius: 6px; + gap: 6px; + font-size: 14px; + transition: all 300ms cubic-bezier(0.23, 1, 0.32, 1); + &.active { + right: 10px; + opacity: 1; + } +`; + +export const Header = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + div { + line-height: 142%; + font-weight: 600; + } + svg { + width: 18px; + height: 18px; + } +`; + +export const Description = styled.div` + padding-left: 1.5rem; + color: #656565; +`; diff --git a/src/contexts/ToastProvider.ts b/src/contexts/ToastProvider.ts new file mode 100644 index 00000000..c717b7b5 --- /dev/null +++ b/src/contexts/ToastProvider.ts @@ -0,0 +1,41 @@ +import { createContext } from "alem"; + +export interface ToastProps { + title: string; + description: string; + type?: "success" | "error" | "warning"; +} + +export interface ToastContextProps { + toast: (newValue: ToastProps) => void; + toastContent: ToastProps; +} + +const ToastProvider = () => { + // Create a provider using a reference key + const { setDefaultData, updateData, getSelf } = createContext("toast-notification"); + + const EMPTY_TOAST = { + title: "", + description: "", + }; + + setDefaultData({ + toastContent: EMPTY_TOAST, + toast: (toastContent: ToastProps) => { + updateData({ + toastContent, + }); + setTimeout(() => { + updateData({ + toastContent: EMPTY_TOAST, + }); + // Wait 5sec before clearing the notification + }, 5000); + }, + }); + + return getSelf(); +}; + +export default ToastProvider; diff --git a/src/hooks/useToast.ts b/src/hooks/useToast.ts new file mode 100644 index 00000000..f79bda0c --- /dev/null +++ b/src/hooks/useToast.ts @@ -0,0 +1,4 @@ +import { useContext } from "alem"; +import { ToastContextProps } from "@app/contexts/ToastProvider"; + +export const useToastNotification = () => useContext("toast-notification"); diff --git a/src/pages/Profile/components/Body/Body.tsx b/src/pages/Profile/components/Body/Body.tsx index 87784dcf..20f0c7a4 100644 --- a/src/pages/Profile/components/Body/Body.tsx +++ b/src/pages/Profile/components/Body/Body.tsx @@ -1,9 +1,13 @@ -import { Near, context, props, useState, useParams, useMemo } from "alem"; +import { Near, context, props, useState, useParams, useMemo, useEffect } from "alem"; import ListsSDK from "@app/SDK/lists"; import Button from "@app/components/Button"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; +import getToastContainer from "@app/components/ToastNotification/getToastContainer"; import constants from "@app/constants"; +import { useToastNotification } from "@app/hooks/useToast"; import ModalOverlay from "@app/modals/ModalOverlay"; +import { Registration, RegistrationStatus } from "@app/types"; +import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; import Select from "../../../../components/Inputs/Select/Select"; import BannerHeader from "../BannerHeader/BannerHeader"; import BodyHeader from "../BodyHeader/BodyHeader"; @@ -19,8 +23,11 @@ import { Container, Details, ModalTitle, Row, Wrapper } from "./styles"; // }; const Body = (props: any) => { - const { projectId, registration } = props; - const { accountId: _accountId } = useParams(); + const { projectId } = props; + const { accountId: _accountId, transactionHashes } = useParams(); + + const registration = ListsSDK.getRegistration(null, projectId); + const accountId = _accountId ?? context.accountId; const { PROJECT_STATUSES, @@ -29,9 +36,19 @@ const Body = (props: any) => { const [statusReview, setStatusReview] = useState({ modalOpen: false, notes: "", newStatus: "" }); + const ToastNotification = getToastContainer(); + + const { toast } = useToastNotification(); + const listsContractId = ListsSDK.getContractId(); const userIsRegistryAdmin = ListsSDK.isRegistryAdmin(context.accountId); + const statusToast = (status: RegistrationStatus) => + toast({ + title: "Updated Successfully!", + description: `Project has been successfully updated to ${status.toLowerCase()}.`, + }); + const handleUpdateStatus = () => { Near.call([ { @@ -45,8 +62,37 @@ const Body = (props: any) => { deposit: NEAR.toIndivisible(0.01).toString(), }, ]); + + // success update project notification + const updateProjectSuccess = setInterval(() => { + ListsSDK.asyncGetRegistration(null, projectId).then((registration: Registration) => { + if (registration.status === statusReview.newStatus) { + statusToast(registration.status); + } + }); + }, 1000); + + // Clear the interval after 60 seconds + setTimeout(() => { + clearInterval(updateProjectSuccess); + }, 60000); }; + // Handle update project status for web wallet + useEffect(() => { + if (accountId && transactionHashes) { + getTransactionsFromHashes(transactionHashes, "plugrel.near").then((trxs) => { + const transaction = trxs[0].body.result.transaction; + const methodName = transaction.actions[0].FunctionCall.method_name; + const successVal = trxs[0].body.result.status?.SuccessValue; + const result = JSON.parse(Buffer.from(successVal, "base64").toString("utf-8")); + if (methodName === "update_registration" && result) { + statusToast(result.status); + } + }); + } + }, []); + const SelectedNavComponent = useMemo(() => { return props.navOptions.find((option: any) => option.id == props.nav).source; }, []); @@ -69,7 +115,7 @@ const Body = (props: any) => { value: status, text: status, })), - value: { text: props.registration.status, value: props.registration.status }, + value: { text: registration.status, value: registration.status }, onChange: (status) => { if (status.value != registration.status) { setStatusReview({ ...statusReview, newStatus: status.value, modalOpen: true }); @@ -121,6 +167,7 @@ const Body = (props: any) => { )} + ); }; diff --git a/src/pages/Profile/components/BodyHeader/BodyHeader.tsx b/src/pages/Profile/components/BodyHeader/BodyHeader.tsx index a69e9798..b9c1e90d 100644 --- a/src/pages/Profile/components/BodyHeader/BodyHeader.tsx +++ b/src/pages/Profile/components/BodyHeader/BodyHeader.tsx @@ -79,12 +79,11 @@ const BodyHeader = ({ profile, accountId, projectId }: Props) => { style={{ marginLeft: "auto" }} href={hrefWithParams(`?tab=editproject&projectId=${projectId}`)} > - edit profile + Edit profile )} {accountId === context.accountId && !projectId && ( )} diff --git a/src/pages/Project/Project.tsx b/src/pages/Project/Project.tsx index 10f6e133..f4303d50 100644 --- a/src/pages/Project/Project.tsx +++ b/src/pages/Project/Project.tsx @@ -116,7 +116,6 @@ const ProjectPage = () => { Date: Wed, 15 May 2024 18:14:13 +0400 Subject: [PATCH 10/40] handle update application statue --- .../NavPages/Applications/Applications.tsx | 36 ++++++++++++++++--- .../ApplicationReviewModal.tsx | 26 ++++++++++++-- src/pages/Profile/components/Body/Body.tsx | 2 +- src/types.ts | 11 ++++++ 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/pages/Pot/NavPages/Applications/Applications.tsx b/src/pages/Pot/NavPages/Applications/Applications.tsx index b1b8a5ad..af8dda2f 100644 --- a/src/pages/Pot/NavPages/Applications/Applications.tsx +++ b/src/pages/Pot/NavPages/Applications/Applications.tsx @@ -1,11 +1,14 @@ -import { Social, State, context, state, useParams, Tooltip, OverlayTrigger } from "alem"; +import { Social, State, context, state, useParams, Tooltip, OverlayTrigger, useEffect } from "alem"; import PotSDK from "@app/SDK/pot"; import Button from "@app/components/Button"; import Dropdown from "@app/components/Inputs/Dropdown/Dropdown"; +import getToastContainer from "@app/components/ToastNotification/getToastContainer"; import ProfileImage from "@app/components/mob.near/ProfileImage"; +import { useToastNotification } from "@app/hooks/useToast"; import { PotDetail } from "@app/types"; import _address from "@app/utils/_address"; import daysAgo from "@app/utils/daysAgo"; +import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; import hrefWithParams from "@app/utils/hrefWithParams"; import ApplicationReviewModal from "../../components/ApplicationReviewModal/ApplicationReviewModal"; import APPLICATIONS_FILTERS_TAGS from "./APPLICATIONS_FILTERS_TAGS"; @@ -21,7 +24,8 @@ import { } from "./styles"; const Applications = ({ potDetail }: { potDetail: PotDetail }) => { - const { potId } = useParams(); + const accountId = context.accountId; + const { potId, transactionHashes } = useParams(); State.init({ newStatus: "", @@ -36,6 +40,10 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { const applications = PotSDK.getApplications(potId); + const ToastNotification = getToastContainer(); + + const { toast } = useToastNotification(); + const getApplicationCount = (sortVal: string) => { if (!applications) return; return applications?.filter((application: any) => { @@ -56,8 +64,27 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { const { owner, admins, chef } = potDetail; - const isChefOrGreater = - context.accountId === chef || admins.includes(context.accountId || "") || context.accountId === owner; + // Handle update application status for web wallet + useEffect(() => { + if (accountId && transactionHashes) { + getTransactionsFromHashes(transactionHashes, accountId).then((trxs) => { + const transaction = trxs[0].body.result.transaction; + + const methodName = transaction.actions[0].FunctionCall.method_name; + const successVal = trxs[0].body.result.status?.SuccessValue; + const result = JSON.parse(Buffer.from(successVal, "base64").toString("utf-8")); + + if (methodName === "chef_set_application_status" && result) { + toast({ + title: "Updated Successfully!", + description: `Application status has been successfully updated to ${result.status}.`, + }); + } + }); + } + }, []); + + const isChefOrGreater = accountId === chef || admins.includes(accountId || "") || accountId === owner; const handleApproveApplication = (projectId: string) => { State.update({ newStatus: "Approved", projectId }); @@ -271,6 +298,7 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { )} {projectId && } + ); }; diff --git a/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx b/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx index 675d0219..8995913e 100644 --- a/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx +++ b/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx @@ -1,8 +1,11 @@ import { Near, State, state, useParams } from "alem"; +import PotSDK from "@app/SDK/pot"; import Button from "@app/components/Button"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; import constants from "@app/constants"; +import { useToastNotification } from "@app/hooks/useToast"; import ModalOverlay from "@app/modals/ModalOverlay"; +import { PotApplication } from "@app/types"; import { ModalBody, ModalFooter, ModalHeader } from "./styles"; const ApplicationReviewModal = ({ @@ -19,6 +22,8 @@ const ApplicationReviewModal = ({ reviewMessageError: "", }); + const { toast } = useToastNotification(); + const { reviewMessage, reviewMessageError } = state; const { potId } = useParams(); @@ -35,6 +40,24 @@ const ApplicationReviewModal = ({ onClose(); }; + const handleSuccess = () => { + const applicationSuccess = setInterval(() => { + PotSDK.asyncGetApplicationByProjectId(potId, projectId).then((application: PotApplication) => { + if (application.status === newStatus) { + toast({ + title: "Updated Successfully!", + description: `Application has been successfully updated to ${newStatus}.`, + }); + } + }); + }, 1000); + // Clear the interval after 60 seconds + setTimeout(() => { + onClose(); + clearInterval(applicationSuccess); + }, 60000); + }; + const handleSubmit = () => { const args = { project_id: projectId, @@ -51,8 +74,7 @@ const ApplicationReviewModal = ({ }, ]; Near.call(transactions); - // NB: we won't get here if user used a web wallet, as it will redirect to the wallet - // <---- TODO: IMPLEMENT EXTENSION WALLET HANDLING ----> + handleSuccess(); }; return ( diff --git a/src/pages/Profile/components/Body/Body.tsx b/src/pages/Profile/components/Body/Body.tsx index 20f0c7a4..676b2d95 100644 --- a/src/pages/Profile/components/Body/Body.tsx +++ b/src/pages/Profile/components/Body/Body.tsx @@ -81,7 +81,7 @@ const Body = (props: any) => { // Handle update project status for web wallet useEffect(() => { if (accountId && transactionHashes) { - getTransactionsFromHashes(transactionHashes, "plugrel.near").then((trxs) => { + getTransactionsFromHashes(transactionHashes, accountId).then((trxs) => { const transaction = trxs[0].body.result.transaction; const methodName = transaction.actions[0].FunctionCall.method_name; const successVal = trxs[0].body.result.status?.SuccessValue; diff --git a/src/types.ts b/src/types.ts index dafb5f2d..dd6d0a6c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -88,6 +88,8 @@ export type FundDonation = { export type RegistrationStatus = "Approved" | "Rejected" | "Pending" | "Graylisted" | "Blacklisted"; +export type ApplicationStatus = "Pending" | "Approved" | "Rejected"; + export type Registration = { id: string; registrant_id: string; @@ -99,3 +101,12 @@ export type Registration = { registrant_notes: null | string; registered_by: string; }; + +export type PotApplication = { + project_id: string; + message: string; + status: ApplicationStatus; + submitted_at: 1715764402476; + updated_at: null; + review_notes: null; +}; From 3f0336db8b99df62d4e40cb9b5f0ade27e0a05c3 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 16 May 2024 11:08:10 +0400 Subject: [PATCH 11/40] add success state to update pot setting --- .../ToastNotification/getToastContainer.tsx | 32 ++-- src/components/mob.near/ProfileImage.tsx | 4 +- src/contexts/ToastProvider.ts | 4 +- .../NavPages/Applications/Applications.tsx | 41 +++-- .../Pot/NavPages/ConfigForm/ConfigForm.tsx | 149 ++++++++++++------ .../ApplicationReviewModal.tsx | 10 +- src/pages/Profile/components/Body/Body.tsx | 25 +-- src/utils/deepEqual.ts | 20 +++ 8 files changed, 189 insertions(+), 96 deletions(-) create mode 100644 src/utils/deepEqual.ts diff --git a/src/components/ToastNotification/getToastContainer.tsx b/src/components/ToastNotification/getToastContainer.tsx index 4b918200..166ba2bf 100644 --- a/src/components/ToastNotification/getToastContainer.tsx +++ b/src/components/ToastNotification/getToastContainer.tsx @@ -1,27 +1,23 @@ import { useEffect } from "alem"; import SuccessfulIcon from "@app/assets/svgs/SuccessfulIcon"; -import ToastProvider from "@app/contexts/ToastProvider"; +import ToastProvider, { ToastProps } from "@app/contexts/ToastProvider"; import { useToastNotification } from "@app/hooks/useToast"; import { Container, Description, Header } from "./styles"; -const getToastContainer = () => { - ToastProvider(); +const ToastContainer = ({ toastContent }: { toastContent: ToastProps }) => { + // ToastProvider(); - const { toastContent } = useToastNotification(); + // const { toastContent } = useToastNotification(); - const ToastContainer = () => { - return ( - -
- - {toastContent.title} -
- {toastContent.description} -
- ); - }; - - return () => ; + return ( + +
+ + {toastContent.title} +
+ {toastContent.description} +
+ ); }; -export default getToastContainer; +export default ToastContainer; diff --git a/src/components/mob.near/ProfileImage.tsx b/src/components/mob.near/ProfileImage.tsx index daeba109..0d5e16f1 100644 --- a/src/components/mob.near/ProfileImage.tsx +++ b/src/components/mob.near/ProfileImage.tsx @@ -60,8 +60,8 @@ const ProfileImage = (profileImgProps: Props) => { accountId, }); } - const fallbackUrl = props.fallbackUrl; - + const fallbackUrl = + props.fallbackUrl ?? "https://ipfs.near.social/ipfs/bafkreiccpup6f2kihv7bhlkfi4omttbjpawnsns667gti7jbhqvdnj4vsm"; const imageProps = { image, alt: title, diff --git a/src/contexts/ToastProvider.ts b/src/contexts/ToastProvider.ts index c717b7b5..b6a54a21 100644 --- a/src/contexts/ToastProvider.ts +++ b/src/contexts/ToastProvider.ts @@ -30,8 +30,8 @@ const ToastProvider = () => { updateData({ toastContent: EMPTY_TOAST, }); - // Wait 5sec before clearing the notification - }, 5000); + // Wait 7sec before clearing the notification + }, 7000); }, }); diff --git a/src/pages/Pot/NavPages/Applications/Applications.tsx b/src/pages/Pot/NavPages/Applications/Applications.tsx index af8dda2f..11985433 100644 --- a/src/pages/Pot/NavPages/Applications/Applications.tsx +++ b/src/pages/Pot/NavPages/Applications/Applications.tsx @@ -2,9 +2,8 @@ import { Social, State, context, state, useParams, Tooltip, OverlayTrigger, useE import PotSDK from "@app/SDK/pot"; import Button from "@app/components/Button"; import Dropdown from "@app/components/Inputs/Dropdown/Dropdown"; -import getToastContainer from "@app/components/ToastNotification/getToastContainer"; +import ToastContainer from "@app/components/ToastNotification/getToastContainer"; import ProfileImage from "@app/components/mob.near/ProfileImage"; -import { useToastNotification } from "@app/hooks/useToast"; import { PotDetail } from "@app/types"; import _address from "@app/utils/_address"; import daysAgo from "@app/utils/daysAgo"; @@ -34,16 +33,16 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { allApplications: null, filteredApplications: [], filterVal: "ALL", + toastContent: { + title: "", + description: "", + }, }); - const { newStatus, projectId, searchTerm, allApplications, filteredApplications, filterVal } = state; + const { newStatus, projectId, searchTerm, allApplications, filteredApplications, filterVal, toastContent } = state; const applications = PotSDK.getApplications(potId); - const ToastNotification = getToastContainer(); - - const { toast } = useToastNotification(); - const getApplicationCount = (sortVal: string) => { if (!applications) return; return applications?.filter((application: any) => { @@ -64,6 +63,23 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { const { owner, admins, chef } = potDetail; + const toast = (newStatus: string) => { + State.update({ + toastContent: { + title: "Updated Successfully!", + description: `Application status has been successfully updated to ${newStatus}.`, + }, + }); + setTimeout(() => { + State.update({ + toastContent: { + title: "", + description: "", + }, + }); + }, 7000); + }; + // Handle update application status for web wallet useEffect(() => { if (accountId && transactionHashes) { @@ -75,10 +91,7 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { const result = JSON.parse(Buffer.from(successVal, "base64").toString("utf-8")); if (methodName === "chef_set_application_status" && result) { - toast({ - title: "Updated Successfully!", - description: `Application status has been successfully updated to ${result.status}.`, - }); + toast(result.status); } }); } @@ -297,8 +310,10 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => {
No applications to display
)} - {projectId && } - + {projectId && ( + + )} + ); }; diff --git a/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx b/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx index a8466161..1d69262d 100644 --- a/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx +++ b/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx @@ -1,5 +1,6 @@ -import { Big, Near, State, context, state, useMemo, useParams } from "alem"; +import { Big, Near, context, useEffect, useMemo, useParams, useState } from "alem"; import ListsSDK from "@app/SDK/lists"; +import PotSDK from "@app/SDK/pot"; import PotFactorySDK from "@app/SDK/potfactory"; import AccountsList from "@app/components/AccountsList/AccountsList"; import Button from "@app/components/Button"; @@ -8,8 +9,11 @@ import DateInput from "@app/components/Inputs/Date/Date"; import Text from "@app/components/Inputs/Text/Text"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; import ModalMultiAccount from "@app/components/ModalMultiAccount/ModalMultiAccount"; +import ToastContainer from "@app/components/ToastNotification/getToastContainer"; import constants from "@app/constants"; import { PotDetail } from "@app/types"; +import deepEqual from "@app/utils/deepEqual"; +import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; import validateNearAddress from "@app/utils/validateNearAddress"; import { CheckboxWrapper, @@ -24,7 +28,8 @@ import { } from "./styles"; const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any }) => { - const { potId } = useParams(); + const { potId, transactionHashes } = useParams(); + const { NADABOT_HUMAN_METHOD, ONE_TGAS, @@ -73,7 +78,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } // console.log("potDetail: ", potDetail); - State.init({ + const inital_state = { owner: isUpdate ? potDetail.owner : context.accountId, ownerError: "", admin: "", @@ -118,12 +123,23 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } usePotlockRegistry: isUpdate ? potDetail.registry_provider == DEFAULT_REGISTRY_PROVIDER : true, latestSourceCodeCommitHash: "", deploymentSuccess: false, - }); + toastContent: { + title: "", + description: "", + }, + }; + + const [state, setState] = useState(inital_state); + const updateState = (updatedState: any) => + setState({ + ...inital_state, + ...updatedState, + }); if (!isUpdate && !state.latestSourceCodeCommitHash) { const res: any = fetch("https://api.github.com/repos/PotLock/core/commits"); if (res.ok && res.body.length > 0) { - State.update({ + updateState({ latestSourceCodeCommitHash: res.body[0].sha, }); } @@ -132,7 +148,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } const getPotDetailArgsFromState = () => { const args = { owner: state.owner, - admins: state.admins.filter((admin: any) => !admin.remove).map((admin: any) => admin.accountId), + admins: state.admins?.filter((admin: any) => !admin.remove).map((admin: any) => admin.accountId), chef: state.chef || null, pot_name: state.name, pot_description: state.description, @@ -229,17 +245,50 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } const pot = pots.find((pot: any) => pot.deployed_by === context.accountId && pot.deployed_at_ms > now); if (pot) { clearInterval(pollId); - State.update({ deploymentSuccess: true }); + updateState({ deploymentSuccess: true }); } }); }, pollIntervalMs); }); }; + const toast = () => { + updateState({ + toastContent: { + title: "Saved Successfully!", + description: "Changes to pot has been saved successfully.", + }, + }); + setTimeout(() => { + updateState({ + toastContent: { + title: "", + description: "", + }, + }); + }, 7000); + }; + + useEffect(() => { + if (context.accountId && transactionHashes) { + getTransactionsFromHashes(transactionHashes, context.accountId).then((trxs) => { + console.log("trxs", trxs); + + const transaction = trxs[0].body.result.transaction; + + const methodName = transaction.actions[0].FunctionCall.method_name; + const successVal = trxs[0].body.result.status?.SuccessValue; + const result = JSON.parse(Buffer.from(successVal, "base64").toString("utf-8")); + if (methodName === "admin_dangerously_set_pot_config" && result) { + toast(); + } + }); + } + }, []); + const handleUpdate = () => { // create update pot args const updateArgs = getPotDetailArgsFromState(); - // console.log("updateArgs: ", updateArgs); const depositFloat = JSON.stringify(updateArgs).length * 0.00003; const deposit = Big(depositFloat).mul(Big(10).pow(24)); const transactions = [ @@ -252,12 +301,21 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } }, ]; Near.call(transactions); - // NB: we won't get here if user used a web wallet, as it will redirect to the wallet - // <---- EXTENSION WALLET HANDLING ----> - // TODO: IMPLEMENT - }; - // console.log("state: ", state); + const potConfigSuccess = setInterval(() => { + PotSDK.asyncGetConfig(potId).then((detail: PotDetail) => { + if (deepEqual(updateArgs, detail, ["source_metadata", "toastContent"])) { + toast(); + clearInterval(potConfigSuccess); + } + }); + }, 1000); + + // Clear the interval after 60 seconds + setTimeout(() => { + clearInterval(potConfigSuccess); + }, 60000); + }; const validateAndUpdatePercentages = (percent: any, stateKey: any, errorKey: any, maxVal: any) => { // TODO: move this to separate component for percentage input that accepts "basisPoints" bool parameter @@ -276,7 +334,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } } // if it ends with a period and this is the only period in the string, set on state if (percent.endsWith(".") && percent.indexOf(".") === percent.length - 1) { - State.update({ + updateState({ [stateKey]: percent, }); return; @@ -290,13 +348,13 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } } } } - State.update(updates); + updateState(updates); }; const handleAddAdmin = () => { let isValid = validateNearAddress(state.admin); if (!isValid) { - State.update({ + updateState({ adminsError: "Invalid NEAR account ID", }); return; @@ -311,7 +369,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } }; const admins = [...state.admins, newAdmin]; // console.log("admins: ", admins); - State.update({ + updateState({ admins, admin: "", adminsError: "", @@ -320,7 +378,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } }; const handleRemoveAdmin = (accountId: any) => { - State.update({ + updateState({ admins: state.admins.map((admin: any) => { if (admin.accountId == accountId) { return { ...admin, remove: true }; @@ -353,11 +411,11 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Owner *", placeholder: `E.g. ${context.accountId}`, value: state.owner, - onChange: (owner) => State.update({ owner, ownerError: "" }), + onChange: (owner) => updateState({ owner, ownerError: "" }), validate: () => { // **CALLED ON BLUR** const valid = validateNearAddress(state.owner); - State.update({ ownerError: valid ? "" : "Invalid NEAR account ID" }); + updateState({ ownerError: valid ? "" : "Invalid NEAR account ID" }); }, error: state.ownerError, disabled: isUpdate ? !userIsOwner : true, @@ -367,7 +425,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } !account.remove) .map((account: any) => account.accountId), allowRemove: isUpdate ? userIsOwner : true, @@ -380,7 +438,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } {...{ varient: "outline", style: { width: "fit-content" }, - onClick: () => State.update({ isAdminsModalOpen: true }), + onClick: () => updateState({ isAdminsModalOpen: true }), }} > Add admins @@ -392,11 +450,11 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Name *", placeholder: "E.g. DeFi Center", value: state.name, - onChange: (name) => State.update({ name, nameError: "" }), + onChange: (name) => updateState({ name, nameError: "" }), validate: () => { // **CALLED ON BLUR** const valid = state.name.length <= MAX_POT_NAME_LENGTH; - State.update({ + updateState({ nameError: valid ? "" : `Name must be ${MAX_POT_NAME_LENGTH} characters or less`, }); }, @@ -409,7 +467,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Custom handle (optional - will slugify name by default)", placeholder: "e.g. my-pot-handle", value: state.customHandle, - onChange: (customHandle) => State.update({ customHandle, customHandleError: "" }), + onChange: (customHandle) => updateState({ customHandle, customHandleError: "" }), validate: () => { // **CALLED ON BLUR** const suffix = `.${potFactoryContractId}`; @@ -423,7 +481,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } ? "" : `Invalid handle (can only contain lowercase alphanumeric symbols + _ or -)`; } - State.update({ + updateState({ customHandleError, }); }, @@ -437,11 +495,11 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Description", placeholder: "Type description", value: state.description, - onChange: (description: string) => State.update({ description }), + onChange: (description: string) => updateState({ description }), validate: () => { // **CALLED ON BLUR** const valid = state.description.length <= MAX_POT_DESCRIPTION_LENGTH; - State.update({ + updateState({ descriptionError: valid ? "" : `Description must be ${MAX_POT_DESCRIPTION_LENGTH} characters or less`, }); }, @@ -509,7 +567,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } selectTime: true, value: state.applicationStartDate, onChange: (date) => { - State.update({ applicationStartDate: date }); + updateState({ applicationStartDate: date }); }, validate: () => { // **CALLED ON BLUR** @@ -520,7 +578,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } const applicationEndDate = new Date(state.applicationEndDate).getTime(); const valid = applicationStartDate > now && (!applicationEndDate || applicationStartDate < applicationEndDate); - State.update({ + updateState({ applicationStartDateError: valid ? "" : "Invalid application start date", }); }, @@ -534,14 +592,14 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } // placeholder: "0", // TODO: possibly add this back in selectTime: true, value: state.applicationEndDate, - onChange: (date) => State.update({ applicationEndDate: date }), + onChange: (date) => updateState({ applicationEndDate: date }), validate: () => { // **CALLED ON BLUR** // must be before matching round start date const valid = (!state.matchingRoundStartDate || state.applicationEndDate < state.matchingRoundStartDate) && (!state.applicationStartDate || state.applicationEndDate > state.applicationStartDate); - State.update({ + updateState({ applicationEndDateError: valid ? "" : "Invalid application end date", }); }, @@ -554,14 +612,14 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Matching round start date", selectTime: true, value: state.matchingRoundStartDate, - onChange: (date) => State.update({ matchingRoundStartDate: date }), + onChange: (date) => updateState({ matchingRoundStartDate: date }), validate: () => { // **CALLED ON BLUR** // must be after application end and before matching round end const valid = (!state.applicationEndDate || state.matchingRoundStartDate > state.applicationEndDate) && (!state.matchingRoundEndDate || state.matchingRoundStartDate < state.matchingRoundEndDate); - State.update({ + updateState({ matchingRoundStartDateError: valid ? "" : "Invalid round start date", }); }, @@ -576,13 +634,13 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } // placeholder: "0", // TODO: possibly add this back in selectTime: true, value: state.matchingRoundEndDate, - onChange: (date) => State.update({ matchingRoundEndDate: date }), + onChange: (date) => updateState({ matchingRoundEndDate: date }), validate: () => { // **CALLED ON BLUR** // must be after matching round start const valid = !state.matchingRoundStartDate || state.matchingRoundEndDate > state.matchingRoundStartDate; - State.update({ matchingRoundEndDateError: valid ? "" : "Invalid round end date" }); + updateState({ matchingRoundEndDateError: valid ? "" : "Invalid round end date" }); }, error: state.matchingRoundEndDateError, disabled: isUpdate ? !isAdminOrGreater : false, @@ -596,7 +654,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } placeholder: "0", value: state.minMatchingPoolDonationAmount, onChange: (amountNear) => { - State.update({ minMatchingPoolDonationAmount: amountNear }); + updateState({ minMatchingPoolDonationAmount: amountNear }); }, validate: () => { // **CALLED ON BLUR** @@ -618,11 +676,11 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Assign chef", placeholder: "E.g. user.near", value: state.chef, - onChange: (chef) => State.update({ chef }), + onChange: (chef) => updateState({ chef }), validate: () => { // **CALLED ON BLUR** const valid = validateNearAddress(state.chef); - State.update({ chefError: valid ? "" : "Invalid NEAR account ID" }); + updateState({ chefError: valid ? "" : "Invalid NEAR account ID" }); }, error: state.chefError, disabled: isUpdate ? !isAdminOrGreater : false, @@ -662,11 +720,11 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } label: "Max. approved projects", placeholder: "e.g. 20", value: state.maxProjects, - onChange: (maxProjects) => State.update({ maxProjects }), + onChange: (maxProjects) => updateState({ maxProjects }), validate: () => { // **CALLED ON BLUR** const valid = parseInt(state.maxProjects) <= MAX_MAX_PROJECTS; - State.update({ maxProjectsError: valid ? "" : `Maximum ${MAX_MAX_PROJECTS}` }); + updateState({ maxProjectsError: valid ? "" : `Maximum ${MAX_MAX_PROJECTS}` }); }, error: state.maxProjectsError, disabled: isUpdate ? !isAdminOrGreater : false, @@ -684,7 +742,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } id: "registrationSelector", checked: state.usePotlockRegistry, onClick: (e: any) => { - State.update({ + updateState({ usePotlockRegistry: e.target.checked, }); }, @@ -707,7 +765,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } id: "sybilSelector", checked: state.useNadabotSybil, onClick: (e: any) => { - State.update({ + updateState({ useNadabotSybil: e.target.checked, }); }, @@ -749,12 +807,12 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } {state.isAdminsModalOpen && ( State.update({ isAdminsModalOpen: false }), + onClose: () => updateState({ isAdminsModalOpen: false }), titleText: "Add admins", descriptionText: "Add NEAR account IDs for your admins.", inputValue: state.admin, onInputChange: (admin: any) => { - State.update({ admin, adminsError: "" }); + updateState({ admin, adminsError: "" }); }, handleAddAccount: handleAddAdmin, handleRemoveAccount: handleRemoveAdmin, @@ -764,6 +822,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } }} /> )} + ); }; diff --git a/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx b/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx index 8995913e..41e0dabe 100644 --- a/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx +++ b/src/pages/Pot/components/ApplicationReviewModal/ApplicationReviewModal.tsx @@ -3,7 +3,6 @@ import PotSDK from "@app/SDK/pot"; import Button from "@app/components/Button"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; import constants from "@app/constants"; -import { useToastNotification } from "@app/hooks/useToast"; import ModalOverlay from "@app/modals/ModalOverlay"; import { PotApplication } from "@app/types"; import { ModalBody, ModalFooter, ModalHeader } from "./styles"; @@ -12,18 +11,18 @@ const ApplicationReviewModal = ({ projectId, onClose, newStatus, + toast, }: { projectId: string; newStatus: string; onClose: () => void; + toast: (newStatus: string) => void; }) => { State.init({ reviewMessage: "", reviewMessageError: "", }); - const { toast } = useToastNotification(); - const { reviewMessage, reviewMessageError } = state; const { potId } = useParams(); @@ -44,10 +43,7 @@ const ApplicationReviewModal = ({ const applicationSuccess = setInterval(() => { PotSDK.asyncGetApplicationByProjectId(potId, projectId).then((application: PotApplication) => { if (application.status === newStatus) { - toast({ - title: "Updated Successfully!", - description: `Application has been successfully updated to ${newStatus}.`, - }); + toast(newStatus); } }); }, 1000); diff --git a/src/pages/Profile/components/Body/Body.tsx b/src/pages/Profile/components/Body/Body.tsx index 676b2d95..8a831d48 100644 --- a/src/pages/Profile/components/Body/Body.tsx +++ b/src/pages/Profile/components/Body/Body.tsx @@ -2,9 +2,8 @@ import { Near, context, props, useState, useParams, useMemo, useEffect } from "a import ListsSDK from "@app/SDK/lists"; import Button from "@app/components/Button"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; -import getToastContainer from "@app/components/ToastNotification/getToastContainer"; +import ToastContainer from "@app/components/ToastNotification/getToastContainer"; import constants from "@app/constants"; -import { useToastNotification } from "@app/hooks/useToast"; import ModalOverlay from "@app/modals/ModalOverlay"; import { Registration, RegistrationStatus } from "@app/types"; import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; @@ -35,20 +34,28 @@ const Body = (props: any) => { } = constants; const [statusReview, setStatusReview] = useState({ modalOpen: false, notes: "", newStatus: "" }); - - const ToastNotification = getToastContainer(); - - const { toast } = useToastNotification(); + const [toastContent, setToastContent] = useState({ + title: "", + description: "", + }); const listsContractId = ListsSDK.getContractId(); const userIsRegistryAdmin = ListsSDK.isRegistryAdmin(context.accountId); - const statusToast = (status: RegistrationStatus) => - toast({ + const statusToast = (status: RegistrationStatus) => { + setToastContent({ title: "Updated Successfully!", description: `Project has been successfully updated to ${status.toLowerCase()}.`, }); + setTimeout(() => { + setToastContent({ + title: "", + description: ``, + }); + }, 7000); + }; + const handleUpdateStatus = () => { Near.call([ { @@ -167,7 +174,7 @@ const Body = (props: any) => { )} - + ); }; diff --git a/src/utils/deepEqual.ts b/src/utils/deepEqual.ts new file mode 100644 index 00000000..8f869266 --- /dev/null +++ b/src/utils/deepEqual.ts @@ -0,0 +1,20 @@ +function deepEqual(obj1: any, obj2: any, keysToIgnore?: string[]): boolean { + if (obj1 === obj2) return true; + + if (typeof obj1 !== "object" || obj1 === null || typeof obj2 !== "object" || obj2 === null) { + return false; + } + + const keys1 = Object.keys(obj1).filter((key) => !keysToIgnore?.includes(key)); + const keys2 = Object.keys(obj2).filter((key) => !keysToIgnore?.includes(key)); + + // if (keys1.length !== keys2.length) return false; + + for (let key of keys1) { + if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key], keysToIgnore)) return false; + } + + return true; +} + +export default deepEqual; From fc3808aafcf9b4c8a98d1ba457e7653b3dc0fd11 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 16 May 2024 13:56:06 +0400 Subject: [PATCH 12/40] fix follow button --- .../components/DonationsInfo/styles.ts | 1 - .../components/FollowButton/FollowButton.tsx | 53 ++++++---------- .../Project/components/FollowButton/styles.ts | 61 ++++++++++--------- 3 files changed, 52 insertions(+), 63 deletions(-) diff --git a/src/pages/Project/components/DonationsInfo/styles.ts b/src/pages/Project/components/DonationsInfo/styles.ts index cd900667..b531cd9e 100644 --- a/src/pages/Project/components/DonationsInfo/styles.ts +++ b/src/pages/Project/components/DonationsInfo/styles.ts @@ -32,7 +32,6 @@ export const Container = styled.div` display: flex; gap: 1.5rem; justify-content: space-between; - > div, button { padding: 10px 0; width: 160px; diff --git a/src/pages/Project/components/FollowButton/FollowButton.tsx b/src/pages/Project/components/FollowButton/FollowButton.tsx index c2004e8a..ae4a39fa 100644 --- a/src/pages/Project/components/FollowButton/FollowButton.tsx +++ b/src/pages/Project/components/FollowButton/FollowButton.tsx @@ -25,47 +25,32 @@ const FollowButton = ({ accountId, classname }: Props) => { const type = follow ? "unfollow" : "follow"; - const socialArgs = { - data: { - [context.accountId]: { - graph: { follow: { [accountId]: follow ? null : "" } }, - index: { - graph: JSON.stringify({ - key: "follow", - value: { - type, - accountId: accountId, - }, - }), - notify: JSON.stringify({ - key: accountId, - value: { - type, - }, - }), + const data = { + graph: { follow: { [accountId]: follow ? null : "" } }, + index: { + graph: JSON.stringify({ + key: "follow", + value: { + type, + accountId: accountId, }, - }, + }), + notify: JSON.stringify({ + key: accountId, + value: { + type, + }, + }), }, }; const buttonText = loading ? "Loading" : follow ? "Following" : inverse ? "Follow back" : "Follow"; return ( - { - const transactions = [ - { - contractName: "social.near", - methodName: "set", - deposit: Big(JSON.stringify(socialArgs).length * 0.00003).mul(Big(10).pow(24)), - args: socialArgs, - }, - ]; - Near.call(transactions); - }} - > - {buttonText} + + + {buttonText} + ); }; diff --git a/src/pages/Project/components/FollowButton/styles.ts b/src/pages/Project/components/FollowButton/styles.ts index e02ac21f..6e4e6790 100644 --- a/src/pages/Project/components/FollowButton/styles.ts +++ b/src/pages/Project/components/FollowButton/styles.ts @@ -1,38 +1,43 @@ import styled from "styled-components"; export const FollowContainer = styled.div<{ buttonText?: string }>` - position: relative; - cursor: pointer; - border-radius: 6px; - border: 1px solid #dd3345; - font-size: 14px; - font-weight: 600; - color: #dd3345; - word-wrap: break-word; - transition: all 300ms; - &::before { - background: #dd3345; - position: absolute; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - content: "Unfollow"; - color: white; - opacity: 0; + button { + position: relative; + cursor: pointer; + border-radius: 6px; + border: 1px solid #dd3345; + background: transparent; + font-size: 14px; + font-weight: 600; + color: #dd3345; + word-wrap: break-word; transition: all 300ms; - } - :hover { - background: #dd3345; - color: white; - ${(props) => - props.buttonText === "Following" - ? ` + &::before { + background: #dd3345; + position: absolute; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + content: "Unfollow"; + color: white; + opacity: 0; + transition: all 300ms; + } + :hover { + background: #dd3345; + color: white; + ${(props) => + props.buttonText === "Following" + ? ` ::before { opacity: 1; } ` - : ""} + : ""} + } } `; From ed57b382dea5b3b557aa94d95255a40f638078b5 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 17 May 2024 10:52:21 +0400 Subject: [PATCH 13/40] fix sort projects --- .../components/AllProjects/AllProjects.tsx | 5 +++-- src/pages/Projects/components/ListSection.tsx | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/pages/Projects/components/AllProjects/AllProjects.tsx b/src/pages/Projects/components/AllProjects/AllProjects.tsx index 20c4a003..bc70cc13 100644 --- a/src/pages/Projects/components/AllProjects/AllProjects.tsx +++ b/src/pages/Projects/components/AllProjects/AllProjects.tsx @@ -24,8 +24,9 @@ const AllProjects = () => { useEffect(() => { if (projects.length === 0 && projectsData) { const { allProjects, approvedProjects } = projectsData; + const shuffledProjects = [...approvedProjects].sort(() => Math.random() - 0.5); setProjects(allProjects); - setFilteredProjects(approvedProjects); + setFilteredProjects(shuffledProjects); } }, [projectsData]); @@ -156,7 +157,7 @@ const AllProjects = () => { {filteredProjects.length ? ( } /> diff --git a/src/pages/Projects/components/ListSection.tsx b/src/pages/Projects/components/ListSection.tsx index d5810a36..41d566ba 100644 --- a/src/pages/Projects/components/ListSection.tsx +++ b/src/pages/Projects/components/ListSection.tsx @@ -9,7 +9,7 @@ type BreakPoint = { type Props = { shouldShuffle?: boolean; renderItem: any; - items: any; + items: any[]; maxCols?: number; responsive?: BreakPoint[]; }; @@ -23,12 +23,12 @@ const ListSection = ({ shouldShuffle, items, renderItem }: Props) => { return

Loading...

; } - const _items = useMemo(() => { - if (shouldShuffle) { - return [...items].sort(() => Math.random() - 0.5); - } - return items; - }, [items, shouldShuffle]); + // const _items = useMemo(() => { + // if (shouldShuffle) { + // return [...items].sort(() => Math.random() - 0.5); + // } + // return items; + // }, [items, shouldShuffle]); const PAGE_SIZE = 9; @@ -65,7 +65,7 @@ const ListSection = ({ shouldShuffle, items, renderItem }: Props) => { )} `; - return ; + return ; }; export default ListSection; From 096aa1c2949da476b067d520779d09e9e7173d91 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 17 May 2024 13:04:28 +0400 Subject: [PATCH 14/40] fix pots home --- .../components/AllProjects/AllProjects.tsx | 2 +- src/pages/Projects/components/ListSection.tsx | 14 +++++------ src/types.ts | 24 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/pages/Projects/components/AllProjects/AllProjects.tsx b/src/pages/Projects/components/AllProjects/AllProjects.tsx index bc70cc13..617bd242 100644 --- a/src/pages/Projects/components/AllProjects/AllProjects.tsx +++ b/src/pages/Projects/components/AllProjects/AllProjects.tsx @@ -159,7 +159,7 @@ const AllProjects = () => { } + renderItem={(project: Project) => } /> ) : (
No results
diff --git a/src/pages/Projects/components/ListSection.tsx b/src/pages/Projects/components/ListSection.tsx index 41d566ba..69a60f67 100644 --- a/src/pages/Projects/components/ListSection.tsx +++ b/src/pages/Projects/components/ListSection.tsx @@ -23,12 +23,12 @@ const ListSection = ({ shouldShuffle, items, renderItem }: Props) => { return

Loading...

; } - // const _items = useMemo(() => { - // if (shouldShuffle) { - // return [...items].sort(() => Math.random() - 0.5); - // } - // return items; - // }, [items, shouldShuffle]); + const _items = useMemo(() => { + if (shouldShuffle) { + return [...items].sort(() => Math.random() - 0.5); + } + return items; + }, [items, shouldShuffle]); const PAGE_SIZE = 9; @@ -65,7 +65,7 @@ const ListSection = ({ shouldShuffle, items, renderItem }: Props) => { )} `; - return ; + return ; }; export default ListSection; diff --git a/src/types.ts b/src/types.ts index dd6d0a6c..5f9c2a2b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -61,13 +61,13 @@ export type PotDonation = { net_amount: string; message: string; donated_at: number; - project_id: null | "string"; - referrer_id: null | "string"; - referrer_fee: null | "string"; + project_id: null | string; + referrer_id: null | string; + referrer_fee: null | string; protocol_fee: string; matching_pool: boolean; - chef_id: null | "string"; - chef_fee: null | "string"; + chef_id: null | string; + chef_fee: null | string; }; export type FundDonation = { @@ -78,12 +78,12 @@ export type FundDonation = { message: string; donated_at: number; project_id: null; - referrer_id: null | "string"; - referrer_fee: null | "string"; + referrer_id: null | string; + referrer_fee: null | string; protocol_fee: string; matching_pool: true; - chef_id: null | "string"; - chef_fee: null | "string"; + chef_id: null | string; + chef_fee: null | string; }; export type RegistrationStatus = "Approved" | "Rejected" | "Pending" | "Graylisted" | "Blacklisted"; @@ -106,7 +106,7 @@ export type PotApplication = { project_id: string; message: string; status: ApplicationStatus; - submitted_at: 1715764402476; - updated_at: null; - review_notes: null; + submitted_at: number; + updated_at: null | string; + review_notes: null | string; }; From a89118a63f462b19bb7d9580636efaa4a5380b3e Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Sat, 18 May 2024 02:12:50 +0400 Subject: [PATCH 15/40] fix project pots --- src/components/PotCard/PotCard.tsx | 4 +-- src/pages/Project/NavPages/Pots/Pots.tsx | 44 +++++++++++------------ src/pages/Project/NavPages/Pots/styles.ts | 1 + 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/components/PotCard/PotCard.tsx b/src/components/PotCard/PotCard.tsx index 789e19dc..546083d4 100644 --- a/src/components/PotCard/PotCard.tsx +++ b/src/components/PotCard/PotCard.tsx @@ -18,11 +18,10 @@ type Props = { const PotCard = ({ potId }: Props) => { const potConfig: PotDetail = PotSDK.getConfig(potId); - if (!potConfig) return ( - {potConfig == null ? ( + {potConfig === null ? (
) : (
Pot {potId} not found.
@@ -55,6 +54,7 @@ const PotCard = ({ potId }: Props) => { return ( diff --git a/src/pages/Project/NavPages/Pots/Pots.tsx b/src/pages/Project/NavPages/Pots/Pots.tsx index 4c17a375..59b3dab5 100644 --- a/src/pages/Project/NavPages/Pots/Pots.tsx +++ b/src/pages/Project/NavPages/Pots/Pots.tsx @@ -1,40 +1,38 @@ -import { useParams, useState } from "alem"; +import { useEffect, useMemo, useParams, useState } from "alem"; import PotSDK from "@app/SDK/pot"; import PotFactorySDK from "@app/SDK/potfactory"; import PotCard from "@app/components/PotCard/PotCard"; +import ListSection from "@app/pages/Projects/components/ListSection"; import { Container, NoResults } from "./styles"; const Pots = () => { const pots = PotFactorySDK.getPots(); const { projectId } = useParams(); - const [potIds, setPotIds] = useState(null); // ids[] of pots that approved project - const [loading, setLoading] = useState(true); // ids[] of pots that approved project + const POT_STATUS = ["Approved", "pending"]; - const getApprovedApplications = (potId: any) => - PotSDK.asyncGetApprovedApplications(potId) - .then((applications: any) => { - if (applications.some((app: any) => app.project_id === projectId)) { - setPotIds([...(potIds || []), potId]); - } - if (pots[pots.length - 1].id === potId) setLoading(false); - }) - .catch(() => console.log(`Error fetching approved applications for ${potId}`)); + const [potIds, setPotIds] = useState(null); // ids[] of pots that approved project + // const [loading, setLoading] = useState(true); - if (pots && loading) { - pots.forEach((pot: any) => { - getApprovedApplications(pot.id); - }); - } + useEffect(() => { + if (pots && !potIds) { + const applicationsPrmomises = pots.map(({ id }: any) => PotSDK.asyncGetApplicationByProjectId(id, projectId)); + Promise.allSettled(applicationsPrmomises).then((applications: any) => { + const enrolledPots: any = []; + applications.forEach((obj: any, idx: number) => { + if (POT_STATUS.includes(obj.value.status)) { + enrolledPots.push(pots[idx]); + } + }); + setPotIds(enrolledPots); + }); + } + }, [pots]); - return loading ? ( + return potIds === null ? ( "Loading..." ) : potIds.length ? ( - - {potIds.map((potId: string) => ( - - ))} - + } /> ) : (
This project has not participated in any pots yet.
diff --git a/src/pages/Project/NavPages/Pots/styles.ts b/src/pages/Project/NavPages/Pots/styles.ts index d25c0194..3c37b1a0 100644 --- a/src/pages/Project/NavPages/Pots/styles.ts +++ b/src/pages/Project/NavPages/Pots/styles.ts @@ -3,6 +3,7 @@ import styled from "styled-components"; export const Container = styled.div` display: grid; grid-template-columns: repeat(3, 1fr); + gap: 1rem; > div { padding-top: 0rem; } From 588c4e2848182660603af79f235e3dccbe0101ce Mon Sep 17 00:00:00 2001 From: Mohamed ElKhamisy <68287884+M-Rb3@users.noreply.github.com> Date: Sat, 18 May 2024 02:27:33 +0400 Subject: [PATCH 16/40] Revert "Staging => mian" --- src/components/PotCard/PotCard.tsx | 4 +-- src/pages/Project/NavPages/Pots/Pots.tsx | 44 ++++++++++++----------- src/pages/Project/NavPages/Pots/styles.ts | 1 - 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/components/PotCard/PotCard.tsx b/src/components/PotCard/PotCard.tsx index 546083d4..789e19dc 100644 --- a/src/components/PotCard/PotCard.tsx +++ b/src/components/PotCard/PotCard.tsx @@ -18,10 +18,11 @@ type Props = { const PotCard = ({ potId }: Props) => { const potConfig: PotDetail = PotSDK.getConfig(potId); + if (!potConfig) return ( - {potConfig === null ? ( + {potConfig == null ? (
) : (
Pot {potId} not found.
@@ -54,7 +55,6 @@ const PotCard = ({ potId }: Props) => { return ( diff --git a/src/pages/Project/NavPages/Pots/Pots.tsx b/src/pages/Project/NavPages/Pots/Pots.tsx index 59b3dab5..4c17a375 100644 --- a/src/pages/Project/NavPages/Pots/Pots.tsx +++ b/src/pages/Project/NavPages/Pots/Pots.tsx @@ -1,38 +1,40 @@ -import { useEffect, useMemo, useParams, useState } from "alem"; +import { useParams, useState } from "alem"; import PotSDK from "@app/SDK/pot"; import PotFactorySDK from "@app/SDK/potfactory"; import PotCard from "@app/components/PotCard/PotCard"; -import ListSection from "@app/pages/Projects/components/ListSection"; import { Container, NoResults } from "./styles"; const Pots = () => { const pots = PotFactorySDK.getPots(); const { projectId } = useParams(); - const POT_STATUS = ["Approved", "pending"]; - const [potIds, setPotIds] = useState(null); // ids[] of pots that approved project - // const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(true); // ids[] of pots that approved project + + const getApprovedApplications = (potId: any) => + PotSDK.asyncGetApprovedApplications(potId) + .then((applications: any) => { + if (applications.some((app: any) => app.project_id === projectId)) { + setPotIds([...(potIds || []), potId]); + } + if (pots[pots.length - 1].id === potId) setLoading(false); + }) + .catch(() => console.log(`Error fetching approved applications for ${potId}`)); - useEffect(() => { - if (pots && !potIds) { - const applicationsPrmomises = pots.map(({ id }: any) => PotSDK.asyncGetApplicationByProjectId(id, projectId)); - Promise.allSettled(applicationsPrmomises).then((applications: any) => { - const enrolledPots: any = []; - applications.forEach((obj: any, idx: number) => { - if (POT_STATUS.includes(obj.value.status)) { - enrolledPots.push(pots[idx]); - } - }); - setPotIds(enrolledPots); - }); - } - }, [pots]); + if (pots && loading) { + pots.forEach((pot: any) => { + getApprovedApplications(pot.id); + }); + } - return potIds === null ? ( + return loading ? ( "Loading..." ) : potIds.length ? ( - } /> + + {potIds.map((potId: string) => ( + + ))} + ) : (
This project has not participated in any pots yet.
diff --git a/src/pages/Project/NavPages/Pots/styles.ts b/src/pages/Project/NavPages/Pots/styles.ts index 3c37b1a0..d25c0194 100644 --- a/src/pages/Project/NavPages/Pots/styles.ts +++ b/src/pages/Project/NavPages/Pots/styles.ts @@ -3,7 +3,6 @@ import styled from "styled-components"; export const Container = styled.div` display: grid; grid-template-columns: repeat(3, 1fr); - gap: 1rem; > div { padding-top: 0rem; } From 6437bbced1405d918b31e97537fe100c021e4258 Mon Sep 17 00:00:00 2001 From: Mohamed ElKhamisy <68287884+M-Rb3@users.noreply.github.com> Date: Sun, 19 May 2024 15:00:23 +0400 Subject: [PATCH 17/40] Revert "Revert "Staging => mian"" --- src/components/PotCard/PotCard.tsx | 4 +-- src/pages/Project/NavPages/Pots/Pots.tsx | 44 +++++++++++------------ src/pages/Project/NavPages/Pots/styles.ts | 1 + 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/components/PotCard/PotCard.tsx b/src/components/PotCard/PotCard.tsx index 789e19dc..546083d4 100644 --- a/src/components/PotCard/PotCard.tsx +++ b/src/components/PotCard/PotCard.tsx @@ -18,11 +18,10 @@ type Props = { const PotCard = ({ potId }: Props) => { const potConfig: PotDetail = PotSDK.getConfig(potId); - if (!potConfig) return ( - {potConfig == null ? ( + {potConfig === null ? (
) : (
Pot {potId} not found.
@@ -55,6 +54,7 @@ const PotCard = ({ potId }: Props) => { return ( diff --git a/src/pages/Project/NavPages/Pots/Pots.tsx b/src/pages/Project/NavPages/Pots/Pots.tsx index 4c17a375..59b3dab5 100644 --- a/src/pages/Project/NavPages/Pots/Pots.tsx +++ b/src/pages/Project/NavPages/Pots/Pots.tsx @@ -1,40 +1,38 @@ -import { useParams, useState } from "alem"; +import { useEffect, useMemo, useParams, useState } from "alem"; import PotSDK from "@app/SDK/pot"; import PotFactorySDK from "@app/SDK/potfactory"; import PotCard from "@app/components/PotCard/PotCard"; +import ListSection from "@app/pages/Projects/components/ListSection"; import { Container, NoResults } from "./styles"; const Pots = () => { const pots = PotFactorySDK.getPots(); const { projectId } = useParams(); - const [potIds, setPotIds] = useState(null); // ids[] of pots that approved project - const [loading, setLoading] = useState(true); // ids[] of pots that approved project + const POT_STATUS = ["Approved", "pending"]; - const getApprovedApplications = (potId: any) => - PotSDK.asyncGetApprovedApplications(potId) - .then((applications: any) => { - if (applications.some((app: any) => app.project_id === projectId)) { - setPotIds([...(potIds || []), potId]); - } - if (pots[pots.length - 1].id === potId) setLoading(false); - }) - .catch(() => console.log(`Error fetching approved applications for ${potId}`)); + const [potIds, setPotIds] = useState(null); // ids[] of pots that approved project + // const [loading, setLoading] = useState(true); - if (pots && loading) { - pots.forEach((pot: any) => { - getApprovedApplications(pot.id); - }); - } + useEffect(() => { + if (pots && !potIds) { + const applicationsPrmomises = pots.map(({ id }: any) => PotSDK.asyncGetApplicationByProjectId(id, projectId)); + Promise.allSettled(applicationsPrmomises).then((applications: any) => { + const enrolledPots: any = []; + applications.forEach((obj: any, idx: number) => { + if (POT_STATUS.includes(obj.value.status)) { + enrolledPots.push(pots[idx]); + } + }); + setPotIds(enrolledPots); + }); + } + }, [pots]); - return loading ? ( + return potIds === null ? ( "Loading..." ) : potIds.length ? ( - - {potIds.map((potId: string) => ( - - ))} - + } /> ) : (
This project has not participated in any pots yet.
diff --git a/src/pages/Project/NavPages/Pots/styles.ts b/src/pages/Project/NavPages/Pots/styles.ts index d25c0194..3c37b1a0 100644 --- a/src/pages/Project/NavPages/Pots/styles.ts +++ b/src/pages/Project/NavPages/Pots/styles.ts @@ -3,6 +3,7 @@ import styled from "styled-components"; export const Container = styled.div` display: grid; grid-template-columns: repeat(3, 1fr); + gap: 1rem; > div { padding-top: 0rem; } From c9f61158053b83b879738a82db9d05a3ee436306 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Mon, 20 May 2024 06:10:14 +0400 Subject: [PATCH 18/40] fix donation if balance is not fetched --- src/modals/ModalDonation/AmountInput/AmountInput.tsx | 7 ++++++- src/modals/ModalDonation/FormPot/FormPot.tsx | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modals/ModalDonation/AmountInput/AmountInput.tsx b/src/modals/ModalDonation/AmountInput/AmountInput.tsx index 8b1eba77..a93f69ba 100644 --- a/src/modals/ModalDonation/AmountInput/AmountInput.tsx +++ b/src/modals/ModalDonation/AmountInput/AmountInput.tsx @@ -17,10 +17,15 @@ const AmountInput = (props: any) => { }, onChange: ({ value }: any) => { const tokenBalance = parseFloat(getTokenBalance(value)); + updateState({ selectedDenomination: denominationOptions.find((option: any) => option.value === value), amountError: - tokenBalance > parseFloat(amount) ? "" : "You don’t have enough balance to complete this transaction.", + tokenBalance !== null + ? tokenBalance > parseFloat(amount) + ? "" + : "You don’t have enough balance to complete this transaction." + : "", }); }, containerStyles: { diff --git a/src/modals/ModalDonation/FormPot/FormPot.tsx b/src/modals/ModalDonation/FormPot/FormPot.tsx index 87be68de..8efeafdd 100644 --- a/src/modals/ModalDonation/FormPot/FormPot.tsx +++ b/src/modals/ModalDonation/FormPot/FormPot.tsx @@ -53,7 +53,7 @@ const FormPot = ({ if (amount === ".") amount = "0."; updateState({ amount, amountError: "" }); // error if amount is greater than balance - if (amount > ftBalance) { + if (amount > ftBalance && ftBalance) { updateState({ amountError: "You don’t have enough balance to complete this transaction." }); } else if (parseFloat(amount) < 0.1) { updateState({ amountError: "Minimum donation is 0.1 NEAR" }); @@ -87,6 +87,8 @@ const FormPot = ({ let totalAmount = 0; Object.values(updatedProjects).forEach((amount: any) => (totalAmount += parseFloat(amount))); + console.log("ftBalance", ftBalance); + if (totalAmount > ftBalance && ftBalance !== null) { updateState({ amountError: "You don’t have enough balance to complete this transaction." }); } else if (parseFloat(amount) < 0.1 && parseFloat(amount) !== 0) { From 951548f151c921cd8fcc617da7a1060d3f7d5dd0 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 09:47:42 +0400 Subject: [PATCH 19/40] improving the donation modal & projects loading Co-authored-by: Wenderson Pires --- src/Main.tsx | 6 +- src/SDK/pot.js | 4 +- src/components/Card/Card.tsx | 9 +- src/components/PotCard/styles.ts | 2 +- src/hooks/useModals.tsx | 4 +- .../ModalDonation/AmountInput/AmountInput.tsx | 2 +- .../ModalDonation/ConfirmPot/ConfirmPot.tsx | 8 +- src/modals/ModalDonation/index.tsx | 1 - src/modals/ModalSuccess/ModalSuccess.tsx | 4 +- src/pages/Pot/NavPages/Projects/Projects.tsx | 187 +++++----- src/pages/Pot/NavPages/Projects/styles.ts | 13 + src/pages/Pot/components/Header/Header.tsx | 2 +- .../PoolAllocationTable.tsx | 111 +++++- .../PoolAllocationTable/Table/Table.tsx | 1 - .../Table/TableSkeleton.tsx | 29 ++ .../PoolAllocationTable/Table/styles.ts | 71 ++++ src/pages/Profile/components/Tabs.tsx | 6 +- src/services/getPotData.ts | 337 ++++++++++++++++++ src/types.ts | 51 ++- src/utils/calculatePayouts.ts | 7 +- 20 files changed, 701 insertions(+), 154 deletions(-) create mode 100644 src/pages/Pot/components/PoolAllocationTable/Table/TableSkeleton.tsx create mode 100644 src/services/getPotData.ts diff --git a/src/Main.tsx b/src/Main.tsx index 0e9b6428..4d2aaa67 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -1,9 +1,12 @@ -import { ModulesProvider } from "alem"; +import { ModulesProvider, useParams } from "alem"; import Banner from "./components/Banner/Banner"; import Nav from "./components/Nav/Nav"; +import ModalSuccess from "./modals/ModalSuccess/ModalSuccess"; import Routes from "./routes/Routes"; const Main = () => { + const { transactionHashes } = useParams(); + return ( <> @@ -12,6 +15,7 @@ const Main = () => {
+ {transactionHashes && } ); }; diff --git a/src/SDK/pot.js b/src/SDK/pot.js index 5ceb71b9..5a624ce8 100644 --- a/src/SDK/pot.js +++ b/src/SDK/pot.js @@ -29,8 +29,8 @@ const PotSDK = { // TODO: paginate return Near.view(potId, "get_matching_pool_donations", {}); }, - asyncGetMatchingPoolDonations: (potId) => { - return Near.asyncView(potId, "get_matching_pool_donations", {}); + asyncGetMatchingPoolDonations: (potId, args) => { + return Near.asyncView(potId, "get_matching_pool_donations", ...(args || {})); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, "get_public_round_donations", { diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 355a7da3..04059d9c 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -32,7 +32,6 @@ import { } from "./styles"; const Card = (props: any) => { - const [ready, isReady] = useState(false); const { payoutDetails, allowDonate: _allowDonate } = props; const { potId } = useParams(); @@ -100,13 +99,7 @@ const Card = (props: any) => { const tags = getTagsFromSocialProfileData(profile); - useEffect(() => { - if (profile !== null && !ready) { - isReady(true); - } - }, [profile, donationsForProject, tags]); - - if (!ready) return ; + if (profile === null && totalAmountNear === null) return ; return ( <> diff --git a/src/components/PotCard/styles.ts b/src/components/PotCard/styles.ts index 33989401..93855712 100644 --- a/src/components/PotCard/styles.ts +++ b/src/components/PotCard/styles.ts @@ -1,6 +1,6 @@ import styled from "styled-components"; -export const Card = styled.a` +export const Card = styled("Link")` display: flex; flex-direction: column; min-width: 320px; diff --git a/src/hooks/useModals.tsx b/src/hooks/useModals.tsx index 8081051a..4c1b8126 100644 --- a/src/hooks/useModals.tsx +++ b/src/hooks/useModals.tsx @@ -1,4 +1,3 @@ -import { useParams } from "alem"; import DonationModalProvider from "@app/contexts/DonationModalProvider"; import { useDonationModal } from "@app/hooks/useDonationModal"; import ModalDonation from "../modals/ModalDonation"; @@ -12,12 +11,11 @@ import ModalSuccess from "../modals/ModalSuccess/ModalSuccess"; const useModals = () => { DonationModalProvider(); - const { transactionHashes: _transactionHashes } = useParams(); const { successfulDonation, donationModalProps } = useDonationModal(); return () => ( <> - {(successfulDonation || _transactionHashes) && } + {successfulDonation && } {donationModalProps && } ); diff --git a/src/modals/ModalDonation/AmountInput/AmountInput.tsx b/src/modals/ModalDonation/AmountInput/AmountInput.tsx index a93f69ba..a49add35 100644 --- a/src/modals/ModalDonation/AmountInput/AmountInput.tsx +++ b/src/modals/ModalDonation/AmountInput/AmountInput.tsx @@ -49,7 +49,7 @@ const AmountInput = (props: any) => { ); const { value, amount, HandleAmoutChange, donationType, denominationOptions, selectedDenomination } = props; - const _value = value || amount || 0; + const _value = value || amount || ""; return ( diff --git a/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx b/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx index d4253fa4..b666aac5 100644 --- a/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx +++ b/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx @@ -25,7 +25,7 @@ const ConfirmPot = ({ bypassChefFee, updateState, potDetail, - potId, + selectedRound, referrerId, accountId, amount, @@ -62,13 +62,13 @@ const ConfirmPot = ({ const pollIntervalMs = 1000; // const totalPollTimeMs = 60000; // consider adding in to make sure interval doesn't run indefinitely const pollId = setInterval(() => { - PotSDK.asyncGetDonationsForDonor(potId, accountId) + PotSDK.asyncGetDonationsForDonor(selectedRound, accountId) .then((alldonations: any) => { const donations: Record = {}; for (const donation of alldonations) { const { project_id, donated_at_ms, donated_at } = donation; if (projectIds.includes(project_id) && (donated_at_ms || donated_at) > afterTs) { - donations[project_id] = { ...donation, potId }; + donations[project_id] = { ...donation, potId: selectedRound }; } } if (Object.keys(donations).length === projectIds.length) { @@ -128,7 +128,7 @@ const ConfirmPot = ({ if (amount) { transactions.push({ - contractName: potId, + contractName: selectedRound, methodName: "donate", args: { referrer_id: referrerId, diff --git a/src/modals/ModalDonation/index.tsx b/src/modals/ModalDonation/index.tsx index cf515a12..f8f84c42 100644 --- a/src/modals/ModalDonation/index.tsx +++ b/src/modals/ModalDonation/index.tsx @@ -270,7 +270,6 @@ const ModalDonation = () => { {...donationModalProps} {...state} accountId={accountId} - potId={potId} referrerId={referrerId} updateState={State.update} ftBalance={ftBalance} diff --git a/src/modals/ModalSuccess/ModalSuccess.tsx b/src/modals/ModalSuccess/ModalSuccess.tsx index c284c79f..4ba23fce 100644 --- a/src/modals/ModalSuccess/ModalSuccess.tsx +++ b/src/modals/ModalSuccess/ModalSuccess.tsx @@ -48,7 +48,7 @@ const ModalSuccess = () => { }); const onClose = () => { - _setSuccessfulDonation(null); + if (_setSuccessfulDonation) _setSuccessfulDonation(null); const location = getLocation(); delete params.transactionHashes; @@ -135,7 +135,7 @@ const ModalSuccess = () => { : ""; if (recipientId) { - if (methodName === "donate") { + if (methodName === "donate" && (result.project_id || result.recipient_id)) { setSuccessfulDonation((prev: any) => ({ ...prev, [recipientId]: { ...result, potId: receiver_id }, diff --git a/src/pages/Pot/NavPages/Projects/Projects.tsx b/src/pages/Pot/NavPages/Projects/Projects.tsx index d0a275a5..2fcb676c 100644 --- a/src/pages/Pot/NavPages/Projects/Projects.tsx +++ b/src/pages/Pot/NavPages/Projects/Projects.tsx @@ -1,40 +1,50 @@ -import { useState, Social, context, useParams, createDebounce, useEffect } from "alem"; -import PotSDK from "@app/SDK/pot"; +import { useState, Social, context, useParams, createDebounce, useEffect, Storage, promisify } from "alem"; import Card from "@app/components/Card/Card"; -import ListSection from "@app/pages/Projects/components/ListSection"; -import calculatePayouts from "@app/utils/calculatePayouts"; +import { getPotDonations } from "@app/pages/Donor/NavPages/Donations/utils"; +import { getConfig, getDonations, getFlaggedAccounts, getPayout, getPotProjects } from "@app/services/getPotData"; +import { FlaggedAddress, Payout, PotApplication, PotDetail, PotDonation } from "@app/types"; import getTagsFromSocialProfileData from "@app/utils/getTagsFromSocialProfileData"; import getTeamMembersFromSocialProfileData from "@app/utils/getTeamMembersFromSocialProfileData"; -import { Centralized, Container, SearchBar, Title } from "./styles"; +import { Centralized, Container, SearchBar, Title, ProjectsWrapper } from "./styles"; type Props = { potDetail: any; allDonations: any; }; -const Projects = (props: Props) => { - const [filteredProjects, setFilteredProjects] = useState([]); - const [projects, setProjects] = useState(null); - const [flaggedAddresses, setFlaggedAddresses] = useState(null); - const [payouts, setPayouts] = useState(null); +type ProjectsState = { + potDetail: PotDetail | null; + donations: PotDonation[] | null; + filteredProjects: PotApplication[]; + projects: PotApplication[] | null; + flaggedAddresses: FlaggedAddress[] | null; + payouts: Record | null; +}; - // get projects +const Projects = (props: Props) => { const { potId } = useParams(); - const { potDetail, allDonations } = props; - - if (!projects) { - PotSDK.asyncGetApprovedApplications(potId) - .then((projects: any) => { - setProjects(projects); - setFilteredProjects(projects); - }) - .catch((err: any) => { - console.log("error fetching projects ", err); - setProjects([]); - setFilteredProjects([]); - }); - } + const [state, setState] = useState({ + filteredProjects: [], + donations: null, + projects: null, + flaggedAddresses: null, + payouts: null, + potDetail: null, + }); + + // const [projects, setProjects] = useState(null); + // const [filteredProjects, setFilteredProjects] = useState([]); + // const [donations, setDonations] = useState(null); + // const [flaggedAddresses, setFlaggedAddresses] = useState(null); + // const [payouts, setPayouts] = useState | null>(null); + // const [potDetail, setPotDetail] = useState(null); + + const updateState = (newValue: Partial) => { + setState((prevState) => ({ ...prevState, ...newValue })); + }; + + const { filteredProjects, flaggedAddresses, payouts, projects, potDetail, donations } = state; const Loading = () => ( @@ -42,44 +52,50 @@ const Projects = (props: Props) => { ); - if (!projects) return ; - - const { public_round_start_ms, public_round_end_ms, referral_fee_public_round_basis_points } = potDetail; - - const now = Date.now(); - const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; + // get projects + useEffect(() => { + if (!projects) + getPotProjects({ + potId, + updateState, + isApprpved: true, + }); + if (!potDetail) + getConfig({ + potId, + updateState, + }); + }, []); - if (!flaggedAddresses) { - PotSDK.getFlaggedAccounts(potDetail, potId) - .then((data) => { - const listOfFlagged: any = []; - data.forEach((adminFlaggedAcc: any) => { - const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); - listOfFlagged.push(...addresses); + useEffect(() => { + if (potDetail) { + if (!flaggedAddresses) + getFlaggedAccounts({ + potId, + potDetail, + type: "list", + updateState, }); - setFlaggedAddresses(listOfFlagged); - }) - .catch((err) => console.log("error getting the flagged accounts ", err)); - } + if (!donations) + getDonations({ + potId, + potDetail, + updateState, + }); + } + }, [potDetail]); useEffect(() => { - if (!payouts) { - if (allDonations.length && flaggedAddresses) - calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses) - .then((payouts: any) => { - setPayouts(payouts ?? []); - }) - .catch((err) => { - console.log("error while calculating payouts ", err); - setPayouts([]); - }); - else if (allDonations.length === 0 && flaggedAddresses?.length === 0) { - setPayouts([]); - } - } - }, [allDonations, flaggedAddresses]); + if (potDetail && flaggedAddresses && donations) + getPayout({ allDonations: donations, flaggedAddresses, potDetail, potId, updateState }); + }, [potDetail, flaggedAddresses, donations]); - if (!flaggedAddresses || !payouts) return ; + if (!projects || !potDetail) return ; + + const { public_round_start_ms, public_round_end_ms } = potDetail; + + const now = Date.now(); + const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; const searchByWords = (searchTerm: string) => { if (projects.length) { @@ -96,7 +112,9 @@ const Projects = (props: Props) => { ]; return fields.some((item) => (item || "").toLowerCase().includes(searchTerm.toLowerCase())); }); - setFilteredProjects(updatedProjects); + updateState({ + filteredProjects: updatedProjects, + }); } }; @@ -124,39 +142,24 @@ const Projects = (props: Props) => { /> {filteredProjects.length > 0 ? ( - { - return ( - - ); - }} - /> + + {filteredProjects.map((project: PotApplication) => ( + + ))} + ) : (
No projects
)} diff --git a/src/pages/Pot/NavPages/Projects/styles.ts b/src/pages/Pot/NavPages/Projects/styles.ts index cbc1849f..2666776b 100644 --- a/src/pages/Pot/NavPages/Projects/styles.ts +++ b/src/pages/Pot/NavPages/Projects/styles.ts @@ -49,6 +49,19 @@ export const SearchBar = styled.div` } `; +export const ProjectsWrapper = styled.div` + margin-top: 2rem; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + @media only screen and (max-width: 1024px) { + grid-template-columns: repeat(2, 1fr); + } + @media only screen and (max-width: 768px) { + grid-template-columns: repeat(1, 1fr); + } +`; + export const Centralized = styled.div` display: flex; justify-content: center; diff --git a/src/pages/Pot/components/Header/Header.tsx b/src/pages/Pot/components/Header/Header.tsx index 146ddd5b..4b8d2f17 100644 --- a/src/pages/Pot/components/Header/Header.tsx +++ b/src/pages/Pot/components/Header/Header.tsx @@ -88,7 +88,7 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation const successVal = trxs[0].body.result.status?.SuccessValue; const result = JSON.parse(Buffer.from(successVal, "base64").toString("utf-8")); // atob not working - if (methodName === "donate" && receiver_id === potId && result) { + if (methodName === "donate" && receiver_id === potId && result.matching_pool) { setFundDonation({ ...result, potId, diff --git a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx index f6692ef6..68f403bf 100644 --- a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx +++ b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx @@ -1,12 +1,17 @@ -import { useState, useParams, Big } from "alem"; +import { useState, useParams, Big, Social } from "alem"; import PotSDK from "@app/SDK/pot"; +import Image from "@app/components/mob.near/Image"; import constants from "@app/constants"; -import { PotDetail } from "@app/types"; +import { PotDetail, PotDonation } from "@app/types"; import _address from "@app/utils/_address"; import calculatePayouts from "@app/utils/calculatePayouts"; +import formatWithCommas from "@app/utils/formatWithCommas"; +import hrefWithParams from "@app/utils/hrefWithParams"; +import nearToUsd from "@app/utils/nearToUsd"; import nearToUsdWithFallback from "@app/utils/nearToUsdWithFallback"; import yoctosToUsdWithFallback from "@app/utils/yoctosToUsdWithFallback"; -import Table from "./Table/Table"; +import TableSkeleton from "./Table/TableSkeleton"; +import { Container, Row } from "./Table/styles"; type Props = { potDetail: PotDetail; @@ -16,11 +21,13 @@ type Props = { const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { const { SUPPORTED_FTS } = constants; - const { base_currency, total_public_donations, matching_pool_balance, public_donations_count } = potDetail; + const { total_public_donations, matching_pool_balance, public_donations_count } = potDetail; const [projectsId, setProjectsId] = useState(null); const [allPayouts, setAllPayouts] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); + const [sponsorshipDonations, setSponsorshipDonations] = useState(null); + const [usdToggle, setUsdToggle] = useState(false); const { potId } = useParams(); @@ -30,9 +37,12 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { }); } - let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); - - if (sponsorshipDonations) sponsorshipDonations.sort((a: any, b: any) => b.net_amount - a.net_amount); + if (potDetail && public_donations_count === 0) { + PotSDK.asyncGetMatchingPoolDonations(potId).then((sponsorshipDonations: PotDonation[]) => { + sponsorshipDonations.sort((a: any, b: any) => b.net_amount - a.net_amount); + setSponsorshipDonations(sponsorshipDonations); + }); + } const calcMatchedAmount = (donations: any) => { if (donations) { @@ -40,7 +50,7 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { donations?.forEach((donation: any) => { total = total.plus(Big(donation.net_amount)); }); - const amount = SUPPORTED_FTS[base_currency.toUpperCase() || "NEAR"].fromIndivisible(total.toString()); + const amount = SUPPORTED_FTS["NEAR"].fromIndivisible(total.toString()); return amount; } }; @@ -49,7 +59,7 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { const donorsCount = uniqueDonorIds.size; - if (!flaggedAddresses) { + if (!flaggedAddresses && allDonations) { PotSDK.getFlaggedAccounts(potDetail, potId) .then((data) => { if (data) { @@ -97,17 +107,82 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { } } - return allPayouts?.length > 0 ? ( - - ) : sponsorshipDonations.length > 0 ? ( + const Table = ({ donations, totalAmount, totalUniqueDonors, title }: any) => { + return ( + +
+ {totalAmount} + raised from + {totalUniqueDonors} + {title === "sponsors" ? "sponsors" : "donors"} +
+
+
Top {title}
+
(nearToUsd ? setUsdToggle(!usdToggle) : "")} + > + {nearToUsd && ( + + + + )} + {usdToggle ? "USD" : "NEAR"} +
+
+ {donations.map(({ projectId, donor_id, matchingAmount, net_amount }: any, idx: number) => { + const id = donor_id || projectId; + const nearAmount = formatWithCommas(SUPPORTED_FTS["NEAR"].fromIndivisible(net_amount || matchingAmount)); + + const profile = Social.getr(`${id}/profile`); + const matchedAmout = usdToggle ? yoctosToUsdWithFallback(matchingAmount || net_amount, true) : nearAmount; + + const url = projectId ? `?tab=project&projectId=${projectId}` : `?tab=profile&accountId=${donor_id}`; + return ( + +
#{idx + 1}
+ + + {_address(profile?.name || id, 15)} + +
+ {matchedAmout} {usdToggle ? " " : "N"} +
+
+ ); + })} +
+ ); + }; + + return public_donations_count > 0 ? ( + allPayouts !== null ? ( +
+ ) : ( + + ) + ) : sponsorshipDonations === null ? ( + + ) : sponsorshipDonations?.length > 0 ? (
obj.donor_id)).size} donations={sponsorshipDonations.slice(0, 5)} /> diff --git a/src/pages/Pot/components/PoolAllocationTable/Table/Table.tsx b/src/pages/Pot/components/PoolAllocationTable/Table/Table.tsx index bf1abbc8..8b7688e0 100644 --- a/src/pages/Pot/components/PoolAllocationTable/Table/Table.tsx +++ b/src/pages/Pot/components/PoolAllocationTable/Table/Table.tsx @@ -1,6 +1,5 @@ import { Social, useState } from "alem"; import Image from "@app/components/mob.near/Image"; -import ProfileImage from "@app/components/mob.near/ProfileImage"; import constants from "@app/constants"; import _address from "@app/utils/_address"; import formatWithCommas from "@app/utils/formatWithCommas"; diff --git a/src/pages/Pot/components/PoolAllocationTable/Table/TableSkeleton.tsx b/src/pages/Pot/components/PoolAllocationTable/Table/TableSkeleton.tsx new file mode 100644 index 00000000..3f40a694 --- /dev/null +++ b/src/pages/Pot/components/PoolAllocationTable/Table/TableSkeleton.tsx @@ -0,0 +1,29 @@ +import { + SkeletonContainer, + SkeletonHeader, + SkeletonRowItem, + SkeletonAddress, + SkeleteonProfile, + SkeletonName, + SkeletonAmount, +} from "./styles"; + +const TableSkeleton = () => { + return ( + + + {new Array(5).fill(0).map((_, idx) => ( + +
#{idx + 1}
+ + + + + +
+ ))} +
+ ); +}; + +export default TableSkeleton; diff --git a/src/pages/Pot/components/PoolAllocationTable/Table/styles.ts b/src/pages/Pot/components/PoolAllocationTable/Table/styles.ts index e4fce641..6063d1e6 100644 --- a/src/pages/Pot/components/PoolAllocationTable/Table/styles.ts +++ b/src/pages/Pot/components/PoolAllocationTable/Table/styles.ts @@ -73,3 +73,74 @@ export const Row = styled.div` display: flex !important; } `; +// skeleton + +const loadingSkeleton = styled.keyframes` + 0% { + opacity: 1; + } + 50% { + opacity: 0.4; + } + 100% { + opacity: 1; + } +`; + +export const SkeletonContainer = styled.div` + display: flex; + flex-direction: column; + height: 360px; + width: 100%; + max-width: 514px; + border-radius: 12px; + border-width: 1px 1px 2px; + border-style: solid; + border-color: rgb(41, 41, 41); + animation-name: ${loadingSkeleton}; + animation-duration: 1s; + animation-iteration-count: infinite; + overflow: hidden; +`; +export const SkeletonHeader = styled.div` + height: 90px; + width: 100%; + background: #eee; +`; + +export const SkeletonRowItem = styled.div` + display: flex; + align-items: center; + width: 100%; + padding: 1rem; + gap: 8px; + border-bottom: 1px solid #c7c7c7; + &:last-of-type { + border-bottom: none; + } +`; + +export const SkeletonAddress = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-left: 24px; + flex: 1; +`; + +export const SkeleteonProfile = styled.div` + width: 18px; + height: 18px; + border-radius: 50%; + background: #eee; +`; +export const SkeletonName = styled.div` + width: 100px; + height: 14px; + background: #eee; +`; +export const SkeletonAmount = styled.div` + width: 57px; + height: 14px; + background: #eee; +`; diff --git a/src/pages/Profile/components/Tabs.tsx b/src/pages/Profile/components/Tabs.tsx index 6326d847..e1440fed 100644 --- a/src/pages/Profile/components/Tabs.tsx +++ b/src/pages/Profile/components/Tabs.tsx @@ -48,14 +48,16 @@ const Tabs = ({ navOptions, nav }: Props) => { } `; + const Link = styled("Link")``; + return ( {navOptions.map((option: any) => { const selected = option.id == getSelectedNavOption().id; return option.label ? ( - + {option.label} - + ) : ( "" ); diff --git a/src/services/getPotData.ts b/src/services/getPotData.ts new file mode 100644 index 00000000..e23c9adf --- /dev/null +++ b/src/services/getPotData.ts @@ -0,0 +1,337 @@ +import { Storage } from "alem"; +import PotSDK from "@app/SDK/pot"; +import { FlaggedAddress, Payout, PotApplication, PotDetail, PotDonation } from "@app/types"; +import calculatePayouts from "@app/utils/calculatePayouts"; + +type UpdateState = (newValues: Partial) => void; + +type GetPayoutProps = { + potId: string; + allDonations: PotDonation[]; + flaggedAddresses: FlaggedAddress[]; + potDetail: PotDetail; + updateState: UpdateState; +}; + +export type ProjectsState = { + potDetail?: PotDetail; + donations?: PotDonation[]; + filteredProjects?: PotApplication[]; + projects?: PotApplication[] | null; + flaggedAddresses?: FlaggedAddress[] | null; + payouts?: Record | null; +}; + +function isEqual(obj1: any, obj2: any) { + // Check if both are the same reference + if (obj1 === obj2) return true; + + // Check if both are null or undefined + if (obj1 == null || obj2 == null) return false; + + // Check if both are not objects (e.g., numbers, strings, etc.) + if (typeof obj1 !== "object" || typeof obj2 !== "object") return false; + + // Get the keys of both objects + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + // Check if both objects have the same number of keys + if (keys1.length !== keys2.length) return false; + + // Check if all keys and their values are the same + for (const key of keys1) { + if (!keys2.includes(key) || !isEqual(obj1[key], obj2[key])) { + return false; + } + } + + return true; +} + +const getPotData = (potId: string, property: string) => Storage.get(`${potId}-${property}`); + +const setPotData = (potId: string, property: string, value: any) => Storage.set(`${potId}-${property}`, value); + +// Get pot detail +export const getConfig = ({ potId, updateState }: { potId: string; updateState: UpdateState }) => { + const potData = getPotData(potId, "config"); + const potDetail = potData?.potDetail; + + if (potDetail) + updateState({ + potDetail, + }); + + PotSDK.asyncGetConfig(potId) + .then((currentPotDetail: PotDetail) => { + if (isEqual(potDetail, currentPotDetail)) return; + else { + setPotData(potId, "config", currentPotDetail); + updateState({ + potDetail: currentPotDetail, + }); + } + }) + .catch((err: unknown) => { + console.log("error getthing pot config ", err); + }); +}; + +// get pot applications +export function getPotProjects({ + potId, + updateState, + isApprpved, +}: { + potId: string; + updateState: UpdateState; + isApprpved: boolean; +}) { + // get projects from local storage + const potData = getPotData(potId, "projects"); + const savedProject = potData.projects || []; + + if (savedProject) { + // if storage project exist show it + let allProjects = savedProject; + // Check if only approved applications is requested + if (isApprpved) allProjects = allProjects.filter((project: PotApplication) => project.status === "Approved"); + updateState({ + projects: allProjects, + filteredProjects: allProjects, + }); + } + // check the current projects + PotSDK.asyncGetApprovedApplications(potId) + .then((projects: PotApplication[]) => { + if (projects.length === potData.projects?.length) return; + setPotData(potId, "projects", projects); + let allProjects = projects; + if (isApprpved) allProjects = allProjects.filter((project: PotApplication) => project.status === "Approved"); + updateState({ + projects, + filteredProjects: allProjects, + }); + }) + .catch((error: unknown) => { + updateState({ + projects: [], + filteredProjects: [], + }); + }); +} + +// get pot payouts +export const getPayout = ({ potId, allDonations, flaggedAddresses, potDetail, updateState }: GetPayoutProps) => { + const potData = getPotData(potId, "payouts"); + const payouts = potData.payouts; + if (payouts) + updateState({ + payouts, + }); + + if (flaggedAddresses) { + if (potDetail.payouts) { + if (isEqual(potDetail.payouts, payouts)) return; + else { + setPotData(potId, "payouts", payouts); + updateState({ + payouts, + }); + } + } else if (allDonations.length && flaggedAddresses) + calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((currentPayouts) => { + if (isEqual(payouts, currentPayouts)) return; + else { + setPotData(potId, "payouts", currentPayouts); + updateState({ + payouts: currentPayouts, + }); + } + }); + else if (allDonations?.length === 0 && flaggedAddresses?.length === 0) { + updateState({ + payouts: {}, + }); + } + } +}; + +// get matched donations +export const asyncGetPublicDonations = (potDetail: PotDetail, potId: string) => { + const limit = 480; // number of donations to fetch per req + + const donationsCount = potDetail.public_donations_count; + const paginations = [...Array(Math.ceil(donationsCount / limit)).keys()]; + + try { + const allDonations = paginations.map((index) => + PotSDK.asyncGetPublicRoundDonations(potId, { + from_index: index * limit, + limit: limit, + }), + ); + + return Promise.all(allDonations); + } catch (error) { + console.error(`error getting public donations`, error); + return Promise.all([]); + } +}; + +export const getDonations = ({ + potId, + potDetail, + updateState, +}: { + potId: string; + potDetail: PotDetail; + updateState: (newValues: Partial) => void; +}) => { + const potData = getPotData(potId, "donations"); + const donations = potData.donations || []; + + if (donations) + updateState({ + donations, + }); + + if (potDetail.public_donations_count !== donations.length) { + asyncGetPublicDonations(potDetail, potId).then((paginatedDonations) => { + const currentDonations = paginatedDonations ? paginatedDonations.flat() : []; + setPotData(potId, "donations", currentDonations); + updateState({ + donations: currentDonations, + }); + }); + } +}; + +const getListOfFlagged = (flaggedAddresses: FlaggedAddress[]) => { + const listOfFlagged: any = []; + flaggedAddresses.forEach((adminFlaggedAcc: any) => { + const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); + listOfFlagged.push(...addresses); + }); + return listOfFlagged; +}; + +export const getFlaggedAccounts = ({ + potId, + potDetail, + updateState, + type, +}: { + potId: string; + potDetail: PotDetail; + updateState: (newValues: Partial) => void; + type: "list" | "obj"; +}) => { + const potData = getPotData(potId, "flaggedAccounts"); + let flaggedAddresses = potData.flaggedAddresses || []; + + if (type === "list") { + flaggedAddresses = getListOfFlagged(flaggedAddresses); + } + updateState({ + flaggedAddresses, + }); + + PotSDK.getFlaggedAccounts(potDetail) + .then((data) => { + if (type === "list") { + const liftOfFlagged = getListOfFlagged(data); + if (liftOfFlagged.length === flaggedAddresses.length) return; + else { + setPotData(potId, "flaggedAccounts", data); + updateState({ + flaggedAddresses: data, + }); + } + } else { + const isNotEqual = data.some((adminFlaggedAcc: FlaggedAddress, idx: string) => { + if (adminFlaggedAcc?.potFlaggedAcc?.length === adminFlaggedAcc?.potFlaggedAcc?.length) return false; + else return true; + }); + + if (isNotEqual) { + setPotData(potId, "flaggedAccounts", data); + updateState({ + flaggedAddresses: data, + }); + } + } + }) + .catch((err) => console.log("error getting the flagged accounts ", err)); +}; + +// const allDonations = allDonationsPaginated ? allDonationsPaginated.flat() : null; + +// const potOptions = { +// potDetail: ({ potId }: { potId: string }) => PotSDK.asyncGetConfig(potId), +// projects: ({ potId }: { potId: string }) => PotSDK.asyncGetApprovedApplications(potId), +// sponsorshipDonations: ({ potId }: { potId: string }) => PotSDK.asyncGetMatchingPoolDonations(potId), +// // need potDetail, flaggedAddresses, potDetail +// payout: ({ allDonations, flaggedAddresses, potDetail }: GetPayoutProps) => +// getPayout({ +// allDonations, +// flaggedAddresses, +// potDetail, +// }), +// // need potDetail +// flaggedAddresses: ({ potId, potDetail }: { potId: string; potDetail: PotDetail }) => +// PotSDK.getFlaggedAccounts(potDetail, potId), +// donations: ({ potId, potDetail }: { potId: string; potDetail: PotDetail }) => +// asyncGetPublicDonations(potDetail, potId), +// }; + +// type PotDataOptions = "potDetail" | "payout" | "flaggedAddresses" | "projects" | "donations" | "sponsorshipDonations"; + +// export function getFlaggedAddresses({ +// potId, +// optionsHandler, +// updateState, +// }: { +// potId: string; +// optionsHandler: Record any>; +// updateState: (newValues: Partial) => void; +// }) { +// // get pot Data from local storage +// const potData: ProjectsState = JSON.parse(Storage.get(potId) || "{}"); + +// const potDetail = potData.potDetail; +// const donations = potData.donations; + +// const options = Object.keys(optionsHandler) as PotDataOptions[]; + +// const needsPotDetail = ["payout", "donations", "flaggedAddresses"]; +// const needsAll = "payout"; +// // what is needed before fetching +// let fetchFirst: Record = {}; + +// options.forEach((option) => { +// if (needsPotDetail.includes(option)) fetchFirst["potDetail"] = ""; +// if (needsAll === option) { +// fetchFirst = { +// potDetail: "", +// donations: "", +// flaggedAddresses: "", +// }; +// } +// }); + +// if( +// fetchFirst["potDetail"] && +// ){ +// PotSDK.asyncGetConfig(potId).then((potDetail:PotDetail)=>{ + +// }) +// } + +// const promises = {}; +// const results = {}; +// options.forEach((option) => { +// const result = potOptions[option]({}); +// }); +// } diff --git a/src/types.ts b/src/types.ts index 5f9c2a2b..fc31d0ed 100644 --- a/src/types.ts +++ b/src/types.ts @@ -54,22 +54,6 @@ export type Pot = { deployed_at_ms: number; }; -export type PotDonation = { - id: string; - donor_id: string; - total_amount: string; - net_amount: string; - message: string; - donated_at: number; - project_id: null | string; - referrer_id: null | string; - referrer_fee: null | string; - protocol_fee: string; - matching_pool: boolean; - chef_id: null | string; - chef_fee: null | string; -}; - export type FundDonation = { id: string; donor_id: string; @@ -110,3 +94,38 @@ export type PotApplication = { updated_at: null | string; review_notes: null | string; }; + +export interface PotDonation { + id: string; + donor_id: string; + total_amount: string; + net_amount: string; + message: string; + donated_at: number; + project_id: null | string; + referrer_id: null | string; + referrer_fee: null | string; + protocol_fee: string; + matching_pool: boolean; + chef_id: null | string; + chef_fee: null | string; +} + +export enum PotAdminRoles { + Admin = "admin", + Chef = "chef", + Wwner = "owner", +} + +export type FlaggedAddress = { + flaggedBy: string; + role: PotAdminRoles; + potFlaggedAcc: "string"; +}; + +export interface Payout { + id: string; + project_id: string; + amount: string; + paid_at: number; +} diff --git a/src/utils/calculatePayouts.ts b/src/utils/calculatePayouts.ts index 73b38fa8..da1d2f1e 100644 --- a/src/utils/calculatePayouts.ts +++ b/src/utils/calculatePayouts.ts @@ -1,7 +1,12 @@ import { Big, Near } from "alem"; import constants from "@app/constants"; +import { Payout } from "@app/types"; -const calculatePayouts = (allPotDonations: any, totalMatchingPool: any, blacklistedAccounts: any) => { +const calculatePayouts = ( + allPotDonations: any, + totalMatchingPool: any, + blacklistedAccounts: any, +): Promise> => { const { NADABOT_CONTRACT_ID } = constants; // * QF/CLR logic taken from https://github.com/gitcoinco/quadratic-funding/blob/master/quadratic-funding/clr.py * From 35103080e4347088d039ad0b6bf819a97e9d602a Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 09:52:36 +0400 Subject: [PATCH 20/40] fix challange button --- src/pages/Pot/components/ChallengeModal/ChallengeModal.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Pot/components/ChallengeModal/ChallengeModal.tsx b/src/pages/Pot/components/ChallengeModal/ChallengeModal.tsx index 44648565..a8300b54 100644 --- a/src/pages/Pot/components/ChallengeModal/ChallengeModal.tsx +++ b/src/pages/Pot/components/ChallengeModal/ChallengeModal.tsx @@ -24,13 +24,12 @@ const ChallengeModal = ({ onClose, existingChallengeForUser }: any) => { const { challengeReason, challengeReasonError } = state; const handleCancelChallenge = () => { - onClose(); State.update({ challengeReason: "", challengeReasonError: "" }); + onClose(); }; const handleSubmitChallenge = () => { PotSDK.challengePayouts(potId, challengeReason); - onClose(); }; const MAX_CHALLENGE_TEXT_LENGTH = 1000; From d85d7ef218518eb3b7083aca2a076ada303c9241 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 15:25:48 +0400 Subject: [PATCH 21/40] update pot header table and projects --- src/SDK/pot.js | 2 +- src/components/Card/Card.tsx | 10 +- src/pages/Pot/NavPages/Projects/Projects.tsx | 119 +++++---- .../PoolAllocationTable.tsx | 140 +++++------ src/pages/Projects/components/ListSection.tsx | 3 +- src/services/getPotData.ts | 238 ++++++++---------- 6 files changed, 229 insertions(+), 283 deletions(-) diff --git a/src/SDK/pot.js b/src/SDK/pot.js index 5a624ce8..e28d4e87 100644 --- a/src/SDK/pot.js +++ b/src/SDK/pot.js @@ -30,7 +30,7 @@ const PotSDK = { return Near.view(potId, "get_matching_pool_donations", {}); }, asyncGetMatchingPoolDonations: (potId, args) => { - return Near.asyncView(potId, "get_matching_pool_donations", ...(args || {})); + return Near.asyncView(potId, "get_matching_pool_donations", args || {}); }, getPublicRoundDonations: (potId, args) => { return Near.view(potId, "get_public_round_donations", { diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 04059d9c..354d5a6f 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -32,9 +32,7 @@ import { } from "./styles"; const Card = (props: any) => { - const { payoutDetails, allowDonate: _allowDonate } = props; - const { potId } = useParams(); - + const { payoutDetails, allowDonate: _allowDonate, potId } = props; // Start Modals provider const Modals = useModals(); const { setDonationModalProps } = useDonationModal(); @@ -55,7 +53,7 @@ const Card = (props: any) => { ? DonateSDK.getDonationsForRecipient(projectId) : []; - const totalAmountNear = useMemo(() => { + const getTotalAmountNear = () => { if (payoutDetails) return payoutDetails.totalAmount; if (!donationsForProject) return "0"; let totalDonationAmountNear = new Big(0); @@ -65,7 +63,9 @@ const Card = (props: any) => { } } return totalDonationAmountNear.toString(); - }, [donationsForProject, payoutDetails]); + }; + + const totalAmountNear = getTotalAmountNear(); const getImageSrc = (image: any) => { const defaultImageUrl = "https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci"; diff --git a/src/pages/Pot/NavPages/Projects/Projects.tsx b/src/pages/Pot/NavPages/Projects/Projects.tsx index 2fcb676c..9fd5f248 100644 --- a/src/pages/Pot/NavPages/Projects/Projects.tsx +++ b/src/pages/Pot/NavPages/Projects/Projects.tsx @@ -1,50 +1,26 @@ -import { useState, Social, context, useParams, createDebounce, useEffect, Storage, promisify } from "alem"; +import { useState, Social, context, useParams, createDebounce, useEffect } from "alem"; import Card from "@app/components/Card/Card"; -import { getPotDonations } from "@app/pages/Donor/NavPages/Donations/utils"; +import ListSection from "@app/pages/Projects/components/ListSection"; import { getConfig, getDonations, getFlaggedAccounts, getPayout, getPotProjects } from "@app/services/getPotData"; import { FlaggedAddress, Payout, PotApplication, PotDetail, PotDonation } from "@app/types"; import getTagsFromSocialProfileData from "@app/utils/getTagsFromSocialProfileData"; import getTeamMembersFromSocialProfileData from "@app/utils/getTeamMembersFromSocialProfileData"; -import { Centralized, Container, SearchBar, Title, ProjectsWrapper } from "./styles"; +import { Centralized, Container, SearchBar, Title } from "./styles"; type Props = { potDetail: any; allDonations: any; }; -type ProjectsState = { - potDetail: PotDetail | null; - donations: PotDonation[] | null; - filteredProjects: PotApplication[]; - projects: PotApplication[] | null; - flaggedAddresses: FlaggedAddress[] | null; - payouts: Record | null; -}; - const Projects = (props: Props) => { const { potId } = useParams(); - const [state, setState] = useState({ - filteredProjects: [], - donations: null, - projects: null, - flaggedAddresses: null, - payouts: null, - potDetail: null, - }); - - // const [projects, setProjects] = useState(null); - // const [filteredProjects, setFilteredProjects] = useState([]); - // const [donations, setDonations] = useState(null); - // const [flaggedAddresses, setFlaggedAddresses] = useState(null); - // const [payouts, setPayouts] = useState | null>(null); - // const [potDetail, setPotDetail] = useState(null); - - const updateState = (newValue: Partial) => { - setState((prevState) => ({ ...prevState, ...newValue })); - }; - - const { filteredProjects, flaggedAddresses, payouts, projects, potDetail, donations } = state; + const [projects, setProjects] = useState(null); + const [filteredProjects, setFilteredProjects] = useState([]); + const [donations, setDonations] = useState(null); + const [flaggedAddresses, setFlaggedAddresses] = useState(null); + const [payouts, setPayouts] = useState | null>(null); + const [potDetail, setPotDetail] = useState(null); const Loading = () => ( @@ -57,13 +33,16 @@ const Projects = (props: Props) => { if (!projects) getPotProjects({ potId, - updateState, + updateState: (projects: PotApplication[]) => { + setFilteredProjects(projects); + setProjects(projects); + }, isApprpved: true, }); if (!potDetail) getConfig({ potId, - updateState, + updateState: setPotDetail, }); }, []); @@ -74,20 +53,28 @@ const Projects = (props: Props) => { potId, potDetail, type: "list", - updateState, + updateState: setFlaggedAddresses, }); if (!donations) getDonations({ potId, potDetail, - updateState, + updateState: setDonations, }); } }, [potDetail]); useEffect(() => { - if (potDetail && flaggedAddresses && donations) - getPayout({ allDonations: donations, flaggedAddresses, potDetail, potId, updateState }); + if (potDetail && flaggedAddresses && donations) { + getPayout({ + allDonations: donations, + flaggedAddresses, + potDetail, + potId, + withTotalAmount: true, + updateState: setPayouts, + }); + } }, [potDetail, flaggedAddresses, donations]); if (!projects || !potDetail) return ; @@ -112,9 +99,7 @@ const Projects = (props: Props) => { ]; return fields.some((item) => (item || "").toLowerCase().includes(searchTerm.toLowerCase())); }); - updateState({ - filteredProjects: updatedProjects, - }); + setFilteredProjects(updatedProjects); } }; @@ -142,24 +127,38 @@ const Projects = (props: Props) => { /> {filteredProjects.length > 0 ? ( - - {filteredProjects.map((project: PotApplication) => ( - - ))} - + { + return ( + + ); + }} + /> ) : (
No projects
)} diff --git a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx index 68f403bf..f10edeb6 100644 --- a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx +++ b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx @@ -1,10 +1,9 @@ -import { useState, useParams, Big, Social } from "alem"; -import PotSDK from "@app/SDK/pot"; +import { useState, useParams, Big, Social, useEffect } from "alem"; import Image from "@app/components/mob.near/Image"; import constants from "@app/constants"; -import { PotDetail, PotDonation } from "@app/types"; +import { getConfig, getDonations, getFlaggedAccounts, getPayout, getSponsorships } from "@app/services/getPotData"; +import { FlaggedAddress, Payout, PotDetail, PotDonation } from "@app/types"; import _address from "@app/utils/_address"; -import calculatePayouts from "@app/utils/calculatePayouts"; import formatWithCommas from "@app/utils/formatWithCommas"; import hrefWithParams from "@app/utils/hrefWithParams"; import nearToUsd from "@app/utils/nearToUsd"; @@ -13,36 +12,65 @@ import yoctosToUsdWithFallback from "@app/utils/yoctosToUsdWithFallback"; import TableSkeleton from "./Table/TableSkeleton"; import { Container, Row } from "./Table/styles"; -type Props = { - potDetail: PotDetail; - allDonations: any; -}; - -const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { +const PoolAllocationTable = () => { const { SUPPORTED_FTS } = constants; - const { total_public_donations, matching_pool_balance, public_donations_count } = potDetail; + const { potId } = useParams(); - const [projectsId, setProjectsId] = useState(null); const [allPayouts, setAllPayouts] = useState(null); - const [flaggedAddresses, setFlaggedAddresses] = useState(null); const [sponsorshipDonations, setSponsorshipDonations] = useState(null); const [usdToggle, setUsdToggle] = useState(false); + const [allDonations, setDonations] = useState(null); + const [flaggedAddresses, setFlaggedAddresses] = useState(null); + const [potDetail, setPotDetail] = useState(null); + + useEffect(() => { + if (!sponsorshipDonations) + getSponsorships({ + potId, + updateState: setSponsorshipDonations, + }); + if (!potDetail) + getConfig({ + potId, + updateState: setPotDetail, + }); + }, []); + + useEffect(() => { + if (potDetail) { + if (!flaggedAddresses) + getFlaggedAccounts({ + potId, + potDetail, + type: "list", + updateState: setFlaggedAddresses, + }); + if (!allDonations) + getDonations({ + potId, + potDetail, + updateState: setDonations, + }); + } + }, [potDetail]); + + useEffect(() => { + if (potDetail && flaggedAddresses && allDonations && !allPayouts) { + getPayout({ + allDonations, + flaggedAddresses, + potDetail, + potId, + withTotalAmount: false, + updateState: setAllPayouts, + }); + } + }, [potDetail, flaggedAddresses, allDonations]); - const { potId } = useParams(); - - if (!projectsId) { - PotSDK.asyncGetApprovedApplications(potId).then((projects: any) => { - setProjectsId(projects); - }); - } + if (potDetail === null || sponsorshipDonations === null) return ""; - if (potDetail && public_donations_count === 0) { - PotSDK.asyncGetMatchingPoolDonations(potId).then((sponsorshipDonations: PotDonation[]) => { - sponsorshipDonations.sort((a: any, b: any) => b.net_amount - a.net_amount); - setSponsorshipDonations(sponsorshipDonations); - }); - } + const { total_public_donations, matching_pool_balance, public_donations_count } = potDetail; const calcMatchedAmount = (donations: any) => { if (donations) { @@ -59,53 +87,7 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { const donorsCount = uniqueDonorIds.size; - if (!flaggedAddresses && allDonations) { - PotSDK.getFlaggedAccounts(potDetail, potId) - .then((data) => { - if (data) { - const listOfFlagged: any = []; - data?.forEach((adminFlaggedAcc: any) => { - const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); - listOfFlagged.push(...addresses); - }); - setFlaggedAddresses(listOfFlagged); - } - }) - .catch((err) => console.log("error getting the flagged accounts ", err)); - } - - const sortAndSetPayouts = (payouts: any) => { - payouts.sort((a: any, b: any) => { - // sort by matching pool allocation, highest to lowest - return b.matchingAmount - a.matchingAmount; - }); - setAllPayouts(payouts.slice(0, 5)); - }; - - if (!allPayouts && allDonations?.length > 0 && flaggedAddresses) { - let allPayouts = []; - - if (potDetail.payouts.length) { - allPayouts = potDetail.payouts.map((payout) => { - const { project_id, amount } = payout; - return { - projectId: project_id, - matchingAmount: amount, - }; - }); - sortAndSetPayouts(allPayouts); - } else { - calculatePayouts(allDonations, matching_pool_balance, flaggedAddresses).then((calculatedPayouts: any) => { - allPayouts = Object.entries(calculatedPayouts).map(([projectId, { matchingAmount }]: any) => { - return { - projectId, - matchingAmount, - }; - }); - sortAndSetPayouts(allPayouts); - }); - } - } + console.log("allPayouts", allPayouts); const Table = ({ donations, totalAmount, totalUniqueDonors, title }: any) => { return ( @@ -136,14 +118,16 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { {usdToggle ? "USD" : "NEAR"} - {donations.map(({ projectId, donor_id, matchingAmount, net_amount }: any, idx: number) => { - const id = donor_id || projectId; - const nearAmount = formatWithCommas(SUPPORTED_FTS["NEAR"].fromIndivisible(net_amount || matchingAmount)); + {donations.map(({ project_id, donor_id, matchingAmount, net_amount, amount }: any, idx: number) => { + const id = donor_id || project_id; + const nearAmount = formatWithCommas( + SUPPORTED_FTS["NEAR"].fromIndivisible(net_amount || matchingAmount || amount), + ); const profile = Social.getr(`${id}/profile`); const matchedAmout = usdToggle ? yoctosToUsdWithFallback(matchingAmount || net_amount, true) : nearAmount; - const url = projectId ? `?tab=project&projectId=${projectId}` : `?tab=profile&accountId=${donor_id}`; + const url = project_id ? `?tab=project&projectId=${project_id}` : `?tab=profile&accountId=${donor_id}`; return (
#{idx + 1}
@@ -172,7 +156,7 @@ const PoolAllocationTable = ({ potDetail, allDonations }: Props) => { title="matching pool allocations" totalAmount={yoctosToUsdWithFallback(total_public_donations, true)} totalUniqueDonors={donorsCount} - donations={allPayouts} + donations={allPayouts.slice(0, 5)} /> ) : ( diff --git a/src/pages/Projects/components/ListSection.tsx b/src/pages/Projects/components/ListSection.tsx index 69a60f67..6d6789d1 100644 --- a/src/pages/Projects/components/ListSection.tsx +++ b/src/pages/Projects/components/ListSection.tsx @@ -1,5 +1,6 @@ import { VM, props, useMemo } from "alem"; import styled from "styled-components"; +import Feed from "@app/components/devs.near/Feed"; type BreakPoint = { breakpoint: number; @@ -17,8 +18,6 @@ type Props = { const ListSection = ({ shouldShuffle, items, renderItem }: Props) => { const responsive = props.responsive || []; - const { Feed } = VM.require("devs.near/widget/Feed"); - if (!Feed) { return

Loading...

; } diff --git a/src/services/getPotData.ts b/src/services/getPotData.ts index e23c9adf..06b59e4f 100644 --- a/src/services/getPotData.ts +++ b/src/services/getPotData.ts @@ -3,10 +3,12 @@ import PotSDK from "@app/SDK/pot"; import { FlaggedAddress, Payout, PotApplication, PotDetail, PotDonation } from "@app/types"; import calculatePayouts from "@app/utils/calculatePayouts"; -type UpdateState = (newValues: Partial) => void; +// type UpdateState = (newValues: Partial) => void; +type UpdateState = (newValues: any) => void; type GetPayoutProps = { potId: string; + withTotalAmount: boolean; allDonations: PotDonation[]; flaggedAddresses: FlaggedAddress[]; potDetail: PotDetail; @@ -22,6 +24,11 @@ export type ProjectsState = { payouts?: Record | null; }; +type CalculatedPayout = { + project_id: string; + amount: number; +}; + function isEqual(obj1: any, obj2: any) { // Check if both are the same reference if (obj1 === obj2) return true; @@ -49,28 +56,43 @@ function isEqual(obj1: any, obj2: any) { return true; } +function isObjectEqual(obj1: CalculatedPayout, obj2: CalculatedPayout): boolean { + return obj1.project_id === obj2.project_id && obj1.amount === obj2.amount; +} + +function areListsEqual(list1: CalculatedPayout[], list2: CalculatedPayout[]): boolean { + // Check if both lists have the same length + if (list1.length !== list2.length) { + return false; + } + + // Check if all objects in the lists are equal + for (let i = 0; i < list1.length; i++) { + if (!isObjectEqual(list1[i], list2[i])) { + return false; + } + } + + // If no differences are found, return true + return true; +} + const getPotData = (potId: string, property: string) => Storage.get(`${potId}-${property}`); const setPotData = (potId: string, property: string, value: any) => Storage.set(`${potId}-${property}`, value); // Get pot detail export const getConfig = ({ potId, updateState }: { potId: string; updateState: UpdateState }) => { - const potData = getPotData(potId, "config"); - const potDetail = potData?.potDetail; + const potDetail = getPotData(potId, "config"); - if (potDetail) - updateState({ - potDetail, - }); + if (potDetail) updateState(potDetail); PotSDK.asyncGetConfig(potId) .then((currentPotDetail: PotDetail) => { if (isEqual(potDetail, currentPotDetail)) return; else { setPotData(potId, "config", currentPotDetail); - updateState({ - potDetail: currentPotDetail, - }); + updateState(currentPotDetail); } }) .catch((err: unknown) => { @@ -89,71 +111,75 @@ export function getPotProjects({ isApprpved: boolean; }) { // get projects from local storage - const potData = getPotData(potId, "projects"); - const savedProject = potData.projects || []; + const savedProject = getPotData(potId, "projects") || []; if (savedProject) { // if storage project exist show it let allProjects = savedProject; // Check if only approved applications is requested if (isApprpved) allProjects = allProjects.filter((project: PotApplication) => project.status === "Approved"); - updateState({ - projects: allProjects, - filteredProjects: allProjects, - }); + updateState(allProjects); } // check the current projects - PotSDK.asyncGetApprovedApplications(potId) + PotSDK.asyncGetApplications(potId) .then((projects: PotApplication[]) => { - if (projects.length === potData.projects?.length) return; + if (projects.length === savedProject?.length) return; setPotData(potId, "projects", projects); let allProjects = projects; if (isApprpved) allProjects = allProjects.filter((project: PotApplication) => project.status === "Approved"); - updateState({ - projects, - filteredProjects: allProjects, - }); + updateState(allProjects); }) .catch((error: unknown) => { - updateState({ - projects: [], - filteredProjects: [], - }); + console.log("error fetching pot applications ", error); + + updateState([]); }); } // get pot payouts -export const getPayout = ({ potId, allDonations, flaggedAddresses, potDetail, updateState }: GetPayoutProps) => { - const potData = getPotData(potId, "payouts"); - const payouts = potData.payouts; - if (payouts) - updateState({ - payouts, - }); +export const getPayout = ({ + potId, + allDonations, + flaggedAddresses, + potDetail, + withTotalAmount, // true => return object false=> return false + updateState, +}: GetPayoutProps) => { + const storageKey = withTotalAmount ? "payouts-obj" : "payouts"; + + const payouts = getPotData(potId, storageKey); + + if (payouts) updateState(payouts); if (flaggedAddresses) { - if (potDetail.payouts) { + if (potDetail.payouts && !withTotalAmount) { if (isEqual(potDetail.payouts, payouts)) return; else { - setPotData(potId, "payouts", payouts); - updateState({ - payouts, - }); + const sortedPayouts = potDetail.payouts; + sortedPayouts.sort((a: any, b: any) => b.matchingAmount - a.matchingAmount); + setPotData(potId, storageKey, sortedPayouts); + updateState(sortedPayouts); } } else if (allDonations.length && flaggedAddresses) - calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((currentPayouts) => { - if (isEqual(payouts, currentPayouts)) return; + calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { + const currentPayouts = Object.entries(calculatedPayouts) + .map(([projectId, { matchingAmount, donorCount, totalAmount }]: any) => ({ + project_id: projectId, + amount: matchingAmount, + donorCount, + totalAmount, + })) + .filter((payout) => payout.amount !== "0"); + currentPayouts.sort((a: any, b: any) => b.matchingAmount - a.matchingAmount); + + if (areListsEqual(currentPayouts, payouts)) return; else { - setPotData(potId, "payouts", currentPayouts); - updateState({ - payouts: currentPayouts, - }); + setPotData(potId, storageKey, withTotalAmount ? calculatedPayouts : currentPayouts); + updateState(withTotalAmount ? calculatedPayouts : currentPayouts); } }); else if (allDonations?.length === 0 && flaggedAddresses?.length === 0) { - updateState({ - payouts: {}, - }); + updateState({}); } } }; @@ -187,23 +213,17 @@ export const getDonations = ({ }: { potId: string; potDetail: PotDetail; - updateState: (newValues: Partial) => void; + updateState: UpdateState; }) => { - const potData = getPotData(potId, "donations"); - const donations = potData.donations || []; + const donations = getPotData(potId, "donations") || []; - if (donations) - updateState({ - donations, - }); + if (donations) updateState(donations); if (potDetail.public_donations_count !== donations.length) { asyncGetPublicDonations(potDetail, potId).then((paginatedDonations) => { const currentDonations = paginatedDonations ? paginatedDonations.flat() : []; setPotData(potId, "donations", currentDonations); - updateState({ - donations: currentDonations, - }); + updateState(currentDonations); }); } }; @@ -225,7 +245,7 @@ export const getFlaggedAccounts = ({ }: { potId: string; potDetail: PotDetail; - updateState: (newValues: Partial) => void; + updateState: UpdateState; type: "list" | "obj"; }) => { const potData = getPotData(potId, "flaggedAccounts"); @@ -234,10 +254,7 @@ export const getFlaggedAccounts = ({ if (type === "list") { flaggedAddresses = getListOfFlagged(flaggedAddresses); } - updateState({ - flaggedAddresses, - }); - + updateState(flaggedAddresses); PotSDK.getFlaggedAccounts(potDetail) .then((data) => { if (type === "list") { @@ -245,9 +262,7 @@ export const getFlaggedAccounts = ({ if (liftOfFlagged.length === flaggedAddresses.length) return; else { setPotData(potId, "flaggedAccounts", data); - updateState({ - flaggedAddresses: data, - }); + updateState(data); } } else { const isNotEqual = data.some((adminFlaggedAcc: FlaggedAddress, idx: string) => { @@ -257,81 +272,30 @@ export const getFlaggedAccounts = ({ if (isNotEqual) { setPotData(potId, "flaggedAccounts", data); - updateState({ - flaggedAddresses: data, - }); + updateState(data); } } }) .catch((err) => console.log("error getting the flagged accounts ", err)); }; -// const allDonations = allDonationsPaginated ? allDonationsPaginated.flat() : null; - -// const potOptions = { -// potDetail: ({ potId }: { potId: string }) => PotSDK.asyncGetConfig(potId), -// projects: ({ potId }: { potId: string }) => PotSDK.asyncGetApprovedApplications(potId), -// sponsorshipDonations: ({ potId }: { potId: string }) => PotSDK.asyncGetMatchingPoolDonations(potId), -// // need potDetail, flaggedAddresses, potDetail -// payout: ({ allDonations, flaggedAddresses, potDetail }: GetPayoutProps) => -// getPayout({ -// allDonations, -// flaggedAddresses, -// potDetail, -// }), -// // need potDetail -// flaggedAddresses: ({ potId, potDetail }: { potId: string; potDetail: PotDetail }) => -// PotSDK.getFlaggedAccounts(potDetail, potId), -// donations: ({ potId, potDetail }: { potId: string; potDetail: PotDetail }) => -// asyncGetPublicDonations(potDetail, potId), -// }; - -// type PotDataOptions = "potDetail" | "payout" | "flaggedAddresses" | "projects" | "donations" | "sponsorshipDonations"; - -// export function getFlaggedAddresses({ -// potId, -// optionsHandler, -// updateState, -// }: { -// potId: string; -// optionsHandler: Record any>; -// updateState: (newValues: Partial) => void; -// }) { -// // get pot Data from local storage -// const potData: ProjectsState = JSON.parse(Storage.get(potId) || "{}"); - -// const potDetail = potData.potDetail; -// const donations = potData.donations; - -// const options = Object.keys(optionsHandler) as PotDataOptions[]; - -// const needsPotDetail = ["payout", "donations", "flaggedAddresses"]; -// const needsAll = "payout"; -// // what is needed before fetching -// let fetchFirst: Record = {}; - -// options.forEach((option) => { -// if (needsPotDetail.includes(option)) fetchFirst["potDetail"] = ""; -// if (needsAll === option) { -// fetchFirst = { -// potDetail: "", -// donations: "", -// flaggedAddresses: "", -// }; -// } -// }); - -// if( -// fetchFirst["potDetail"] && -// ){ -// PotSDK.asyncGetConfig(potId).then((potDetail:PotDetail)=>{ - -// }) -// } - -// const promises = {}; -// const results = {}; -// options.forEach((option) => { -// const result = potOptions[option]({}); -// }); -// } +export const getSponsorships = ({ potId, updateState }: { potId: string; updateState: UpdateState }) => { + const sponsors = getPotData(potId, "sponsors"); + console.log("sponsors", sponsors); + + if (sponsors) updateState(sponsors); + + PotSDK.asyncGetMatchingPoolDonations(potId) + .then((sponsorshipDonations: PotDonation[]) => { + sponsorshipDonations.sort((a: any, b: any) => b.net_amount - a.net_amount); + if (sponsors?.length === sponsorshipDonations?.length) return; + else { + updateState(sponsorshipDonations); + setPotData(potId, "sponsors", sponsorshipDonations); + } + }) + .catch((err: unknown) => { + console.log("error fetching sponsors ", err); + updateState([]); + }); +}; From 9dbd6aaa107f7ef121f054aa77858d094c09690e Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 15:46:02 +0400 Subject: [PATCH 22/40] update applications --- .../NavPages/Applications/Applications.tsx | 53 +++++++++++++------ .../PoolAllocationTable.tsx | 15 +++--- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/pages/Pot/NavPages/Applications/Applications.tsx b/src/pages/Pot/NavPages/Applications/Applications.tsx index 11985433..8fa8064a 100644 --- a/src/pages/Pot/NavPages/Applications/Applications.tsx +++ b/src/pages/Pot/NavPages/Applications/Applications.tsx @@ -1,10 +1,9 @@ import { Social, State, context, state, useParams, Tooltip, OverlayTrigger, useEffect } from "alem"; -import PotSDK from "@app/SDK/pot"; import Button from "@app/components/Button"; import Dropdown from "@app/components/Inputs/Dropdown/Dropdown"; import ToastContainer from "@app/components/ToastNotification/getToastContainer"; import ProfileImage from "@app/components/mob.near/ProfileImage"; -import { PotDetail } from "@app/types"; +import { getConfig, getPotProjects } from "@app/services/getPotData"; import _address from "@app/utils/_address"; import daysAgo from "@app/utils/daysAgo"; import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; @@ -22,12 +21,13 @@ import { Status, } from "./styles"; -const Applications = ({ potDetail }: { potDetail: PotDetail }) => { +const Applications = () => { const accountId = context.accountId; const { potId, transactionHashes } = useParams(); State.init({ newStatus: "", + potDetail: null, projectId: "", searchTerm: "", allApplications: null, @@ -39,27 +39,48 @@ const Applications = ({ potDetail }: { potDetail: PotDetail }) => { }, }); - const { newStatus, projectId, searchTerm, allApplications, filteredApplications, filterVal, toastContent } = state; - - const applications = PotSDK.getApplications(potId); + const { + newStatus, + projectId, + searchTerm, + allApplications, + filteredApplications, + filterVal, + toastContent, + potDetail, + } = state; const getApplicationCount = (sortVal: string) => { - if (!applications) return; - return applications?.filter((application: any) => { + if (!allApplications) return; + return allApplications?.filter((application: any) => { if (sortVal === "All") return true; return application.status === sortVal; })?.length; }; - if (applications && !allApplications) { - applications.reverse(); - State.update({ - filteredApplications: applications, - allApplications: applications, - }); - } + useEffect(() => { + if (!potDetail) + getConfig({ + potId, + updateState: (potDetail) => + State.update({ + potDetail, + }), + }); + if (!allApplications) + getPotProjects({ + potId, + isApprpved: false, + updateState: (applications) => + State.update({ + allApplications: applications, + filteredApplications: applications, + }), + }); + }, []); - if (!allApplications) return
; + if (allApplications === null || potDetail === null) + return
; const { owner, admins, chef } = potDetail; diff --git a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx index f10edeb6..b00d66d7 100644 --- a/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx +++ b/src/pages/Pot/components/PoolAllocationTable/PoolAllocationTable.tsx @@ -68,9 +68,9 @@ const PoolAllocationTable = () => { } }, [potDetail, flaggedAddresses, allDonations]); - if (potDetail === null || sponsorshipDonations === null) return ""; + if (potDetail === null) return ; - const { total_public_donations, matching_pool_balance, public_donations_count } = potDetail; + const { total_public_donations, public_donations_count } = potDetail; const calcMatchedAmount = (donations: any) => { if (donations) { @@ -87,8 +87,6 @@ const PoolAllocationTable = () => { const donorsCount = uniqueDonorIds.size; - console.log("allPayouts", allPayouts); - const Table = ({ donations, totalAmount, totalUniqueDonors, title }: any) => { return ( @@ -120,12 +118,13 @@ const PoolAllocationTable = () => {
{donations.map(({ project_id, donor_id, matchingAmount, net_amount, amount }: any, idx: number) => { const id = donor_id || project_id; - const nearAmount = formatWithCommas( - SUPPORTED_FTS["NEAR"].fromIndivisible(net_amount || matchingAmount || amount), - ); + + const generalAmount = net_amount || matchingAmount || amount; + + const nearAmount = formatWithCommas(SUPPORTED_FTS["NEAR"].fromIndivisible(generalAmount)); const profile = Social.getr(`${id}/profile`); - const matchedAmout = usdToggle ? yoctosToUsdWithFallback(matchingAmount || net_amount, true) : nearAmount; + const matchedAmout = usdToggle ? yoctosToUsdWithFallback(generalAmount, true) : nearAmount; const url = project_id ? `?tab=project&projectId=${project_id}` : `?tab=profile&accountId=${donor_id}`; return ( From 8a5cf6f0b2218b358bdc65744da482cf22820100 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 16:17:19 +0400 Subject: [PATCH 23/40] update Donations --- .../Pot/NavPages/Donations/Donations.tsx | 47 +++++++++++++------ src/services/getPotData.ts | 6 +-- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/pages/Pot/NavPages/Donations/Donations.tsx b/src/pages/Pot/NavPages/Donations/Donations.tsx index 1a71c710..d72e58e9 100644 --- a/src/pages/Pot/NavPages/Donations/Donations.tsx +++ b/src/pages/Pot/NavPages/Donations/Donations.tsx @@ -1,21 +1,22 @@ import { State, state, useEffect, useParams } from "alem"; import Arrow from "@app/assets/svgs/Arrow"; -import constants from "@app/constants"; -import { PotDetail } from "@app/types"; +import { getConfig, getDonations } from "@app/services/getPotData"; +import { PotDetail, PotDonation } from "@app/types"; import _address from "@app/utils/_address"; -import getTimePassed from "@app/utils/getTimePassed"; import DonationsTable from "../../components/DonationsTable/DonationsTable"; -import { Container, DonationsCount, OuterText, OuterTextContainer, Sort, TableContainer } from "./styes"; +import { Container, DonationsCount, OuterText, OuterTextContainer, Sort } from "./styes"; type Props = { allDonations: any[]; potDetail: PotDetail; }; -const Donations = (props: Props) => { - const { allDonations, potDetail } = props; +const Donations = () => { + const { potId } = useParams(); State.init({ + potDetail: null, + allDonations: null, filteredDonations: [], currentFilter: "date", filter: { @@ -24,23 +25,39 @@ const Donations = (props: Props) => { }, }); - const { filteredDonations, currentFilter, filter } = state; + const { filteredDonations, currentFilter, filter, potDetail, allDonations } = state; useEffect(() => { - if (allDonations && filteredDonations.length === 0) { - const sortedDonations = [...allDonations].reverse(); - State.update({ filteredDonations: sortedDonations }); - } - }, [allDonations]); - if (!allDonations) return
; + if (!potDetail) + getConfig({ + potId, + updateState: (potDetail) => + State.update({ + potDetail, + }), + }); + if (!allDonations && potDetail) + getDonations({ + potId, + potDetail, + updateState: (allDonations) => + State.update({ + allDonations, + filteredDonations: allDonations, + }), + }); + }, [potDetail]); + + if (allDonations === null && potDetail === null) + return
; const searchDonations = (searchTerm: string) => { // filter donations that match the search term (donor_id, project_id) - const filteredDonations = allDonations.filter((donation) => { + const filteredDonations = allDonations.filter((donation: PotDonation) => { const { donor_id, project_id } = donation; const searchFields = [donor_id, project_id]; - return searchFields.some((field) => field.toLowerCase().includes(searchTerm.toLowerCase())); + return searchFields.some((field) => (field || "").toLowerCase().includes(searchTerm.toLowerCase())); }); return filteredDonations; }; diff --git a/src/services/getPotData.ts b/src/services/getPotData.ts index 06b59e4f..51393386 100644 --- a/src/services/getPotData.ts +++ b/src/services/getPotData.ts @@ -215,13 +215,14 @@ export const getDonations = ({ potDetail: PotDetail; updateState: UpdateState; }) => { - const donations = getPotData(potId, "donations") || []; + const donations = getPotData(potId, "donations"); if (donations) updateState(donations); - if (potDetail.public_donations_count !== donations.length) { + if (potDetail.public_donations_count !== donations?.length) { asyncGetPublicDonations(potDetail, potId).then((paginatedDonations) => { const currentDonations = paginatedDonations ? paginatedDonations.flat() : []; + currentDonations.reverse(); setPotData(potId, "donations", currentDonations); updateState(currentDonations); }); @@ -281,7 +282,6 @@ export const getFlaggedAccounts = ({ export const getSponsorships = ({ potId, updateState }: { potId: string; updateState: UpdateState }) => { const sponsors = getPotData(potId, "sponsors"); - console.log("sponsors", sponsors); if (sponsors) updateState(sponsors); From 7d569c648f04d032176cea18844eb2ad67e934a7 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 16:35:08 +0400 Subject: [PATCH 24/40] update sponsors --- src/pages/Pot/NavPages/Sponsors/Sponsors.tsx | 81 +++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/src/pages/Pot/NavPages/Sponsors/Sponsors.tsx b/src/pages/Pot/NavPages/Sponsors/Sponsors.tsx index 5e7a0e53..566a6e19 100644 --- a/src/pages/Pot/NavPages/Sponsors/Sponsors.tsx +++ b/src/pages/Pot/NavPages/Sponsors/Sponsors.tsx @@ -1,50 +1,59 @@ -import { State, state, useParams } from "alem"; -import PotSDK from "@app/SDK/pot"; -import { PotDetail } from "@app/types"; +import { useEffect, useParams, useState } from "alem"; +import { getConfig, getSponsorships } from "@app/services/getPotData"; +import { PotDetail, PotDonation } from "@app/types"; import SponsorsBoard from "../../components/SponsorsBoard/SponsorsBoard"; import SponsorsTable from "../../components/SponsorsTable/SponsorsTable"; import { Container, TableContainer } from "./styles"; -const Sponsors = ({ potDetail }: { potDetail: PotDetail }) => { +const Sponsors = () => { const { potId } = useParams(); - let sponsorshipDonations = PotSDK.getMatchingPoolDonations(potId); - - State.init({ - sponsorshipDonations: null, - }); - - if (sponsorshipDonations && !state.sponsorshipDonations) { - // accumulate donations for each address - sponsorshipDonations = sponsorshipDonations.reduce((accumulator: any, currentDonation: any) => { - accumulator[currentDonation.donor_id] = { - amount: accumulator[currentDonation.donor_id].amount || 0 + currentDonation.net_amount, - ...currentDonation, - }; - return accumulator; - }, {}); - - // add % share of total to each donation - const total = parseFloat(potDetail.matching_pool_balance); - - sponsorshipDonations = Object.values(sponsorshipDonations).sort((a: any, b: any) => b.amount - a.amount); - sponsorshipDonations = sponsorshipDonations.map((donation: any) => { - return { - ...donation, - percentage_share: ((donation.amount / total) * 100).toFixed(2).replace(/[.,]00$/, ""), - }; - }); - State.update({ sponsorshipDonations }); - } - - if (!state.sponsorshipDonations) return
; + const [sponsorshipDonations, setSponsorshipDonations] = useState(null); + const [potDetail, setPotDetail] = useState(null); + + useEffect(() => { + if (!potDetail) { + getConfig({ + potId, + updateState: setPotDetail, + }); + } + if (!sponsorshipDonations && potDetail) { + getSponsorships({ + potId, + updateState: (donations) => { + let sponsorshipDonations = donations.reduce((accumulator: any, currentDonation: any) => { + accumulator[currentDonation.donor_id] = { + amount: accumulator[currentDonation.donor_id].amount || 0 + currentDonation.net_amount, + ...currentDonation, + }; + return accumulator; + }, {}); + + // add % share of total to each donation + const total = parseFloat(potDetail.matching_pool_balance); + + sponsorshipDonations = Object.values(sponsorshipDonations).sort((a: any, b: any) => b.amount - a.amount); + sponsorshipDonations = sponsorshipDonations.map((donation: any) => { + return { + ...donation, + percentage_share: ((donation.amount / total) * 100).toFixed(2).replace(/[.,]00$/, ""), + }; + }); + setSponsorshipDonations(sponsorshipDonations); + }, + }); + } + }, [potDetail]); + + if (!sponsorshipDonations) return
; return ( - + - + ); From e2facba45eec4cac8b5c6b2093915a8fae0affcc Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 18:53:56 +0400 Subject: [PATCH 25/40] finish update flagging --- src/components/Card/Card.tsx | 5 +- src/pages/Pot/NavPages/Payouts/Payouts.tsx | 118 ++++++++---------- src/pages/Pot/NavPages/Projects/Projects.tsx | 10 +- src/pages/Pot/Pot.tsx | 2 +- .../FlaggedAccounts/FlaggedAccounts.tsx | 47 ++++--- src/pages/Pot/components/Header/Header.tsx | 2 +- .../PoolAllocationTable.tsx | 9 +- src/services/getPotData.ts | 70 +++++------ src/types.ts | 11 +- src/utils/calculatePayouts.ts | 4 +- 10 files changed, 141 insertions(+), 137 deletions(-) diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 354d5a6f..4508b9ae 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,4 +1,4 @@ -import { Big, RouteLink, Social, context, useEffect, useMemo, useParams, useState } from "alem"; +import { Big, RouteLink, Social, context } from "alem"; import DonateSDK from "@app/SDK/donate"; import PotSDK from "@app/SDK/pot"; import { useDonationModal } from "@app/hooks/useDonationModal"; @@ -33,6 +33,7 @@ import { const Card = (props: any) => { const { payoutDetails, allowDonate: _allowDonate, potId } = props; + // Start Modals provider const Modals = useModals(); const { setDonationModalProps } = useDonationModal(); @@ -196,7 +197,7 @@ const Card = (props: any) => { {payoutDetails && ( Estimated matched amount - {yoctosToNear(payoutDetails.matchingAmount) || "- N"} + {yoctosToNear(payoutDetails.amount) || "- N"} )} diff --git a/src/pages/Pot/NavPages/Payouts/Payouts.tsx b/src/pages/Pot/NavPages/Payouts/Payouts.tsx index ddb30141..69e1ad7a 100644 --- a/src/pages/Pot/NavPages/Payouts/Payouts.tsx +++ b/src/pages/Pot/NavPages/Payouts/Payouts.tsx @@ -1,9 +1,7 @@ -import { context, useParams, State, state } from "alem"; -import PotSDK from "@app/SDK/pot"; +import { useParams, State, state, useEffect } from "alem"; import ArrowDown from "@app/assets/svgs/ArrowDown"; import ProfileImage from "@app/components/mob.near/ProfileImage"; -import { PotDetail } from "@app/types"; -import calculatePayouts from "@app/utils/calculatePayouts"; +import { getConfig, getDonations, getFlaggedAccounts, getPayout } from "@app/services/getPotData"; import yoctosToNear from "@app/utils/yoctosToNear"; import FlaggedAccounts from "../../components/FlaggedAccounts/FlaggedAccounts"; import PayoutsChallenges from "../../components/PayoutsChallenges/PayoutsChallenges"; @@ -25,67 +23,59 @@ import { WarningText, } from "./styles"; -const Payouts = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonations: any }) => { +const Payouts = () => { const { potId } = useParams(); State.init({ - allPayouts: null, + potDetail: null, + allDonations: null, + allPayouts: null, // array + allPayoutsDetails: null, // obj filteredPayouts: null, flaggedAddresses: null, }); - const { allPayouts, filteredPayouts, flaggedAddresses } = state; + const { allPayouts, filteredPayouts, flaggedAddresses, potDetail, allDonations } = state; - if (!flaggedAddresses) { - PotSDK.getFlaggedAccounts(potDetail, potId) - .then((data) => { - const listOfFlagged: any = []; - data.forEach((adminFlaggedAcc: any) => { - const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); - listOfFlagged.push(...addresses); - }); - State.update({ flaggedAddresses: listOfFlagged }); - }) - .catch((err) => console.log("error getting the flagged accounts ", err)); - } + useEffect(() => { + if (!potDetail) + getConfig({ + potId, + updateState: (potDetail) => State.update({ potDetail }), + }); + if (!allDonations && potDetail) + getDonations({ + potDetail, + potId, + updateState: (allDonations) => State.update({ allDonations }), + }); + if (!flaggedAddresses && potDetail) + getFlaggedAccounts({ + potDetail, + potId, + updateState: (flaggedAddresses) => State.update({ flaggedAddresses }), + type: "list", + }); + }, [potDetail]); - if (!allPayouts && allDonations && potDetail && flaggedAddresses) { - calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((calculatedPayouts: any) => { - if (potDetail.payouts.length) { - // handle these payouts, which don't contain all the info needed - // pot payouts contain id, project_id, amount & paid_at - // loop through potDetail payouts and synthesize the two sets of payouts, so projectId and matchingAmount are taken from potDetail payouts, and donorCount and totalAmount are taken from calculatedPayouts - const synthesizedPayouts = potDetail.payouts.map((payout) => { - const { project_id, amount } = payout; - const { totalAmount, donorCount } = calculatedPayouts[project_id]; - return { - projectId: project_id, - totalAmount, - matchingAmount: amount, - donorCount, - }; - }); - State.update({ allPayouts: synthesizedPayouts, filteredPayouts: synthesizedPayouts }); - } else { - // calculate estimated payouts - const allPayouts = Object.entries(calculatedPayouts).map( - ([projectId, { totalAmount, matchingAmount, donorCount }]: any) => { - return { - projectId, - totalAmount, - matchingAmount, - donorCount, - }; - }, - ); // TODO: refactor to use PotsSDK (note that this is duplicated in Pots/Projects.jsx) - allPayouts.sort((a, b) => { - // sort by matching pool allocation, highest to lowest - return b.matchingAmount - a.matchingAmount; - }); - State.update({ allPayouts, filteredPayouts: allPayouts }); - } + useEffect(() => { + console.log({ + potDetail, + flaggedAddresses, + allDonations, }); - } + + if (potDetail && flaggedAddresses && allDonations && !allPayouts) { + getPayout({ + allDonations, + flaggedAddresses, + potDetail, + potId, + withTotalAmount: true, + updateState: (payouts) => State.update({ allPayouts: payouts, filteredPayouts: payouts }), + }); + } + }, [potDetail, allDonations, flaggedAddresses]); const searchPayouts = (searchTerm: string) => { // filter payouts that match the search term (donor_id, project_id) @@ -96,7 +86,7 @@ const Payouts = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonatio }); filteredPayouts.sort((a: any, b: any) => { // sort by matching pool allocation, highest to lowest - return b.matchingAmount - a.matchingAmount; + return b.amount - a.amount; }); return filteredPayouts; }; @@ -105,7 +95,7 @@ const Payouts = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonatio return ( - + {!potDetail.all_paid_out && ( @@ -162,7 +152,7 @@ const Payouts = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonatio No payouts to display ) : ( filteredPayouts.map((payout: any, index: number) => { - const { projectId, donorCount, matchingAmount, totalAmount } = payout; + const { project_id, donorCount, amount, totalAmount } = payout; return ( @@ -173,12 +163,12 @@ const Payouts = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonatio width: "24px", }} className="profile-image" - accountId={projectId} + accountId={project_id} /> - - {projectId.length > MAX_ACCOUNT_ID_DISPLAY_LENGTH - ? projectId.slice(0, MAX_ACCOUNT_ID_DISPLAY_LENGTH) + "..." - : projectId} + + {project_id.length > MAX_ACCOUNT_ID_DISPLAY_LENGTH + ? project_id.slice(0, MAX_ACCOUNT_ID_DISPLAY_LENGTH) + "..." + : project_id} {/* Total Raised */} @@ -197,7 +187,7 @@ const Payouts = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonatio {/* Matching Pool Allocation */} - {yoctosToNear(matchingAmount, true)} Allocated + {yoctosToNear(amount, true)} Allocated diff --git a/src/pages/Pot/NavPages/Projects/Projects.tsx b/src/pages/Pot/NavPages/Projects/Projects.tsx index 9fd5f248..c37d9d54 100644 --- a/src/pages/Pot/NavPages/Projects/Projects.tsx +++ b/src/pages/Pot/NavPages/Projects/Projects.tsx @@ -12,14 +12,14 @@ type Props = { allDonations: any; }; -const Projects = (props: Props) => { +const Projects = () => { const { potId } = useParams(); const [projects, setProjects] = useState(null); const [filteredProjects, setFilteredProjects] = useState([]); const [donations, setDonations] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); - const [payouts, setPayouts] = useState | null>(null); + const [payouts, setPayouts] = useState(null); const [potDetail, setPotDetail] = useState(null); const Loading = () => ( @@ -65,7 +65,7 @@ const Projects = (props: Props) => { }, [potDetail]); useEffect(() => { - if (potDetail && flaggedAddresses && donations) { + if (potDetail && flaggedAddresses && donations && !payouts) { getPayout({ allDonations: donations, flaggedAddresses, @@ -77,7 +77,7 @@ const Projects = (props: Props) => { } }, [potDetail, flaggedAddresses, donations]); - if (!projects || !potDetail) return ; + if (projects === null || potDetail === null) return ; const { public_round_start_ms, public_round_end_ms } = potDetail; @@ -149,7 +149,7 @@ const Projects = (props: Props) => { projects, projectId: project.project_id, allowDonate: publicRoundOpen && project.project_id !== context.accountId, - payoutDetails: (payouts || {})[project.project_id] || { + payoutDetails: (payouts || []).find((payout: Payout) => payout.project_id === project.project_id) || { donorCount: 0, matchingAmount: "0", totalAmount: "0", diff --git a/src/pages/Pot/Pot.tsx b/src/pages/Pot/Pot.tsx index 9c463f50..072eb5dd 100644 --- a/src/pages/Pot/Pot.tsx +++ b/src/pages/Pot/Pot.tsx @@ -76,7 +76,7 @@ const Pot = () => { return ( - + {/* */}
diff --git a/src/pages/Pot/components/FlaggedAccounts/FlaggedAccounts.tsx b/src/pages/Pot/components/FlaggedAccounts/FlaggedAccounts.tsx index 3f884ecf..ab9786f7 100644 --- a/src/pages/Pot/components/FlaggedAccounts/FlaggedAccounts.tsx +++ b/src/pages/Pot/components/FlaggedAccounts/FlaggedAccounts.tsx @@ -1,36 +1,45 @@ -import { useParams, useState } from "alem"; +import { useEffect, useParams, useState } from "alem"; import PotSDK from "@app/SDK/pot"; import ProfileImage from "@app/components/mob.near/ProfileImage"; +import { getConfig, getFlaggedAccounts } from "@app/services/getPotData"; import { PotDetail } from "@app/types"; import hrefWithParams from "@app/utils/hrefWithParams"; import { Container, Flag, Line, Table, Title } from "./styles"; -const FlaggedAccounts = ({ potDetail }: { potDetail: PotDetail }) => { +const FlaggedAccounts = () => { const { potId } = useParams(); const [flaggedAddresses, setFlaggedAddresses] = useState(null); + const [potDetail, setPotDetail] = useState(null); const [toggleView, setToggleView] = useState(null); - if (!flaggedAddresses) { - PotSDK.getFlaggedAccounts(potDetail, potId) - .then((data) => { - const listOfFlagged: any = []; - data.forEach((adminFlaggedAcc: any) => { - const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); - addresses.forEach((address) => { - listOfFlagged.push({ - address: address, - reason: adminFlaggedAcc.potFlaggedAcc[address], - flaggedBy: adminFlaggedAcc.flaggedBy, - role: adminFlaggedAcc.role, + useEffect(() => { + if (!potDetail) getConfig({ potId, updateState: setPotDetail }); + if (!flaggedAddresses && potDetail) + getFlaggedAccounts({ + potId, + potDetail, + type: "obj", + updateState: (data) => { + console.log("data", data); + + const listOfFlagged: any = []; + data.forEach((adminFlaggedAcc: any) => { + const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); + addresses.forEach((address) => { + listOfFlagged.push({ + address: address, + reason: adminFlaggedAcc.potFlaggedAcc[address], + flaggedBy: adminFlaggedAcc.flaggedBy, + role: adminFlaggedAcc.role, + }); }); }); - }); - setFlaggedAddresses(listOfFlagged); - }) - .catch((err) => console.log("error getting the flagged accounts ", err)); - } + setFlaggedAddresses(listOfFlagged); + }, + }); + }, [potDetail]); return !flaggedAddresses ? ( "Loading..." diff --git a/src/pages/Pot/components/Header/Header.tsx b/src/pages/Pot/components/Header/Header.tsx index 4b8d2f17..0c84b10a 100644 --- a/src/pages/Pot/components/Header/Header.tsx +++ b/src/pages/Pot/components/Header/Header.tsx @@ -242,7 +242,7 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation
- +
{isApplicationModalOpen && ( { } }; - const uniqueDonorIds = allDonations ? new Set(allDonations.map((donation: any) => donation.donor_id)) : new Set([]); - - const donorsCount = uniqueDonorIds.size; + const donorsCount = useMemo(() => { + const uniqueDonorIds = allDonations ? new Set(allDonations.map((donation: any) => donation.donor_id)) : new Set([]); + return uniqueDonorIds.size; + }, [allDonations]); const Table = ({ donations, totalAmount, totalUniqueDonors, title }: any) => { return ( diff --git a/src/services/getPotData.ts b/src/services/getPotData.ts index 51393386..f0667ef1 100644 --- a/src/services/getPotData.ts +++ b/src/services/getPotData.ts @@ -1,6 +1,6 @@ import { Storage } from "alem"; import PotSDK from "@app/SDK/pot"; -import { FlaggedAddress, Payout, PotApplication, PotDetail, PotDonation } from "@app/types"; +import { FlaggedAddress, Payout, PotApplication, PotDetail, PotDonation, CalculatedPayout } from "@app/types"; import calculatePayouts from "@app/utils/calculatePayouts"; // type UpdateState = (newValues: Partial) => void; @@ -24,11 +24,6 @@ export type ProjectsState = { payouts?: Record | null; }; -type CalculatedPayout = { - project_id: string; - amount: number; -}; - function isEqual(obj1: any, obj2: any) { // Check if both are the same reference if (obj1 === obj2) return true; @@ -148,6 +143,7 @@ export const getPayout = ({ const storageKey = withTotalAmount ? "payouts-obj" : "payouts"; const payouts = getPotData(potId, storageKey); + console.log("payouts", payouts); if (payouts) updateState(payouts); @@ -156,29 +152,33 @@ export const getPayout = ({ if (isEqual(potDetail.payouts, payouts)) return; else { const sortedPayouts = potDetail.payouts; - sortedPayouts.sort((a: any, b: any) => b.matchingAmount - a.matchingAmount); + sortedPayouts.sort((a: any, b: any) => b.amount - a.amount); setPotData(potId, storageKey, sortedPayouts); updateState(sortedPayouts); } - } else if (allDonations.length && flaggedAddresses) + } else if (allDonations.length && flaggedAddresses) { calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { - const currentPayouts = Object.entries(calculatedPayouts) - .map(([projectId, { matchingAmount, donorCount, totalAmount }]: any) => ({ - project_id: projectId, - amount: matchingAmount, - donorCount, - totalAmount, - })) - .filter((payout) => payout.amount !== "0"); - currentPayouts.sort((a: any, b: any) => b.matchingAmount - a.matchingAmount); + const currentPayouts = potDetail.payouts + ? potDetail.payouts.map((payout: Payout) => ({ + ...payout, + donorCount: calculatedPayouts[payout.project_id].donorCount, + totalAmount: calculatedPayouts[payout.project_id].totalAmount, + })) + : Object.entries(calculatedPayouts).map(([projectId, { matchingAmount, donorCount, totalAmount }]: any) => ({ + project_id: projectId, + amount: matchingAmount, + donorCount, + totalAmount, + })); + currentPayouts.sort((a: any, b: any) => b.amount - a.amount); if (areListsEqual(currentPayouts, payouts)) return; else { - setPotData(potId, storageKey, withTotalAmount ? calculatedPayouts : currentPayouts); - updateState(withTotalAmount ? calculatedPayouts : currentPayouts); + setPotData(potId, storageKey, currentPayouts); + updateState(currentPayouts); } }); - else if (allDonations?.length === 0 && flaggedAddresses?.length === 0) { + } else if (allDonations?.length === 0 && flaggedAddresses?.length === 0) { updateState({}); } } @@ -249,29 +249,25 @@ export const getFlaggedAccounts = ({ updateState: UpdateState; type: "list" | "obj"; }) => { - const potData = getPotData(potId, "flaggedAccounts"); - let flaggedAddresses = potData.flaggedAddresses || []; + const flaggedAddresses = getPotData(potId, "flaggedAccounts") || []; + + const saveListOfFlagged = getListOfFlagged(flaggedAddresses); if (type === "list") { - flaggedAddresses = getListOfFlagged(flaggedAddresses); + updateState(saveListOfFlagged); + } else { + updateState(flaggedAddresses); } - updateState(flaggedAddresses); - PotSDK.getFlaggedAccounts(potDetail) + + PotSDK.getFlaggedAccounts(potDetail, potId) .then((data) => { - if (type === "list") { - const liftOfFlagged = getListOfFlagged(data); - if (liftOfFlagged.length === flaggedAddresses.length) return; - else { + const liftOfFlagged = getListOfFlagged(data); + + if (liftOfFlagged.length !== saveListOfFlagged.length) { + if (type === "list") { setPotData(potId, "flaggedAccounts", data); updateState(data); - } - } else { - const isNotEqual = data.some((adminFlaggedAcc: FlaggedAddress, idx: string) => { - if (adminFlaggedAcc?.potFlaggedAcc?.length === adminFlaggedAcc?.potFlaggedAcc?.length) return false; - else return true; - }); - - if (isNotEqual) { + } else { setPotData(potId, "flaggedAccounts", data); updateState(data); } diff --git a/src/types.ts b/src/types.ts index fc31d0ed..33286515 100644 --- a/src/types.ts +++ b/src/types.ts @@ -123,9 +123,16 @@ export type FlaggedAddress = { potFlaggedAcc: "string"; }; -export interface Payout { +export type Payout = { id: string; project_id: string; amount: string; paid_at: number; -} +}; + +export type CalculatedPayout = { + project_id: string; + amount: number; + donorCount: number; + totalAmount: string; +}; diff --git a/src/utils/calculatePayouts.ts b/src/utils/calculatePayouts.ts index da1d2f1e..1bf81001 100644 --- a/src/utils/calculatePayouts.ts +++ b/src/utils/calculatePayouts.ts @@ -1,12 +1,12 @@ import { Big, Near } from "alem"; import constants from "@app/constants"; -import { Payout } from "@app/types"; +import { CalculatedPayout } from "@app/types"; const calculatePayouts = ( allPotDonations: any, totalMatchingPool: any, blacklistedAccounts: any, -): Promise> => { +): Promise> => { const { NADABOT_CONTRACT_ID } = constants; // * QF/CLR logic taken from https://github.com/gitcoinco/quadratic-funding/blob/master/quadratic-funding/clr.py * From 20a0dadf2cacb8285cc140b944b8501e183d4b0f Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 22 May 2024 23:29:29 +0400 Subject: [PATCH 26/40] fix pot setting --- src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx b/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx index 1d69262d..f08cd4ee 100644 --- a/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx +++ b/src/pages/Pot/NavPages/ConfigForm/ConfigForm.tsx @@ -132,7 +132,7 @@ const ConfigForm = ({ potDetail, style }: { potDetail?: PotDetail; style?: any } const [state, setState] = useState(inital_state); const updateState = (updatedState: any) => setState({ - ...inital_state, + ...state, ...updatedState, }); From e1f627352b813d3ac5169ce5ad61349434d613c2 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 23 May 2024 12:32:34 +0400 Subject: [PATCH 27/40] update setting --- src/pages/Pot/NavPages/Projects/Projects.tsx | 2 +- src/pages/Pot/NavPages/Settings/Settings.tsx | 20 ++++-- src/services/getPotData.ts | 76 ++++++++++++-------- 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/pages/Pot/NavPages/Projects/Projects.tsx b/src/pages/Pot/NavPages/Projects/Projects.tsx index c37d9d54..cb862fd8 100644 --- a/src/pages/Pot/NavPages/Projects/Projects.tsx +++ b/src/pages/Pot/NavPages/Projects/Projects.tsx @@ -151,7 +151,7 @@ const Projects = () => { allowDonate: publicRoundOpen && project.project_id !== context.accountId, payoutDetails: (payouts || []).find((payout: Payout) => payout.project_id === project.project_id) || { donorCount: 0, - matchingAmount: "0", + amount: "0", totalAmount: "0", }, }} diff --git a/src/pages/Pot/NavPages/Settings/Settings.tsx b/src/pages/Pot/NavPages/Settings/Settings.tsx index cc684af0..cd79ac4d 100644 --- a/src/pages/Pot/NavPages/Settings/Settings.tsx +++ b/src/pages/Pot/NavPages/Settings/Settings.tsx @@ -1,6 +1,7 @@ -import { OverlayTrigger, Tooltip, context, useParams, useState } from "alem"; +import { OverlayTrigger, Tooltip, context, useEffect, useParams, useState } from "alem"; import PotSDK from "@app/SDK/pot"; import ProfileImage from "@app/components/mob.near/ProfileImage"; +import { getConfig } from "@app/services/getPotData"; import { PotDetail } from "@app/types"; import _address from "@app/utils/_address"; import hrefWithParams from "@app/utils/hrefWithParams"; @@ -8,15 +9,26 @@ import ConfigForm from "../ConfigForm/ConfigForm"; import getFields from "./getFields"; import { Admins, AdminsWrapper, Container, Detail, PrviewContainer, Title } from "./styles"; -const Settings = ({ potDetail }: { potDetail: PotDetail }) => { - const { owner, admins } = potDetail; - +const Settings = () => { const { potId } = useParams(); const [editSettings, setEditSettings] = useState(false); + const [potDetail, setPotDetail] = useState(null); const userIsAdminOrGreater = PotSDK.isUserPotAdminOrGreater(potId, context.accountId); + useEffect(() => { + if (!potDetail) + getConfig({ + potId, + updateState: setPotDetail, + }); + }, []); + + if (potDetail === null) return
; + const fields = getFields(potId, potDetail); + const { owner, admins } = potDetail; + const AdminsTooltip = () => (
diff --git a/src/services/getPotData.ts b/src/services/getPotData.ts index f0667ef1..3768ee62 100644 --- a/src/services/getPotData.ts +++ b/src/services/getPotData.ts @@ -142,44 +142,58 @@ export const getPayout = ({ }: GetPayoutProps) => { const storageKey = withTotalAmount ? "payouts-obj" : "payouts"; - const payouts = getPotData(potId, storageKey); - console.log("payouts", payouts); + const flaggedLength = flaggedAddresses.length; + const allDonationsLength = allDonations.length; + + const potData = getPotData(potId, storageKey); + + const payouts = potData?.payouts; + const storedFlaggedLength = potData?.flaggedLength; + const storedAllDonationsLength = potData?.allDonationsLength; + + // limit payment calculations by checking the calculatePayouts params + const isEqualStored = flaggedLength === storedFlaggedLength && storedAllDonationsLength === allDonationsLength; if (payouts) updateState(payouts); - if (flaggedAddresses) { - if (potDetail.payouts && !withTotalAmount) { - if (isEqual(potDetail.payouts, payouts)) return; - else { - const sortedPayouts = potDetail.payouts; - sortedPayouts.sort((a: any, b: any) => b.amount - a.amount); - setPotData(potId, storageKey, sortedPayouts); - updateState(sortedPayouts); - } - } else if (allDonations.length && flaggedAddresses) { + if (!isEqualStored || payouts.length === 0) { + if (potDetail.payouts.length && !withTotalAmount) { + const sortedPayouts = potDetail.payouts; + sortedPayouts.sort((a: any, b: any) => b.amount - a.amount); + setPotData(potId, storageKey, { + payouts: sortedPayouts, + flaggedLength, + allDonationsLength, + }); + updateState(sortedPayouts); + } else if (allDonations?.length === 0 && flaggedAddresses?.length === 0) { + updateState({}); + } else if (allDonations && flaggedAddresses) { calculatePayouts(allDonations, potDetail.matching_pool_balance, flaggedAddresses).then((calculatedPayouts) => { - const currentPayouts = potDetail.payouts - ? potDetail.payouts.map((payout: Payout) => ({ - ...payout, - donorCount: calculatedPayouts[payout.project_id].donorCount, - totalAmount: calculatedPayouts[payout.project_id].totalAmount, - })) - : Object.entries(calculatedPayouts).map(([projectId, { matchingAmount, donorCount, totalAmount }]: any) => ({ - project_id: projectId, - amount: matchingAmount, - donorCount, - totalAmount, - })); + const currentPayouts = + potDetail.payouts.length > 0 + ? potDetail.payouts.map((payout: Payout) => ({ + ...payout, + donorCount: calculatedPayouts[payout.project_id].donorCount, + totalAmount: calculatedPayouts[payout.project_id].totalAmount, + })) + : Object.entries(calculatedPayouts).map( + ([projectId, { matchingAmount, donorCount, totalAmount }]: any) => ({ + project_id: projectId, + amount: matchingAmount, + donorCount, + totalAmount, + }), + ); currentPayouts.sort((a: any, b: any) => b.amount - a.amount); - if (areListsEqual(currentPayouts, payouts)) return; - else { - setPotData(potId, storageKey, currentPayouts); - updateState(currentPayouts); - } + setPotData(potId, storageKey, { + payouts: currentPayouts, + flaggedLength, + allDonationsLength, + }); + updateState(currentPayouts); }); - } else if (allDonations?.length === 0 && flaggedAddresses?.length === 0) { - updateState({}); } } }; From dd86f3ee034ef9c78f9ba3f821979d20a4ba3972 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 23 May 2024 13:18:35 +0400 Subject: [PATCH 28/40] update pot header --- .../Pot/NavPages/Donations/Donations.tsx | 5 - src/pages/Pot/NavPages/Projects/Projects.tsx | 5 - src/pages/Pot/Pot.tsx | 32 +---- src/pages/Pot/components/Header/Header.tsx | 123 ++++++++++-------- .../components/HeaderStatus/HeaderStatus.tsx | 14 +- 5 files changed, 83 insertions(+), 96 deletions(-) diff --git a/src/pages/Pot/NavPages/Donations/Donations.tsx b/src/pages/Pot/NavPages/Donations/Donations.tsx index d72e58e9..65b6be41 100644 --- a/src/pages/Pot/NavPages/Donations/Donations.tsx +++ b/src/pages/Pot/NavPages/Donations/Donations.tsx @@ -6,11 +6,6 @@ import _address from "@app/utils/_address"; import DonationsTable from "../../components/DonationsTable/DonationsTable"; import { Container, DonationsCount, OuterText, OuterTextContainer, Sort } from "./styes"; -type Props = { - allDonations: any[]; - potDetail: PotDetail; -}; - const Donations = () => { const { potId } = useParams(); diff --git a/src/pages/Pot/NavPages/Projects/Projects.tsx b/src/pages/Pot/NavPages/Projects/Projects.tsx index cb862fd8..e6e40a84 100644 --- a/src/pages/Pot/NavPages/Projects/Projects.tsx +++ b/src/pages/Pot/NavPages/Projects/Projects.tsx @@ -7,11 +7,6 @@ import getTagsFromSocialProfileData from "@app/utils/getTagsFromSocialProfileDat import getTeamMembersFromSocialProfileData from "@app/utils/getTeamMembersFromSocialProfileData"; import { Centralized, Container, SearchBar, Title } from "./styles"; -type Props = { - potDetail: any; - allDonations: any; -}; - const Projects = () => { const { potId } = useParams(); diff --git a/src/pages/Pot/Pot.tsx b/src/pages/Pot/Pot.tsx index 072eb5dd..64996404 100644 --- a/src/pages/Pot/Pot.tsx +++ b/src/pages/Pot/Pot.tsx @@ -44,30 +44,6 @@ const Pot = () => { // default to home tab } - // Get total public donations - const allDonationsPaginated = useCache(() => { - const limit = 480; // number of donations to fetch per req - - const donationsCount = potDetail.public_donations_count; - const paginations = [...Array(Math.ceil(donationsCount / limit)).keys()]; - - try { - const allDonations = paginations.map((index) => - PotSDK.asyncGetPublicRoundDonations(potId, { - from_index: index * limit, - limit: limit, - }), - ); - - return Promise.all(allDonations); - } catch (error) { - console.error(`error getting public donations`, error); - return Promise.all([]); - } - }, "pot-public-donations"); - - const allDonations = allDonationsPaginated ? allDonationsPaginated.flat() : null; - const options = navOptions(potId || "", potDetail); const SelectedNavComponent = useMemo(() => { @@ -76,14 +52,12 @@ const Pot = () => { return ( - {/* */} -
+ +
- - {SelectedNavComponent && } - + {SelectedNavComponent && } ); }; diff --git a/src/pages/Pot/components/Header/Header.tsx b/src/pages/Pot/components/Header/Header.tsx index 0c84b10a..4d689d9c 100644 --- a/src/pages/Pot/components/Header/Header.tsx +++ b/src/pages/Pot/components/Header/Header.tsx @@ -5,7 +5,8 @@ import constants from "@app/constants"; import { useDonationModal } from "@app/hooks/useDonationModal"; import useModals from "@app/hooks/useModals"; import CopyIcon from "@app/pages/Project/components/CopyIcon"; -import { PotDetail } from "@app/types"; +import { getConfig, getDonations, getFlaggedAccounts, getPotProjects } from "@app/services/getPotData"; +import { PotDetail, PotDonation } from "@app/types"; import calculatePayouts from "@app/utils/calculatePayouts"; import getTransactionsFromHashes from "@app/utils/getTransactionsFromHashes"; import nearToUsd from "@app/utils/nearToUsd"; @@ -18,23 +19,7 @@ import PoolAllocationTable from "../PoolAllocationTable/PoolAllocationTable"; import SuccessFundModal, { ExtendedFundDonation } from "../SuccessFundModal/SuccessFundModal"; import { ButtonsWrapper, Container, Description, Fund, HeaderWrapper, Referral, Title } from "./styles"; -const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonations: any }) => { - const { - admins, - chef, - owner, - pot_name, - pot_description, - matching_pool_balance, - public_round_end_ms, - public_round_start_ms, - application_start_ms, - application_end_ms, - cooldown_end_ms: _cooldown_end_ms, - all_paid_out, - registry_provider, - } = potDetail; - +const Header = () => { const { IPFS_BASE_URL, NADA_BOT_URL } = constants; const { potId, transactionHashes } = useParams(); @@ -55,29 +40,59 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation const [isDao, setIsDao] = useState(null); const [applicationSuccess, setApplicationSuccess] = useState(null); const [flaggedAddresses, setFlaggedAddresses] = useState(null); + const [potDetail, setPotDetail] = useState(null); + const [allDonations, setAlldonations] = useState(null); // set fund mathcing pool success const [fundDonation, setFundDonation] = useState(null); - const verifyIsOnRegistry = (address: any) => { - Near.asyncView("lists.potlock.near", "get_registrations_for_registrant", { - registrant_id: address, - }).then((registrations) => { - const registration = registrations.find( - (registration: any) => registration.list_id === 1, // potlock registry list id - ); - if (registration) { - setRegistryStatus(registration.status); - } - }); - }; - useEffect(() => { - if (!isDao) { - verifyIsOnRegistry(context.accountId || ""); + if (!potDetail) + getConfig({ + potId, + updateState: setPotDetail, + }); + if (!projects) + getPotProjects({ + potId, + isApprpved: true, + updateState: setProjects, + }); + if (potDetail && !flaggedAddresses) { + getFlaggedAccounts({ + potId, + potDetail, + type: "list", + updateState: setFlaggedAddresses, + }); } - }, []); + if (!allDonations && potDetail) + getDonations({ + potId, + potDetail, + updateState: setAlldonations, + }); + }, [potDetail]); + + if (potDetail === null) return
; // Handle fund success for web wallet + + const { + admins, + chef, + owner, + pot_name, + pot_description, + matching_pool_balance, + public_round_end_ms, + public_round_start_ms, + application_start_ms, + application_end_ms, + cooldown_end_ms: _cooldown_end_ms, + all_paid_out, + registry_provider, + } = potDetail; + useEffect(() => { if (accountId && transactionHashes) { getTransactionsFromHashes(transactionHashes, accountId).then((trxs) => { @@ -99,20 +114,31 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation } }, []); + const verifyIsOnRegistry = (address: any) => { + Near.asyncView("lists.potlock.near", "get_registrations_for_registrant", { + registrant_id: address, + }).then((registrations) => { + const registration = registrations.find( + (registration: any) => registration.list_id === 1, // potlock registry list id + ); + if (registration) { + setRegistryStatus(registration.status); + } + }); + }; + + useEffect(() => { + if (!isDao) { + verifyIsOnRegistry(context.accountId || ""); + } + }, []); + const projectNotRegistered = registryStatus === null; const userIsAdminOrGreater = admins.includes(accountId) || owner === accountId; const userIsChefOrGreater = userIsAdminOrGreater || chef === accountId; const existingApplication = PotSDK.getApplicationByProjectId(potId, context.accountId); - useEffect(() => { - if (!projects) { - PotSDK.asyncGetApprovedApplications(potId).then((projects: any) => { - setProjects(projects); - }); - } - }, []); - const applicationExists = existingApplication || applicationSuccess; const now = Date.now(); @@ -136,19 +162,6 @@ const Header = ({ potDetail, allDonations }: { potDetail: PotDetail; allDonation const payoutsChallenges = PotSDK.getPayoutsChallenges(potId); - if (!flaggedAddresses) { - PotSDK.getFlaggedAccounts(potDetail, potId) - .then((data) => { - const listOfFlagged: any = []; - data.forEach((adminFlaggedAcc: any) => { - const addresses = Object.keys(adminFlaggedAcc.potFlaggedAcc); - listOfFlagged.push(...addresses); - }); - setFlaggedAddresses(listOfFlagged); - }) - .catch((err) => console.log("error getting the flagged accounts ", err)); - } - const handleSetPayouts = () => { if (allDonations && flaggedAddresses !== null) { calculatePayouts(allDonations, matching_pool_balance, flaggedAddresses).then((calculatedPayouts: any) => { diff --git a/src/pages/Pot/components/HeaderStatus/HeaderStatus.tsx b/src/pages/Pot/components/HeaderStatus/HeaderStatus.tsx index ee8c34e9..4399580f 100644 --- a/src/pages/Pot/components/HeaderStatus/HeaderStatus.tsx +++ b/src/pages/Pot/components/HeaderStatus/HeaderStatus.tsx @@ -1,14 +1,24 @@ -import { useState } from "alem"; +import { useEffect, useParams, useState } from "alem"; import styled from "styled-components"; +import { getConfig } from "@app/services/getPotData"; import { PotDetail } from "@app/types"; import TimeLeft from "../TimeLeft"; import ProgressBar from "./ProgressBar"; import statsList from "./statsList"; import { Loader, State, Wrapper } from "./styles"; -const HeaderStatus = ({ potDetail }: { potDetail: PotDetail }) => { +const HeaderStatus = () => { + const { potId } = useParams(); const [mobileMenuActive, setMobileMenuActive] = useState(false); + const [potDetail, setPotDetail] = useState(null); + + useEffect(() => { + if (!potDetail) getConfig({ potId, updateState: setPotDetail }); + }, []); + + if (potDetail === null) return ""; + const stats = statsList(potDetail); const getIndexOfActive = () => { From 26d221de47dca0a6de7b6d03a0269e54dc1c88ab Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 23 May 2024 13:22:04 +0400 Subject: [PATCH 29/40] set the card back to statefill for now --- src/components/Card/Card.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 4508b9ae..a853df4a 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,4 +1,4 @@ -import { Big, RouteLink, Social, context } from "alem"; +import { Big, RouteLink, Social, context, useMemo, useParams } from "alem"; import DonateSDK from "@app/SDK/donate"; import PotSDK from "@app/SDK/pot"; import { useDonationModal } from "@app/hooks/useDonationModal"; @@ -32,7 +32,9 @@ import { } from "./styles"; const Card = (props: any) => { - const { payoutDetails, allowDonate: _allowDonate, potId } = props; + const { potId } = useParams(); + + const { payoutDetails, allowDonate: _allowDonate } = props; // Start Modals provider const Modals = useModals(); @@ -66,7 +68,7 @@ const Card = (props: any) => { return totalDonationAmountNear.toString(); }; - const totalAmountNear = getTotalAmountNear(); + const totalAmountNear = useMemo(getTotalAmountNear, [donationsForProject, payoutDetails]); const getImageSrc = (image: any) => { const defaultImageUrl = "https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci"; From 1f1eda0b7c67779e9bdc0c0e314ccb439a5c9391 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 23 May 2024 15:40:07 +0400 Subject: [PATCH 30/40] fix github url link in about section --- src/pages/Project/NavPages/About/About.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Project/NavPages/About/About.tsx b/src/pages/Project/NavPages/About/About.tsx index 2349d698..98be4cfa 100644 --- a/src/pages/Project/NavPages/About/About.tsx +++ b/src/pages/Project/NavPages/About/About.tsx @@ -41,7 +41,7 @@ const About = ({ projectId, accountId }: Props) => { return `https://${path}`; } // If the path does not contain "github.com/", assume it's a repository path and prepend the prefix - return `${prefix}${path}`; + return path; } const Github = () => From 8df9a1a2372832a8aee577fcd6e25d79a83e1748 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 24 May 2024 01:21:35 +0400 Subject: [PATCH 31/40] fix project success --- src/pages/CreateProject/CreateProject.tsx | 4 ++-- src/pages/CreateProject/components/CreateForm/CreateForm.tsx | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/CreateProject/CreateProject.tsx b/src/pages/CreateProject/CreateProject.tsx index 808b7ce8..57ddd976 100644 --- a/src/pages/CreateProject/CreateProject.tsx +++ b/src/pages/CreateProject/CreateProject.tsx @@ -5,8 +5,8 @@ import Header from "./components/Header/Header"; const CreateProject = () => { const { tab } = useParams(); - const registeration = ListsSDK.getRegistration(null, context.accountId); - const edit = tab === "editproject" || registeration; + // const registeration = ListsSDK.getRegistration(null, context.accountId); + const edit = tab === "editproject"; return ( <> diff --git a/src/pages/CreateProject/components/CreateForm/CreateForm.tsx b/src/pages/CreateProject/components/CreateForm/CreateForm.tsx index 9ed25af3..fb0f90ba 100644 --- a/src/pages/CreateProject/components/CreateForm/CreateForm.tsx +++ b/src/pages/CreateProject/components/CreateForm/CreateForm.tsx @@ -94,9 +94,7 @@ const CreateForm = (props: { edit: boolean }) => { } }, [policy]); - const registeredProject = useMemo(() => { - return ListsSDK.getRegistration(null, state.isDao ? state.daoAddress : context.accountId); - }, [state.isDao, state.daoAddress]); + const registeredProject = ListsSDK.getRegistration(null, state.isDao ? state.daoAddress : context.accountId); const proposals: any = checkDao ? Near.view(state.daoAddress, "get_proposals", { From b5f4a139de2f9265e329757c4aa7216b84a32c24 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 24 May 2024 01:45:03 +0400 Subject: [PATCH 32/40] fix double fetch on list sdk --- src/SDK/lists.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/SDK/lists.js b/src/SDK/lists.js index c0c86e16..c2e2875f 100644 --- a/src/SDK/lists.js +++ b/src/SDK/lists.js @@ -34,9 +34,7 @@ const ListsSDK = { const registration = registrations.find( (registration) => registration.list_id === (listId || potlockRegistryListId), ); - return Near.view(_listContractId, "get_registration", { - registration_id: registration.id, - }); + return registration; } }, asyncGetRegistration: (listId, registrantId) => { From 10430e41ebc3eeb382dca72d87434aa371d99551 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 24 May 2024 01:45:46 +0400 Subject: [PATCH 33/40] make card stateless --- src/components/Card/ButtonDonation.tsx | 36 +++++++++++++++++ src/components/Card/Card.tsx | 40 +++++-------------- src/components/Card/styles.ts | 26 +----------- .../ModalDonation/AmountInput/AmountInput.tsx | 2 +- .../ConfirmDirect/ConfirmDirect.tsx | 2 +- .../ModalDonation/ConfirmPot/ConfirmPot.tsx | 2 +- src/modals/ModalDonation/FormPot/FormPot.tsx | 2 +- src/{utils => modules}/nearToUsd.ts | 0 .../SuccessfullRegister.tsx | 2 +- .../Donor/NavPages/Donations/Donations.tsx | 2 +- .../DonorsLeaderboard/DonorsLeaderboard.tsx | 2 +- src/pages/Pot/NavPages/Projects/Projects.tsx | 2 +- src/pages/Pot/components/Header/Header.tsx | 2 +- .../PoolAllocationTable.tsx | 2 +- .../PoolAllocationTable/Table/Table.tsx | 2 +- .../SponsorsTable/SponsorsTable.tsx | 2 +- .../PotlockFunding/PotlockFunding.tsx | 2 +- src/utils/nearToUsdWithFallback.ts | 2 +- src/utils/yoctosToUsd.ts | 4 +- src/utils/yoctosToUsdWithFallback.ts | 2 +- 20 files changed, 65 insertions(+), 71 deletions(-) create mode 100644 src/components/Card/ButtonDonation.tsx rename src/{utils => modules}/nearToUsd.ts (100%) diff --git a/src/components/Card/ButtonDonation.tsx b/src/components/Card/ButtonDonation.tsx new file mode 100644 index 00000000..aae5ab5d --- /dev/null +++ b/src/components/Card/ButtonDonation.tsx @@ -0,0 +1,36 @@ +import { context, useEffect } from "alem"; +import { useDonationModal } from "@app/hooks/useDonationModal"; +import useModals from "@app/hooks/useModals"; +import Button from "../Button"; + +const ButtonDonation = ({ allowDonate, projectId }: { allowDonate: boolean; projectId: string }) => { + const Modals = useModals(); + const { setDonationModalProps } = useDonationModal(); + useEffect(() => {}, []); // make the component statefull so it does not break + return ( +
e.preventDefault()} + style={{ + cursor: "default", + color: "#292929", + }} + > + + {allowDonate && context.accountId && ( + + )} +
+ ); +}; + +export default ButtonDonation; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index a853df4a..99d990a0 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,8 +1,6 @@ -import { Big, RouteLink, Social, context, useMemo, useParams } from "alem"; +import { Big, RouteLink, Social, useEffect } from "alem"; import DonateSDK from "@app/SDK/donate"; import PotSDK from "@app/SDK/pot"; -import { useDonationModal } from "@app/hooks/useDonationModal"; -import useModals from "@app/hooks/useModals"; import routesPath from "@app/routes/routesPath"; import _address from "@app/utils/_address"; import getTagsFromSocialProfileData from "@app/utils/getTagsFromSocialProfileData"; @@ -12,6 +10,7 @@ import yoctosToUsdWithFallback from "@app/utils/yoctosToUsdWithFallback"; import CardSkeleton from "../../pages/Projects/components/CardSkeleton"; import Button from "../Button"; import Image from "../mob.near/Image"; +import ButtonDonation from "./ButtonDonation"; import { Amount, AmountDescriptor, @@ -32,17 +31,10 @@ import { } from "./styles"; const Card = (props: any) => { - const { potId } = useParams(); - - const { payoutDetails, allowDonate: _allowDonate } = props; - - // Start Modals provider - const Modals = useModals(); - const { setDonationModalProps } = useDonationModal(); + const { payoutDetails, allowDonate: _allowDonate, potId } = props; const projectId = props.project.registrant_id || props.projectId; const profile = Social.getr(`${projectId}/profile`) as any; - const allowDonate = _allowDonate ?? true; const MAX_DESCRIPTION_LENGTH = 80; @@ -68,7 +60,12 @@ const Card = (props: any) => { return totalDonationAmountNear.toString(); }; - const totalAmountNear = useMemo(getTotalAmountNear, [donationsForProject, payoutDetails]); + const totalAmountNear = getTotalAmountNear(); + // useMemo(getTotalAmountNear, [donationsForProject, payoutDetails]); + // console.log("totalAmountNear", totalAmountNear); + // console.log("profile", profile); + + if (profile === null || totalAmountNear === null) return ; const getImageSrc = (image: any) => { const defaultImageUrl = "https://ipfs.near.social/ipfs/bafkreih4i6kftb34wpdzcuvgafozxz6tk6u4f5kcr2gwvtvxikvwriteci"; @@ -102,11 +99,9 @@ const Card = (props: any) => { const tags = getTagsFromSocialProfileData(profile); - if (profile === null && totalAmountNear === null) return ; - return ( <> - + {/* */} @@ -181,20 +176,7 @@ const Card = (props: any) => { {payoutDetails.donorCount === 1 ? "Donor" : "Donors"} )} - {allowDonate && context.accountId && ( - - )} + {payoutDetails && ( diff --git a/src/components/Card/styles.ts b/src/components/Card/styles.ts index 3cd0fb1c..3d90e69a 100644 --- a/src/components/Card/styles.ts +++ b/src/components/Card/styles.ts @@ -16,7 +16,7 @@ export const CardContainer = styled.div` margin-right: auto; transition: all 300ms; &:hover { - transform: translateY(-1rem); + /* transform: translateY(-1rem); */ } `; @@ -111,30 +111,6 @@ export const DonationsInfoItem = styled.div` align-items: center; `; -export const DonationButton = styled.button` - flex-direction: row; - justify-content: center; - align-items: center; - padding: 16px 24px; - background: #fef6ee; - overflow: hidden; - box-shadow: 0px -2.700000047683716px 0px #4a4a4a inset; - border-radius: 6px; - border: 1px solid #4a4a4a; - gap: 8px; - display: inline-flex; - text-align: center; - color: #2e2e2e; - font-size: 14px; - line-height: 16px; - font-weight: 600; - - &:hover { - text-decoration: none; - cursor: pointer; - } -`; - export const Amount = styled.div` font-size: 17px; font-weight: 600; diff --git a/src/modals/ModalDonation/AmountInput/AmountInput.tsx b/src/modals/ModalDonation/AmountInput/AmountInput.tsx index a49add35..2f966e6a 100644 --- a/src/modals/ModalDonation/AmountInput/AmountInput.tsx +++ b/src/modals/ModalDonation/AmountInput/AmountInput.tsx @@ -1,6 +1,6 @@ import NearIcon from "@app/assets/svgs/near-icon"; import Select from "@app/components/Inputs/Select/Select"; -import nearToUsd from "@app/utils/nearToUsd"; +import nearToUsd from "@app/modules/nearToUsd"; import { Container, DropdownWrapper, PotDenomination } from "./styles"; const AmountInput = (props: any) => { diff --git a/src/modals/ModalDonation/ConfirmDirect/ConfirmDirect.tsx b/src/modals/ModalDonation/ConfirmDirect/ConfirmDirect.tsx index af4227eb..13ce6fdc 100644 --- a/src/modals/ModalDonation/ConfirmDirect/ConfirmDirect.tsx +++ b/src/modals/ModalDonation/ConfirmDirect/ConfirmDirect.tsx @@ -8,8 +8,8 @@ import CheckBox from "@app/components/Inputs/Checkbox/Checkbox"; import TextArea from "@app/components/Inputs/TextArea/TextArea"; import ProfileImage from "@app/components/mob.near/ProfileImage"; import constants from "@app/constants"; +import nearToUsd from "@app/modules/nearToUsd"; import hrefWithParams from "@app/utils/hrefWithParams"; -import nearToUsd from "@app/utils/nearToUsd"; import { Amout, ButtonWrapper, Container, FeesRemoval, Label, NoteWrapper } from "./styles"; const ConfirmDirect = ({ diff --git a/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx b/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx index b666aac5..71db8b52 100644 --- a/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx +++ b/src/modals/ModalDonation/ConfirmPot/ConfirmPot.tsx @@ -5,9 +5,9 @@ import Button from "@app/components/Button"; import BreakdownSummary from "@app/components/Cart/BreakdownSummary/BreakdownSummary"; import CheckBox from "@app/components/Inputs/Checkbox/Checkbox"; import ProfileImage from "@app/components/mob.near/ProfileImage"; +import nearToUsd from "@app/modules/nearToUsd"; import _address from "@app/utils/_address"; import hrefWithParams from "@app/utils/hrefWithParams"; -import nearToUsd from "@app/utils/nearToUsd"; import { Amout, ButtonWrapper, diff --git a/src/modals/ModalDonation/FormPot/FormPot.tsx b/src/modals/ModalDonation/FormPot/FormPot.tsx index 8efeafdd..b418c4d5 100644 --- a/src/modals/ModalDonation/FormPot/FormPot.tsx +++ b/src/modals/ModalDonation/FormPot/FormPot.tsx @@ -2,9 +2,9 @@ import { Near, Social, context } from "alem"; import Button from "@app/components/Button"; import ProfileImage from "@app/components/mob.near/ProfileImage"; import constants from "@app/constants"; +import nearToUsd from "@app/modules/nearToUsd"; import _address from "@app/utils/_address"; import hrefWithParams from "@app/utils/hrefWithParams"; -import nearToUsd from "@app/utils/nearToUsd"; import AmountInput from "../AmountInput/AmountInput"; import Alert from "../Banners/Alert"; import VerifyInfo from "../Banners/VerifyInfo"; diff --git a/src/utils/nearToUsd.ts b/src/modules/nearToUsd.ts similarity index 100% rename from src/utils/nearToUsd.ts rename to src/modules/nearToUsd.ts diff --git a/src/pages/CreateProject/components/SuccessfullRegister/SuccessfullRegister.tsx b/src/pages/CreateProject/components/SuccessfullRegister/SuccessfullRegister.tsx index ac521447..fad04a89 100644 --- a/src/pages/CreateProject/components/SuccessfullRegister/SuccessfullRegister.tsx +++ b/src/pages/CreateProject/components/SuccessfullRegister/SuccessfullRegister.tsx @@ -11,7 +11,7 @@ const SuccessfullRegister = ({ registeredProject }: { registeredProject: any })