From fc27b13613a7137f150b694f030f1426ae498f33 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Mon, 1 Apr 2024 16:33:01 +0400 Subject: [PATCH 01/16] add date field to external funding --- apps/potlock/widget/Project/CreateForm.jsx | 229 +++++++++++++----- .../widget/Project/ExternalFunding.jsx | 5 +- .../widget/Project/ModalAddFundingSource.jsx | 38 ++- 3 files changed, 200 insertions(+), 72 deletions(-) diff --git a/apps/potlock/widget/Project/CreateForm.jsx b/apps/potlock/widget/Project/CreateForm.jsx index 774793d4..cb62f5ed 100644 --- a/apps/potlock/widget/Project/CreateForm.jsx +++ b/apps/potlock/widget/Project/CreateForm.jsx @@ -278,30 +278,91 @@ const FundingHeaderItemText = styled.div` word-wrap: break-word; `; -const TableRow = styled.div` +const Table = styled.div` display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 100%; -`; - -const TableRowItem = styled.div` - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - gap: 20px; - padding: 20px; - width: ${100 / FUNDING_SOURCE_COLUMNS.length}%; -`; - -const TableRowText = styled.div` - color: #292929; - font-size: 14px; - font-weight: 400; - line-height: 24px; - word-wrap: break-word; + flex-direction: column; + border-radius: 6px; + border: 1px solid #7b7b7b; + background: #fff; + .header, + .fudning-row { + display: flex; + justify-content: space-between; + } + .header { + border-bottom: 0.5px solid #7b7b7b; + } + .fudning-row:not(:last-of-type) { + border-bottom: 0.5px solid #7b7b7b; + } + .item { + width: 140px; + display: flex; + align-items: center; + &:nth-of-type(1) { + width: 190px; + } + &:nth-of-type(2) { + flex: 1; + } + } + .source { + width: 190px; + flex-direction: column; + align-items: flex-start; + gap: 4px; + div { + font-weight: 600; + } + div:last-of-type { + color: #7b7b7b; + font-weight: 400; + } + } + .amount { + display: flex; + gap: 0.5rem; + justify-content: flex-end; + div:last-child { + font-weight: 600; + } + } + .btns { + width: 95px; + gap: 2rem; + justify-content: space-between; + svg { + cursor: pointer; + path { + transition: 300ms ease-in-out; + } + &:hover { + path { + fill: black; + } + } + } + } + .header .item { + padding: 10px 1rem; + color: #7b7b7b; + font-weight: 600; + } + .fudning-row .item { + padding: 1rem 1rem; + } + @media only screen and (max-width: 769px) { + .header { + display: none; + } + .fudning-row { + flex-direction: column; + } + .item { + width: 100%; + justify-content: flex-start; + } + } `; State.init({ @@ -896,6 +957,8 @@ const uploadFileUpdateState = (body, callback) => { // console.log("state in create form: ", state); +console.log(state.fundingSources); + return ( {!state.socialDataFetched || !registrations ? ( @@ -1430,55 +1493,97 @@ return ( */} {state.fundingSources.length > 0 && ( - - {FUNDING_SOURCE_COLUMNS.map((column, index) => ( - - {column} - - ))} - - )} - {state.fundingSources.map( - ({ investorName, description, amountReceived, denomination }, index) => { - return ( - - - {investorName} - - - {description} - - - {amountReceived} - - - {denomination} - {/* { + +
+
Funding source
+
Description
+
Amount
+
+
+ {state.fundingSources.map((funding, idx) => ( +
+
+
{funding.investorName}
+ {funding.date && ( +
+ {new Date(funding.date).toLocaleDateString("en-US", { + month: "numeric", + day: "numeric", + year: "numeric", + })} +
+ )} +
+
{funding.description}
+
+
{funding.denomination}
+
{funding.amountReceived}
+
+
+ {/* Edit Button */} + State.update({ - fundingSourceIndex: index, - }); - }} - /> */} - + + + + + + + + + + + {/* Delete Button */} + { const updatedFundingSources = state.fundingSources.filter( - (fs, i) => i != index + (fudning, i) => i !== idx ); - State.update({ fundingSources: updatedFundingSources }); + State.update({ + fundingSources: updatedFundingSources, + }); }} - /> - - - ); - } + width="14" + height="18" + viewBox="0 0 14 18" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + + +
+
+ ))} +
)} !fs.investorName || !fs.amountReceived || !fs.denomination || !fs.description @@ -1641,6 +1746,7 @@ return ( fundingSourceIndex: state.fundingSourceIndex, handleAddFundingSource: ({ investorName, + date, description, amountReceived, denomination, @@ -1649,6 +1755,7 @@ return ( if (i == state.fundingSourceIndex) { return { investorName, + date, description, amountReceived, denomination, diff --git a/apps/potlock/widget/Project/ExternalFunding.jsx b/apps/potlock/widget/Project/ExternalFunding.jsx index 0fb10b25..ab18c25c 100644 --- a/apps/potlock/widget/Project/ExternalFunding.jsx +++ b/apps/potlock/widget/Project/ExternalFunding.jsx @@ -140,7 +140,7 @@ const ArrowDown = (props) => ( ); -const externalTableTabs = ["funding Source", "description", "amount"]; +const externalTableTabs = ["funding Source", "description", "date", "amount"]; return ( @@ -165,11 +165,12 @@ return ( ))} - {externalFunding.map(({ investorName, description, amountReceived, denomination }) => ( + {externalFunding.map(({ investorName, description, date, amountReceived, denomination }) => (
{investorName}
{description}
+
{date ?? "No specified date"}
{parseFloat(amountReceived).toLocaleString() + " " + denomination}{" "} diff --git a/apps/potlock/widget/Project/ModalAddFundingSource.jsx b/apps/potlock/widget/Project/ModalAddFundingSource.jsx index 654e270b..da3a596b 100644 --- a/apps/potlock/widget/Project/ModalAddFundingSource.jsx +++ b/apps/potlock/widget/Project/ModalAddFundingSource.jsx @@ -7,26 +7,26 @@ const { fundingSourceIndex, } = props; -// console.log("props in add funding source modal: ", props); - -State.init({ +const initalState = { investorName: fundingSources[fundingSourceIndex]?.investorName || "", investorNameError: "", + date: fundingSources[fundingSourceIndex]?.date || "", + dateError: "", description: fundingSources[fundingSourceIndex]?.description || "", descriptionError: "", denomination: fundingSources[fundingSourceIndex]?.denomination || "", denominationError: "", amountReceived: fundingSources[fundingSourceIndex]?.amountReceived || "", amountReceivedError: "", -}); +}; + +State.init(initalState); -// console.log("state in add funding source modal: ", state); +useEffect(() => { + State.update(initalState); +}, [fundingSources, fundingSourceIndex]); const IPFS_BASE_URL = "https://nftstorage.link/ipfs/"; -// const MONEY_ICON_URL = -// IPFS_BASE_URL + "bafkreiem3zqv4smaflel54lwtl65d7zbulkan3vnfor4fi2sqn3n5p7tpe"; -// const CLOSE_ICON_URL = -// IPFS_BASE_URL + "bafkreifyg2vvmdjpbhkylnhye5es3vgpsivhigkjvtv2o4pzsae2z4vi5i"; const ModalHeader = styled.div` display: flex; @@ -144,6 +144,22 @@ return ( error: state.investorNameError, }} /> + + Date (optional) + + ), + + // placeholder: "0", // TODO: possibly add this back in + selectTime: false, + value: state.date, + onChange: (date) => State.update({ date: date }), + error: state.dateError, + }} + /> { const fundingSource = { investorName: state.investorName, + date: state.date, description: state.description, denomination: state.denomination, amountReceived: state.amountReceived, @@ -225,6 +243,8 @@ return ( State.update({ investorName: "", investorNameError: "", + date: "", + dateError: "", description: "", descriptionError: "", denomination: "", From 3f4001fb2825353927407acd70fecd83bf3c0f24 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 3 Apr 2024 14:53:37 +0400 Subject: [PATCH 02/16] donation modal v2 form page --- apps/potlock/widget/Cart/Modal.jsx | 8 +- apps/potlock/widget/Inputs/Dropdown.jsx | 2 +- apps/potlock/widget/Inputs/Select.jsx | 12 +- apps/potlock/widget/Inputs/Text.jsx | 2 - .../widget/ModalDonation/AmountInput.jsx | 127 ++++++ .../widget/ModalDonation/ConfirmPage.jsx | 48 ++ apps/potlock/widget/ModalDonation/Form.jsx | 286 ++++++++++++ apps/potlock/widget/ModalDonation/Main.jsx | 421 ++++++++++++++++++ apps/potlock/widget/Project/Card.jsx | 2 +- apps/potlock/widget/Project/ModalDonation.jsx | 4 - apps/potlock/widget/SDK/pot.jsx | 6 + 11 files changed, 898 insertions(+), 20 deletions(-) create mode 100644 apps/potlock/widget/ModalDonation/AmountInput.jsx create mode 100644 apps/potlock/widget/ModalDonation/ConfirmPage.jsx create mode 100644 apps/potlock/widget/ModalDonation/Form.jsx create mode 100644 apps/potlock/widget/ModalDonation/Main.jsx diff --git a/apps/potlock/widget/Cart/Modal.jsx b/apps/potlock/widget/Cart/Modal.jsx index 1ebfc447..781076dc 100644 --- a/apps/potlock/widget/Cart/Modal.jsx +++ b/apps/potlock/widget/Cart/Modal.jsx @@ -22,7 +22,8 @@ const ModalOverlay = styled.div` left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.4); + background: rgba(255, 255, 255, 0.3); + backdrop-filter: blur(4px); display: flex; flex-direction: column; justify-content: flex-start; @@ -38,10 +39,11 @@ const ModalOverlay = styled.div` const ModalContainer = styled.div` width: 383px; + border-radius: 12px; + box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), + 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); padding: 24px 0px; background: white; - border-radius: 10px; - box-shadow: 20px 20px 32px -4px rgba(93, 93, 93, 0.06); display: flex; flex-direction: column; justify-content: flex-start; diff --git a/apps/potlock/widget/Inputs/Dropdown.jsx b/apps/potlock/widget/Inputs/Dropdown.jsx index 737d50a5..b15897fb 100644 --- a/apps/potlock/widget/Inputs/Dropdown.jsx +++ b/apps/potlock/widget/Inputs/Dropdown.jsx @@ -78,7 +78,7 @@ return ( <> {openFilter && setOpenFilter(false)} />}
setOpenFilter(!openFilter)}> - + {sortVal || title} {placeholder}} /> - {/* {props.iconRight ? ( - {props.iconRight} - ) : ( */} + - {/* )} */} diff --git a/apps/potlock/widget/Inputs/Text.jsx b/apps/potlock/widget/Inputs/Text.jsx index f4126a87..547323c9 100644 --- a/apps/potlock/widget/Inputs/Text.jsx +++ b/apps/potlock/widget/Inputs/Text.jsx @@ -90,8 +90,6 @@ return ( {label && } - {/* {props.prefixText && {props.prefixText}} */} - {/* {props.prefixElement && props.prefixElement} */} {props.preInputChildren && props.preInputChildren} + ) : ( + + ), + }} + /> + +); + +const AmountInput = (props) => { + const { value, HandleAmoutChange, donationType, denominationOptions } = props; + return ( + + HandleAmoutChange(e.target.value)} /> +
{nearToUsd ? `~$ ${nearToUsd * value}` : ""}
+ {donationType === "pot" ? ( + + +
{denominationOptions[0].text}
+
+ ) : ( + + )} +
+ ); +}; + +return { AmountInput }; diff --git a/apps/potlock/widget/ModalDonation/ConfirmPage.jsx b/apps/potlock/widget/ModalDonation/ConfirmPage.jsx new file mode 100644 index 00000000..b8a58151 --- /dev/null +++ b/apps/potlock/widget/ModalDonation/ConfirmPage.jsx @@ -0,0 +1,48 @@ +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 1.5rem; + padding: 1.5rem 2rem; +`; + +const Label = styled.div` + font-weight: 500; + margin-bottom: 4px; +`; +const Amout = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + div { + font-weight: 600; + font-size: 1rem; + &:last-of-type { + color: #7b7b7b; + } + } + img, + svg { + width: 20px; + } +`; +const ConfirmPage = (props) => { + const { selectedDenomination, amount } = props; + + return ( +
+
+ + +
+ {selectedDenomination.icon ? : } +
+
{amount}
+
+
+
+ ); +}; + +return { + ConfirmPage, +}; diff --git a/apps/potlock/widget/ModalDonation/Form.jsx b/apps/potlock/widget/ModalDonation/Form.jsx new file mode 100644 index 00000000..c7e19e2a --- /dev/null +++ b/apps/potlock/widget/ModalDonation/Form.jsx @@ -0,0 +1,286 @@ +const { ownerId, NADABOT_HUMAN_METHOD } = VM.require("potlock.near/widget/constants") || { + ownerId: "", + NADABOT_HUMAN_METHOD: "", +}; + +const { AmountInput } = VM.require(`potlock.near/widget/ModalDonation.AmountInput`) || { + AmountInput: () => {}, +}; + +const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { + getConfig: () => {}, +}; + +const Form = styled.div` + display: flex; + flex-direction: column; + padding: 1.5rem 2rem; +`; + +const Label = styled.div` + font-weight: 500; + margin-bottom: 0.5rem; + margin-top: 0.5rem; +`; + +const DonationType = styled.div` + display: flex; + flex-direction: column; + gap: 0.75rem; + > div { + display: flex; + border-radius: 8px; + align-items: center; + gap: 0.5rem; + padding: 1rem; + cursor: pointer; + color: #a6a6a6; + background: #f6f5f3; + border: 1px solid transparent; + &.active { + box-shadow: 0px 0px 1.4px 2px #fee6e5; + background: white; + color: #dd3345; + border-color: #dd3345; + } + &.disabled { + pointer-events: none; + } + } +`; + +const CheckBox = styled.div` + width: 20px; + height: 20px; + border: 2px solid #d9d9d9; + display: flex; + border-radius: 50%; + div { + width: 10px; + height: 10px; + background: transparent; + border-radius: 50%; + margin: auto; + } + &.active { + border-color: #dd3345; + div { + background: #dd3345; + } + } +`; + +const Button = styled.div` + display: flex; + margin-top: 4rem; + margin-bottom: 0.5rem; + button { + padding: 12px 16px; + width: 100%; + font-weight: 500; + } +`; + +const CurrentBalance = styled.div` + display: flex; + margin-top: 0.5rem; + gap: 0.5rem; + justify-content: flex-end; + div:last-of-type { + color: #7b7b7b; + } +`; + +const PotWrapper = styled.div` + display: flex; + flex-direction: column; + margin-top: 1.5rem; +`; + +const PotSelector = styled.div` + display: flex; +`; + +const Pot = styled.div` + border-radius: 6px; + border: 1px solid #dbdbdb; + border-bottom-width: 2px; + background: #fff; + padding: 0.75rem 1rem; +`; + +const SelectPot = ({ selectedRound, activeRoundsOptions, updateState }) => ( + + { + updateState({ + selectedRound: val, + }); + }, + }} + /> + +); + +const FormPage = (props) => { + const { + recipientId, + profile, + amount, + amountError, + denominationOptions, + updateState, + selectedDenomination, + donationType, + ftBalance, + activeRounds, + activeRound, + NADABOT_CONTRACT_ID, + updateTab, + } = props; + + const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { + account_id: context.accountId, + }); + + const donationTypes = [ + { + label: "Direct donation", + val: "direct", + disabled: false, + }, + { + label: "Quadratically matched donation", + val: "pot", + disabled: !activeRound && (!activeRounds || activeRounds.length === 0), + }, + ]; + + const activeRoundsOptions = {}; + + (activeRounds || []).forEach((round) => { + activeRoundsOptions[round] = { + label: PotSDK.getConfig(round)?.pot_name || round, + val: round, + }; + }); + + const passedPotConfig = PotSDK.getConfig(activeRound); + + const isFtDonation = selectedDenomination.text !== "NEAR"; + + const HandleAmoutChange = (amount) => { + amount = amount.replace(/[^\d.]/g, ""); // remove all non-numeric characters except for decimal + if (amount === ".") amount = "0."; + updateState({ amount, amountError: "" }); + // error if amount is greater than balance + if (Big(amount).mul(Big(10).pow(selectedDenomination.decimals)).gt(ftBalance)) { + updateState({ amountError: "Insufficient balance" }); + } else if (!isFtDonation && parseFloat(amount) < 0.1) { + updateState({ amountError: "Minimum donation is 0.1 NEAR" }); + } + }; + + const isLoading = isUserHumanVerified === null || activeRounds === null; + + return recipientId ? ( + profile === null ? ( + + ) : ( +
+ + + {donationTypes.map((type) => ( +
+ updateState({ + donationType: type.val, + }) + } + className={`${donationType === type.val ? "active" : ""} ${ + type.disabled ? "disabled" : "" + }`} + > + +
+
+
+ {type.label} {type.disabled && (no pots available) } +
+
+ ))} +
+ {donationType === "pot" && ( + + + {(activeRound || activeRounds?.length === 1) && ( + {Object.values(activeRoundsOptions)[0]?.label} + )} + {activeRounds?.length > 1 && ( + + )} + + )} + + + + +
+ {ftBalance} {selectedDenomination.text} +
+
available
+
+ + + ) + ) : ( + "" + ); +}; + +return { + FormPage, +}; diff --git a/apps/potlock/widget/ModalDonation/Main.jsx b/apps/potlock/widget/ModalDonation/Main.jsx new file mode 100644 index 00000000..b1ab82dd --- /dev/null +++ b/apps/potlock/widget/ModalDonation/Main.jsx @@ -0,0 +1,421 @@ +const Container = styled.div` + display: flex; + flex-direction: column; + border-radius: 12px; + background: #fff; + font-size: 14px; + box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), + 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); + overflow: hidden; + border-radius: 6px; +`; + +const Banner = styled.div` + position: relative; + display: flex; + flex-direction: column; + padding: 1.5rem 2rem; + gap: 0.5rem; + overflow: hidden; + background: #dd3345; + color: white; + font-size: 22px; + .left-pattern { + position: absolute; + left: 0; + top: 0; + width: 30%; + transform: translate(-10%, -10%) scaleX(-1); + pointer-events: none; + } + .right-pattern { + position: absolute; + right: 0; + top: 0; + width: 30%; + transform: translate(10%, -10%); + pointer-events: none; + } +`; + +const BannerBg = (props) => ( + + + + + + + +); + +const HeaderIcons = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + svg { + width: 14px; + cursor: pointer; + transition: all 300ms ease-in-out; + } + .close-icon { + margin-left: auto; + &:hover { + rotate: 90deg; + } + } + svg:first-of-type { + transform: translateX(-4px); + } +`; + +const DENOMINATION_OPTIONS = [{ text: "NEAR", value: "NEAR", decimals: 24 }]; + +const { recipientId, referrerId, potId, onClose, NADABOT_CONTRACT_ID, POT } = props; + +const DEFAULT_DONATION_AMOUNT = "1"; + +const initialState = { + amount: DEFAULT_DONATION_AMOUNT, + donationType: "direct", + showBreakdown: false, + bypassProtocolFee: false, + bypassChefFee: false, + addNote: false, + donationNote: "", + donationNoteError: "", + allPots: null, + detailForPots: {}, + approvedProjectsForPots: {}, + activeRoundsForProject: potId ? [potId] : null, // mapping of potId to { potDetail } + intervalId: null, + ftBalances: null, + selectedDenomination: DENOMINATION_OPTIONS[0], + denominationOptions: DENOMINATION_OPTIONS, + selectedRound: "", + currentPage: "form", +}; + +State.init(initialState); + +const { + amount, + denomination, + donationType, + showBreakdownm, + bypassProtocolFee, + bypassChefFee, + addNote, + donationNote, + donationNoteError, + allPots, + detailForPots, + approvedProjectsForPots, + activeRoundsForProject, + intervalId, + nearBalance, + ftBalances, + denominationOptions, + selectedDenomination, + selectedRound, + currentPage, +} = state; + +const [activeRounds, setActiveRounds] = useState(null); + +const profile = Social.getr(`${recipientId}/profile`); +const profileName = profile?.name || recipientId; + +const MAX_NOTE_LENGTH = 60; + +const { ownerId, DONATION_CONTRACT_ID, NADABOT_HUMAN_METHOD, NADA_BOT_URL, SUPPORTED_FTS } = + VM.require("potlock.near/widget/constants") || { + DONATION_CONTRACT_ID: "", + NADABOT_HUMAN_METHOD: "", + ownerId: "", + NADA_BOT_URL: "", + SUPPORTED_FTS: {}, + }; + +let ListsSDK = + VM.require("potlock.near/widget/SDK.lists") || + (() => ({ + getRegistrations: () => {}, + })); +ListsSDK = ListsSDK({ env: props.env }); + +const projects = ListsSDK.getRegistrations() || []; + +let DonateSDK = + VM.require("potlock.near/widget/SDK.donate") || + (() => ({ + getConfig: () => {}, + asyncGetDonationsForDonor: () => {}, + })); +DonateSDK = DonateSDK({ env: props.env }); + +let PotFactorySDK = + VM.require("potlock.near/widget/SDK.potfactory") || + (() => ({ + getPots: () => {}, + })); + +PotFactorySDK = PotFactorySDK({ env: props.env }); + +const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { + getConfig: () => {}, + asyncGetConfig: () => {}, + getApprovedApplications: () => {}, + asyncGetApplicationByProjectId: () => {}, + asyncGetDonationsForDonor: () => {}, + isRoundActive: () => {}, +}; + +const { nearToUsd, formatWithCommas } = VM.require("potlock.near/widget/utils") || { + nearToUsd: 1, + formatWithCommas: () => {}, +}; + +const { addItemsToCart, clearCart } = VM.require("potlock.near/widget/SDK.cart") || { + addItemsToCart: () => {}, + clearCart: () => {}, +}; + +const { FormPage } = VM.require("potlock.near/widget/ModalDonation.Form") || { + FormPage: () => {}, +}; +const { ConfirmPage } = VM.require("potlock.near/widget/ModalDonation.ConfirmPage") || { + ConfirmPage: () => {}, +}; + +const pages = { + form: FormPage, + confirm: ConfirmPage, +}; + +const ActivePageComponent = pages[currentPage]; + +// get all active pots +const pots = useCache( + () => + // get all pots + PotFactorySDK.asyncGetPots() + .then((pots) => { + const activePots = pots.map((pot) => + // if active + PotSDK.isRoundActive(pot.id) + // check if project had applied + .then((isActive) => isActive && pot.id) + .catch((e) => { + console.error("error checking active round for pot: " + pot.id, e); + }) + ); + return Promise.all(activePots); + }) + .catch((e) => { + console.error("error getting pots: ", e); + }), + "active-pots" +); + +if (!activeRound && !activeRounds) { + (pots ?? []).forEach((pot, idx) => { + if (pot) { + PotSDK.asyncGetApplicationByProjectId(pot, recipientId) + .then((application) => { + if (application.status === "Approved") { + setActiveRounds((prev) => { + const prevRounds = prev || []; + if (!prevRounds.includes(pot)) { + return [...prevRounds, pot]; + } + }); + if (!selectedRound) + State.update({ + selectedRound: pot, + }); + } else if (pots.length - 1 === idx && !activeRounds) { + setActiveRounds((prev) => [...(prev || [])]); + } + }) + .catch((err) => {}); + } + }); +} +// Get Ft Balances +useEffect(() => { + if (!ftBalances) { + console.log("ftBalancesRes", ftBalances); + + asyncFetch( + `https://near-mainnet.api.pagoda.co/eapi/v1/accounts/${context.accountId}/balances/FT`, + { + headers: { + "Content-Type": "application/json", + "x-api-key": "dce81322-81b0-491d-8880-9cfef4c2b3c2", + }, + } + ) + .then((ftBalancesRes) => { + console.log("ftBalancesRes", ftBalancesRes); + + if (ftBalancesRes) { + const ftBalances = ftBalancesRes.body.balances; + State.update({ + ftBalances: ftBalances, + denominationOptions: DENOMINATION_OPTIONS.concat( + ftBalances + .map(({ amount, contract_account_id, metadata }) => ({ + amount, + id: contract_account_id, + text: metadata.symbol, + value: metadata.symbol, + icon: metadata.icon, + decimals: metadata.decimals, + })) + .filter((option) => option.text.length < 10) + ), + }); + } + }) + .catch((err) => console.log("fetching Ft balances faild")); + } +}, [ftBalances]); + +const nearBalanceRes = fetch( + `https://near-mainnet.api.pagoda.co/eapi/v1/accounts/${context.accountId}/balances/NEAR`, + { + headers: { + "Content-Type": "application/json", + "x-api-key": "dce81322-81b0-491d-8880-9cfef4c2b3c2", + }, + } +); + +const ftBalance = useMemo(() => { + if (selectedDenomination.text === "NEAR") { + const nearBalance = nearBalanceRes?.body?.balance; + + return nearBalance + ? formatWithCommas(Big(nearBalance.amount).div(Big(10).pow(24)).toFixed(2)) + : "-"; + } + const balance = denominationOptions.find( + // this is where we need the details + (option) => option.text === selectedDenomination.text + ); + return balance + ? formatWithCommas(Big(balance.amount).div(Big(10).pow(balance.decimals)).toFixed(2)) + : "-"; +}, [selectedDenomination, ftBalances, nearBalanceRes]); + +return ( + { + e.stopPropagation(); + }, + contentStyle: { + padding: "0px", + }, + children: ( + + + + + + {currentPage !== "form" && ( + + State.update({ + currentPage: "form", + }) + } + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + + + )} + + onClose()} + className="close-icon" + viewBox="0 0 14 14" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + + + + {currentPage !== "form" ? ( +
Confirm donation
+ ) : ( +
Donate to {profileName}
+ )} +
+ +
+ ), + }} + /> +); diff --git a/apps/potlock/widget/Project/Card.jsx b/apps/potlock/widget/Project/Card.jsx index 2f45978f..2c49388d 100644 --- a/apps/potlock/widget/Project/Card.jsx +++ b/apps/potlock/widget/Project/Card.jsx @@ -543,7 +543,7 @@ return ( {state.donateModal.isOpen && ( { + return Near.asyncView(potId, "is_round_active", {}); + }, getMatchingPoolDonations: (potId) => { // TODO: paginate return Near.view(potId, "get_matching_pool_donations", {}); @@ -51,6 +54,9 @@ return { getApplicationByProjectId: (potId, projectId) => { return Near.view(potId, "get_application_by_project_id", { project_id: projectId }); }, + asyncGetApplicationByProjectId: (potId, projectId) => { + return Near.asyncView(potId, "get_application_by_project_id", { project_id: projectId }); + }, getApprovedApplications: (potId) => { return Near.view(potId, "get_approved_applications", {}); }, From a3f2728f70a293ad10eecb4ab0b6caa20b42876c Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Wed, 3 Apr 2024 18:29:02 +0400 Subject: [PATCH 03/16] confirm page --- apps/potlock/widget/Cart/BreakdownSummary.jsx | 73 +++---- .../widget/ModalDonation/ConfirmPage.jsx | 198 +++++++++++++++++- apps/potlock/widget/ModalDonation/Form.jsx | 25 ++- apps/potlock/widget/ModalDonation/Main.jsx | 6 +- apps/potlock/widget/Project/ModalDonation.jsx | 4 +- 5 files changed, 236 insertions(+), 70 deletions(-) diff --git a/apps/potlock/widget/Cart/BreakdownSummary.jsx b/apps/potlock/widget/Cart/BreakdownSummary.jsx index 35d8b143..271887ff 100644 --- a/apps/potlock/widget/Cart/BreakdownSummary.jsx +++ b/apps/potlock/widget/Cart/BreakdownSummary.jsx @@ -30,8 +30,8 @@ const CHEVRON_UP_URL = IPFS_BASE_URL + "bafkreibdm7w6zox4znipjqlmxr66wsjjpqq4dguswo7evvrmzlnss3c3vi"; const SvgIcon = styled.svg` - width: 20px; - height: 20px; + width: 16px; + height: 16px; `; const BreakdownSummary = styled.div` @@ -46,9 +46,8 @@ const BreakdownSummary = styled.div` width: 100%; margin-top: 8px; gap: 12px; - border-radius: 8px; border: 1px #dbdbdb solid; - background: #fafafa; + border-radius: 8px; transition: all 300ms ease-in-out; &.hidden { visibility: hidden; @@ -62,7 +61,7 @@ const BreakdownSummary = styled.div` const Header = styled.div` display: flex; flex-direction: row; - justify-content: flex-end; + justify-content: flex-start; align-items: center; width: 100%; `; @@ -70,8 +69,7 @@ const Header = styled.div` const BreakdownTitle = styled.div` color: #2e2e2e; font-size: 14px; - line-height: 16px; - font-weight: 600; + font-weight: 500; word-wrap: break-word; `; @@ -113,7 +111,6 @@ const BreakdownItem = styled.div` const BreakdownItemLeft = styled.div` color: #7b7b7b; font-size: 14px; - line-height: 20px; font-weight: 400; word-wrap: break-word; `; @@ -121,8 +118,8 @@ const BreakdownItemLeft = styled.div` const BreakdownItemRight = styled.div` display: flex; flex-direction: row; - // justify-content: flex-end; align-items: center; + font-weight: 500; gap: 8px; `; @@ -140,23 +137,24 @@ const Icon = styled.img` `; const NearIcon = (props) => ( -
+ )} + {addNote ? ( { }} /> ) : ( - + updateState({ addNote: true })}> { /> -
updateState({ addNote: true })}>Add Note
+
Add Note
)} + ); }; diff --git a/apps/potlock/widget/ModalDonation/Form.jsx b/apps/potlock/widget/ModalDonation/Form.jsx index e8e12498..d0f80094 100644 --- a/apps/potlock/widget/ModalDonation/Form.jsx +++ b/apps/potlock/widget/ModalDonation/Form.jsx @@ -156,11 +156,11 @@ const FormPage = (props) => { activeRounds, activeRound, NADABOT_CONTRACT_ID, - updateTab, + accountId, } = props; const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { - account_id: context.accountId, + account_id: accountId, }); const donationTypes = [ @@ -279,9 +279,6 @@ const FormPage = (props) => { ? "Nah, I want to have less impact" : "Proceed to donate", onClick: () => updateState({ currentPage: "confirm" }), - style: { - padding: "12px 16px", - }, }} /> diff --git a/apps/potlock/widget/ModalDonation/Main.jsx b/apps/potlock/widget/ModalDonation/Main.jsx index 859e09e6..d9dce402 100644 --- a/apps/potlock/widget/ModalDonation/Main.jsx +++ b/apps/potlock/widget/ModalDonation/Main.jsx @@ -108,8 +108,12 @@ const HeaderIcons = styled.div` rotate: 90deg; } } - svg:first-of-type { - transform: translateX(-4px); + div { + cursor: pointer; + display: flex; + } + .back-arrow:hover svg { + transform: translateX(-10px); } `; @@ -119,6 +123,8 @@ const { recipientId, referrerId, potId, onClose, NADABOT_CONTRACT_ID, POT } = pr const DEFAULT_DONATION_AMOUNT = "1"; +const accountId = context.accountId; + const initialState = { amount: DEFAULT_DONATION_AMOUNT, donationType: "direct", @@ -289,15 +295,12 @@ if (!activeRound && !activeRounds) { // Get Ft Balances useEffect(() => { if (!ftBalances) { - asyncFetch( - `https://near-mainnet.api.pagoda.co/eapi/v1/accounts/${context.accountId}/balances/FT`, - { - headers: { - "Content-Type": "application/json", - "x-api-key": "dce81322-81b0-491d-8880-9cfef4c2b3c2", - }, - } - ) + asyncFetch(`https://near-mainnet.api.pagoda.co/eapi/v1/accounts/${accountId}/balances/FT`, { + headers: { + "Content-Type": "application/json", + "x-api-key": "dce81322-81b0-491d-8880-9cfef4c2b3c2", + }, + }) .then((ftBalancesRes) => { if (ftBalancesRes) { const ftBalances = ftBalancesRes.body.balances; @@ -323,7 +326,7 @@ useEffect(() => { }, [ftBalances]); const nearBalanceRes = fetch( - `https://near-mainnet.api.pagoda.co/eapi/v1/accounts/${context.accountId}/balances/NEAR`, + `https://near-mainnet.api.pagoda.co/eapi/v1/accounts/${accountId}/balances/NEAR`, { headers: { "Content-Type": "application/json", @@ -367,21 +370,21 @@ return ( {currentPage !== "form" && ( - State.update({ currentPage: "form", }) } - viewBox="0 0 16 16" - fill="none" - xmlns="http://www.w3.org/2000/svg" > - - + + + +
)} setIsModalDonationOpen(false), - recipientId: props.projectId, - referrerId: props.referrerId, - openDonateToProjectModal: () => setIsModalDonationOpen(true), + recipientId: projectId, + referrerId: referrerId, openDonationModalSuccess: (donation) => { setIsModalDonationOpen(false); State.update({ successfulDonation: donation, }); }, - // potId: state.donateToProjectModal.potId, // TODO: add this in if project is in a pot? }} /> {successfulDonation && ( diff --git a/apps/potlock/widget/Project/ModalDonation.jsx b/apps/potlock/widget/Project/ModalDonation.jsx index 9b52656c..c416d38f 100644 --- a/apps/potlock/widget/Project/ModalDonation.jsx +++ b/apps/potlock/widget/Project/ModalDonation.jsx @@ -243,14 +243,6 @@ ListsSDK = ListsSDK({ env: props.env }); const projects = ListsSDK.getRegistrations() || []; -let DonateSDK = - VM.require("potlock.near/widget/SDK.donate") || - (() => ({ - getConfig: () => {}, - asyncGetDonationsForDonor: () => {}, - })); -DonateSDK = DonateSDK({ env: props.env }); - let PotFactorySDK = VM.require("potlock.near/widget/SDK.potfactory") || (() => ({ @@ -488,6 +480,14 @@ const protocolConfig = ? Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}) : null; +let DonateSDK = + VM.require("potlock.near/widget/SDK.donate") || + (() => ({ + getConfig: () => {}, + asyncGetDonationsForDonor: () => {}, + })); +DonateSDK = DonateSDK({ env: props.env }); + const donationContractConfig = !potDetail ? DonateSDK.getConfig() || {} : null; const [protocolFeeRecipientAccount, protocolFeeBasisPoints, referralFeeBasisPoints] = useMemo( From 00dfa8625d95b721184969476264cfb14f575ce6 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Thu, 4 Apr 2024 22:51:35 +0400 Subject: [PATCH 05/16] finish pot confirm step --- apps/potlock/widget/Cart/BreakdownSummary.jsx | 2 +- apps/potlock/widget/Components/Banner.jsx | 2 +- .../widget/ModalDonation/AmountInput.jsx | 11 +- apps/potlock/widget/ModalDonation/Banners.jsx | 89 ++++ apps/potlock/widget/ModalDonation/Checks.jsx | 82 +++ .../{ConfirmPage.jsx => ConfirmDirect.jsx} | 28 +- .../widget/ModalDonation/ConfirmPot.jsx | 493 ++++++++++++++++++ apps/potlock/widget/ModalDonation/Form.jsx | 138 ++--- apps/potlock/widget/ModalDonation/FormPot.jsx | 452 ++++++++++++++++ apps/potlock/widget/ModalDonation/Main.jsx | 193 ++++--- apps/potlock/widget/Pots/Header.jsx | 60 ++- apps/potlock/widget/Pots/Projects.jsx | 3 +- apps/potlock/widget/Project/Card.jsx | 11 +- apps/potlock/widget/Project/DonationsInfo.jsx | 17 +- 14 files changed, 1362 insertions(+), 219 deletions(-) create mode 100644 apps/potlock/widget/ModalDonation/Banners.jsx create mode 100644 apps/potlock/widget/ModalDonation/Checks.jsx rename apps/potlock/widget/ModalDonation/{ConfirmPage.jsx => ConfirmDirect.jsx} (97%) create mode 100644 apps/potlock/widget/ModalDonation/ConfirmPot.jsx create mode 100644 apps/potlock/widget/ModalDonation/FormPot.jsx diff --git a/apps/potlock/widget/Cart/BreakdownSummary.jsx b/apps/potlock/widget/Cart/BreakdownSummary.jsx index 408b8487..21f61dc2 100644 --- a/apps/potlock/widget/Cart/BreakdownSummary.jsx +++ b/apps/potlock/widget/Cart/BreakdownSummary.jsx @@ -169,7 +169,7 @@ const { } = donationContractConfig; const protocolFeeBasisPoints = props.protocolFeeBasisPoints ?? protocol_fee_basis_points; -const referralFeeBasisPoints = potRferralFeeBasisPoints || referralFeeBasisPoints; +const referralFeeBasisPoints = potRferralFeeBasisPoints || props.referralFeeBasisPoints; const TOTAL_BASIS_POINTS = 10_000; let projectAllocationBasisPoints = diff --git a/apps/potlock/widget/Components/Banner.jsx b/apps/potlock/widget/Components/Banner.jsx index 61658f72..8ab55db2 100644 --- a/apps/potlock/widget/Components/Banner.jsx +++ b/apps/potlock/widget/Components/Banner.jsx @@ -52,7 +52,7 @@ const Container = styled.div` bottom: 0; left: 0; position: fixed; - z-index: 1000; + z-index: 999; background: #7fc41e; color: white; display: flex; diff --git a/apps/potlock/widget/ModalDonation/AmountInput.jsx b/apps/potlock/widget/ModalDonation/AmountInput.jsx index 86c442c2..307f8b76 100644 --- a/apps/potlock/widget/ModalDonation/AmountInput.jsx +++ b/apps/potlock/widget/ModalDonation/AmountInput.jsx @@ -39,7 +39,7 @@ const Container = styled.div` border: none; border-radius: 0; &:focus { - box-shadow: 0px 0px 0px 4px rgba(82, 82, 82, 0.08); + box-shadow: none; } } .usd-amount { @@ -110,9 +110,14 @@ const AmountInput = (props) => { const { value, HandleAmoutChange, donationType, denominationOptions } = props; return ( -
{nearToUsd ? `~$ ${nearToUsd * value}` : ""}
- {donationType === "pot" ? ( + {donationType === "pot" || denominationOptions.length === 1 ? (
{denominationOptions[0].text}
diff --git a/apps/potlock/widget/ModalDonation/Banners.jsx b/apps/potlock/widget/ModalDonation/Banners.jsx new file mode 100644 index 00000000..cc344afd --- /dev/null +++ b/apps/potlock/widget/ModalDonation/Banners.jsx @@ -0,0 +1,89 @@ +const AlertBanner = styled.div` + display: flex; + padding: 0.75rem 1rem; + color: #ed464f; + gap: 1rem; + align-items: center; + border: 1px solid #f4b37d; + border-radius: 6px; + background: #fef6ee; + margin-top: 1.5rem; + div { + font-weight: 500; + } + .icon { + width: 22px; + } +`; + +const NadabotBanner = styled.div` + display: flex; + align-items: center; + padding: 0.75rem 1rem; + border: 1px solid #f4b37d; + border-radius: 6px; + background: #fef6ee; + flex-wrap: wrap; + margin-top: 1.5rem; + .label { + display: flex; + align-items: center; + font-weight: 500; + gap: 1rem; + img { + width: 24px; + height: 24px; + } + } + .verify { + color: #dd3345; + font-weight: 500; + margin-left: auto; + &:hover { + text-decoration: none; + } + } + @media only screen and (max-width: 480px) { + flex-direction: column; + align-items: flex-start; + gap: 0px; + .labe { + align-items: flex-start; + } + .verify { + margin-left: 40px; + } + } +`; +const NADA_BOT_ICON = "bafkreicojpp23dmf7hakbt67eah4ba52dx3reekdccaupoggzzlhdkroyi"; + +const Alert = ({ error }) => ( + +
+ + + +
+
{error}
+
+); + +const Nadabot = () => ( + +
+ nadabot + You need to be verified to donate. +
+ + Verify to donate + +
+); + +return { + Alert, + Nadabot, +}; diff --git a/apps/potlock/widget/ModalDonation/Checks.jsx b/apps/potlock/widget/ModalDonation/Checks.jsx new file mode 100644 index 00000000..56baa4d6 --- /dev/null +++ b/apps/potlock/widget/ModalDonation/Checks.jsx @@ -0,0 +1,82 @@ +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 0.75rem; + > div { + display: flex; + border-radius: 8px; + align-items: center; + gap: 0.5rem; + padding: 1rem; + cursor: pointer; + border: 1px solid #dbdbdb; + color: #7b7b7b; + background: white; + transition: all 300ms; + .text { + flex: 1; + font-weight: 500; + } + &.active { + box-shadow: 0px 0px 1.4px 2px #fee6e5; + color: #dd3345; + border-color: #dd3345; + span { + color: #7b7b7b; + } + } + &.disabled { + pointer-events: none; + color: #a6a6a6; + background: #f6f5f3; + span { + color: #a6a6a6; + } + } + } +`; + +const CheckBox = styled.div` + width: 20px; + height: 20px; + border: 2px solid #d9d9d9; + display: flex; + border-radius: 50%; + div { + width: 10px; + height: 10px; + background: transparent; + border-radius: 50%; + margin: auto; + } + &.active { + border-color: #dd3345; + div { + background: #dd3345; + } + } +`; + +const Checks = ({ options, value, onClick }) => { + return ( + + {options.map((option) => ( +
onClick(option.val)} + className={`${value === option.val ? "active" : ""} ${option.disabled ? "disabled" : ""}`} + > + +
+
+
+ {option.label} {option.info && {option.info} } + {option.disabled && {option.disabledText} } +
+
+ ))} +
+ ); +}; + +return { Checks }; diff --git a/apps/potlock/widget/ModalDonation/ConfirmPage.jsx b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx similarity index 97% rename from apps/potlock/widget/ModalDonation/ConfirmPage.jsx rename to apps/potlock/widget/ModalDonation/ConfirmDirect.jsx index efd0c822..71f17f9a 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmPage.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx @@ -75,6 +75,7 @@ const FeesRemoval = styled.div` gap: 1rem; .check { display: flex; + flex-wrap: wrap; align-items: center; } .label { @@ -94,9 +95,15 @@ const FeesRemoval = styled.div` } } .profile-image { - border-radius: 50%; width: 17px; height: 17px; + display: flex !important; + } + @media only screen and (max-width: 480px) { + .address { + margin-left: 34px; + width: 100%; + } } `; @@ -109,6 +116,9 @@ const Button = styled.div` width: 100%; font-weight: 500; } + @media only screen and (max-width: 480px) { + margin-top: 2rem; + } `; const NearIcon = (props) => ( @@ -195,7 +205,7 @@ const pollForDonationSuccess = ({ }, pollIntervalMs); }; -const ConfirmPage = (props) => { +const ConfirmDirect = (props) => { const { selectedDenomination, bypassProtocolFee, @@ -204,9 +214,8 @@ const ConfirmPage = (props) => { donationNoteError, addNote, updateState, - NADABOT_CONTRACT_ID, selectedRound, - recipientId, + projectId, referrerId, accountId, amount, @@ -214,11 +223,6 @@ const ConfirmPage = (props) => { donationType, } = props; - // check if user is verified for matched doation - const isUserHumanVerified = Near.view(NADABOT_CONTRACT_ID, NADABOT_HUMAN_METHOD, { - account_id: accountId, - }); - // Get protcol, referral & chef Fee const potDetail = PotSDK.getConfig(selectedRound); @@ -259,7 +263,6 @@ const ConfirmPage = (props) => { const donationAmountIndivisible = Big(amount).mul( new Big(10).pow(selectedDenomination.decimals) ); - let projectId = recipientId; const args = { referrer_id: referrerId, @@ -269,7 +272,7 @@ const ConfirmPage = (props) => { }; const potId = selectedRound || null; - const isPotDonation = potId && isUserHumanVerified === true; + const isPotDonation = potId; const now = Date.now(); @@ -420,6 +423,7 @@ const ConfirmPage = (props) => { ...props, referrerId, protocolFeeBasisPoints, + referralFeeBasisPoints, bypassChefFee, chef: potDetail?.chef, chefFeeBasisPoints, @@ -529,5 +533,5 @@ const ConfirmPage = (props) => { }; return { - ConfirmPage, + ConfirmDirect, }; diff --git a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx new file mode 100644 index 00000000..8569cc9d --- /dev/null +++ b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx @@ -0,0 +1,493 @@ +const { ownerId, NADABOT_HUMAN_METHOD, DONATION_CONTRACT_ID } = VM.require( + "potlock.near/widget/constants" +) || { + ownerId: "", + NADABOT_HUMAN_METHOD: "", + DONATION_CONTRACT_ID: "", +}; + +const { nearToUsd } = VM.require("potlock.near/widget/utils"); + +let DonateSDK = + VM.require("potlock.near/widget/SDK.donate") || + (() => ({ + getConfig: () => {}, + asyncGetDonationsForDonor: () => {}, + })); +DonateSDK = DonateSDK({ env: props.env }); + +const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { + getConfig: () => {}, + asyncGetDonationsForDonor: () => {}, +}; +const { _address } = VM.require(`potlock.near/widget/Components.DonorsUtils`) || { + _address: (address) => address, +}; + +const Container = styled.div` + display: flex; + flex-direction: column; + padding: 1.5rem 2rem; + gap: 1.5rem; + @media only screen and (max-width: 480px) { + padding: 1.5rem 1.125rem; + } +`; + +const ContentScrollable = styled.div` + display: flex; + flex-direction: column; + gap: 1.5rem; + overflow-y: scroll; + height: 450px; +`; + +const Label = styled.div` + font-weight: 500; + margin-bottom: 4px; +`; +const Amout = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + span { + font-weight: 600; + } + div { + font-weight: 600; + font-size: 1rem; + } + .usd-amount { + color: #7b7b7b; + } + .toggle-icon { + width: 8px; + display: flex; + transition: all 300ms ease-in-out; + margin-left: auto; + overflow: hidden; + svg { + width: 100%; + } + } + img, + svg { + width: 20px; + } +`; + +const SvgIcon = styled.svg` + width: 16px; +`; + +const FeesRemoval = styled.div` + display: flex; + flex-direction: column; + gap: 1rem; + .check { + display: flex; + align-items: center; + flex-wrap: wrap; + } + .label { + margin-right: 4px; + margin-left: 8px; + } + .address { + font-weight: 600; + gap: 4px; + display: flex; + align-items: center; + color: #292929; + transition: all 300ms; + &:hover { + color: #dd3345; + text-decoration: none; + } + } + .profile-image { + width: 17px; + height: 17px; + display: flex !important; + } + @media only screen and (max-width: 480px) { + .address { + margin-left: 34px; + width: 100%; + } + } +`; + +const Button = styled.div` + display: flex; + margin-top: 4rem; + margin-bottom: 0.5rem; + button { + padding: 12px 16px; + width: 100%; + font-weight: 500; + } + @media only screen and (max-width: 480px) { + margin-top: 2rem; + } +`; + +const Projects = styled.div` + padding: 8px 0; + display: flex; + flex-direction: column; + border-radius: 8px; + border: 1px solid #ebebeb; + background: rgba(235, 235, 235, 0.24); + transition: all 300ms ease-in-out; + &.hidden { + max-height: 0; + overflow: hidden; + opacity: 0; + } + .project { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.5rem 1rem; + transition: 300ms ease-in-out; + } + .profile-image { + width: 40px; + height: 40px; + box-shadow: 0px 0px 1px 0px #a6a6a6 inset; + border-radius: 50%; + } + .info { + display: flex; + flex-direction: column; + .name { + font-weight: 600; + } + .address { + color: #7b7b7b; + transition: all 300ms; + &:hover { + text-decoration: none; + color: #dd3345; + } + } + } +`; + +const ProjectAmount = styled.div` + margin-left: auto; + display: flex; + gap: 1rem; + align-items: center; + div { + font-weight: 600; + } + svg { + width: 16px; + } +`; + +const NearIcon = (props) => ( + + + + + + + + + + + +); + +const ProfileImg = ({ accountId, profile }) => ( + +); + +const CheckBox = ({ id, checked, onClick }) => ( + +); + +const getFeesBasisPoints = (protocolConfig, potDetail) => { + if (protocolConfig) { + return [ + protocolConfig.account_id, + protocolConfig.basis_points, + potDetail.referral_fee_public_round_basis_points, + ]; + } else { + return ["", 0, 0]; + } +}; + +const pollForDonationSuccess = ({ + projectId, + afterTs, + accountId, + openDonationSuccessModal, + isPotDonation, +}) => { + // poll for updates + const pollIntervalMs = 1000; + // const totalPollTimeMs = 60000; // consider adding in to make sure interval doesn't run indefinitely + const pollId = setInterval(() => { + PotSDK.asyncGetDonationsForDonor(accountId).then((donations) => { + for (const donation of donations) { + const { project_id, donated_at_ms, donated_at } = donation; + if ((project_id === projectId && donated_at_ms > afterTs) || donated_at > afterTs) { + // display success message + clearInterval(pollId); + openDonationSuccessModal(donation); + } + } + }); + }, pollIntervalMs); +}; + +const ConfirmPot = (props) => { + const { + selectedDenomination, + bypassProtocolFee, + bypassChefFee, + updateState, + potDetail, + potId, + projectId, + referrerId, + accountId, + amount, + openDonationSuccessModal, + selectedProjects, + donationType, + hrefWithParams, + toggleAmount, + } = props; + + const protocolConfigContractId = potDetail.protocol_config_provider.split(":")[0]; + const protocolConfigViewMethodName = potDetail.protocol_config_provider.split(":")[1]; + const protocolConfig = + protocolConfigContractId && protocolConfigViewMethodName + ? Near.view(protocolConfigContractId, protocolConfigViewMethodName, {}) + : null; + + const [protocolFeeRecipientAccount, protocolFeeBasisPoints, referralFeeBasisPoints] = + getFeesBasisPoints(protocolConfig, potDetail); + + const chefFeeBasisPoints = potDetail?.chef_fee_basis_points; + + const donationAmountIndivisible = (num) => + Big(num).mul(new Big(10).pow(selectedDenomination.decimals)); + + const projectAmount = parseFloat(amount) / Object.keys(selectedProjects).length; + + const autoProjectAmount = donationAmountIndivisible(projectAmount); + + const handleDonate = () => { + const now = Date.now(); + + const successArgs = { + projectId, + now, + accountId, + openDonationSuccessModal, + isPotDonation: true, + }; + + const transactions = []; + + Object.keys(selectedProjects).forEach((project) => { + let amount = 0; + if (donationType === "auto") { + amount = autoProjectAmount; + } else { + amount = donationAmountIndivisible(selectedProjects[project]); + } + + if (amount) { + transactions.push({ + contractName: potId, + methodName: "donate", + args: { + referrer_id: referrerId, + project_id: project, + bypass_protocol_fee: bypassProtocolFee, + ...(bypassChefFee ? { custom_chef_fee_basis_points: 0 } : {}), + }, + deposit: amount.toFixed(0), + gas: "300000000000000", + }); + } + }); + + Near.call(transactions); + + // pollForDonationSuccess(successArgs); + }; + + return ( + + +
+ + + updateState({ + toggleAmount: !toggleAmount, + }) + } + > +
+ {selectedDenomination.icon ? : } +
+
+ {amount} {selectedDenomination.text} +
+ {nearToUsd &&
~${nearToUsd * amount}
} +
+ + + +
+
+
+ + {Object.keys(selectedProjects).map((project_id) => { + const profile = Social.getr(`${project_id}/profile`); + + return selectedProjects[project_id] > 0 || donationType === "auto" ? ( +
+ +
+ {profile?.name &&
{_address(profile?.name, 20)}
} + + {_address(project_id, 20)} + +
+ +
+ {" "} + {donationType === "auto" + ? projectAmount < 0.01 + ? "<0.01" + : projectAmount.toFixed(2) + : selectedProjects[project_id]}{" "} +
+ +
+
+ ) : ( + "" + ); + })} +
+ + +
+ { + updateState({ bypassProtocolFee: e.target.checked }); + }} + /> + +
Remove {protocolFeeBasisPoints / 100 || "-"}% protocol fee
+ + + + {protocolFeeRecipientAccount} + +
+ {potDetail?.chef && chefFeeBasisPoints > 0 && ( +
+ { + updateState({ bypassChefFee: e.target.checked }); + }} + /> + +
Remove {chefFeeBasisPoints / 100 || "-"}% chef fee
+ + + + {potDetail?.chef} + +
+ )} +
+
+ +
+ ); +}; + +return { + ConfirmPot, +}; diff --git a/apps/potlock/widget/ModalDonation/Form.jsx b/apps/potlock/widget/ModalDonation/Form.jsx index d0f80094..b5f2300a 100644 --- a/apps/potlock/widget/ModalDonation/Form.jsx +++ b/apps/potlock/widget/ModalDonation/Form.jsx @@ -6,6 +6,12 @@ const { ownerId, NADABOT_HUMAN_METHOD } = VM.require("potlock.near/widget/consta const { AmountInput } = VM.require(`potlock.near/widget/ModalDonation.AmountInput`) || { AmountInput: () => {}, }; +const { Checks } = VM.require(`potlock.near/widget/ModalDonation.Checks`) || { + CheckBox: () => {}, +}; +const { Nadabot, Alert } = VM.require(`potlock.near/widget/ModalDonation.Banners`) || { + CheckBox: () => {}, +}; const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { getConfig: () => {}, @@ -23,53 +29,6 @@ const Label = styled.div` margin-top: 0.5rem; `; -const DonationType = styled.div` - display: flex; - flex-direction: column; - gap: 0.75rem; - > div { - display: flex; - border-radius: 8px; - align-items: center; - gap: 0.5rem; - padding: 1rem; - cursor: pointer; - color: #a6a6a6; - background: #f6f5f3; - border: 1px solid transparent; - &.active { - box-shadow: 0px 0px 1.4px 2px #fee6e5; - background: white; - color: #dd3345; - border-color: #dd3345; - } - &.disabled { - pointer-events: none; - } - } -`; - -const CheckBox = styled.div` - width: 20px; - height: 20px; - border: 2px solid #d9d9d9; - display: flex; - border-radius: 50%; - div { - width: 10px; - height: 10px; - background: transparent; - border-radius: 50%; - margin: auto; - } - &.active { - border-color: #dd3345; - div { - background: #dd3345; - } - } -`; - const Button = styled.div` display: flex; margin-top: 4rem; @@ -79,14 +38,16 @@ const Button = styled.div` width: 100%; font-weight: 500; } + @media only screen and (max-width: 480px) { + margin-top: 2rem; + } `; const CurrentBalance = styled.div` display: flex; margin-top: 0.5rem; gap: 0.5rem; - flex-wrap: wrap-reverse; - justify-content: space-between; + justify-content: flex-end; .amount-alert { color: #e54141; } @@ -142,9 +103,9 @@ const SelectPot = ({ selectedRound, activeRoundsOptions, updateState }) => ( ); -const FormPage = (props) => { +const FormDirect = (props) => { const { - recipientId, + projectId, profile, amount, amountError, @@ -163,6 +124,8 @@ const FormPage = (props) => { account_id: accountId, }); + const needsToVerify = isUserHumanVerified === false && donationType === "pot"; + const donationTypes = [ { label: "Direct donation", @@ -172,7 +135,8 @@ const FormPage = (props) => { { label: "Quadratically matched donation", val: "pot", - disabled: !activeRound && (!activeRounds || activeRounds.length === 0), + disabled: !activeRounds || activeRounds.length === 0, + disabledText: "(no pots available)", }, ]; @@ -185,8 +149,6 @@ const FormPage = (props) => { }; }); - const passedPotConfig = PotSDK.getConfig(activeRound); - const isFtDonation = selectedDenomination.text !== "NEAR"; const HandleAmoutChange = (amount) => { @@ -195,7 +157,7 @@ const FormPage = (props) => { updateState({ amount, amountError: "" }); // error if amount is greater than balance if (amount > ftBalance) { - updateState({ amountError: "Insufficient balance" }); + updateState({ amountError: "You don’t have enough balance to complete this transaction." }); } else if (!isFtDonation && parseFloat(amount) < 0.1) { updateState({ amountError: "Minimum donation is 0.1 NEAR" }); } @@ -203,38 +165,25 @@ const FormPage = (props) => { const isLoading = isUserHumanVerified === null || activeRounds === null; - return recipientId ? ( + return projectId ? ( profile === null ? ( ) : (
- - {donationTypes.map((type) => ( -
- updateState({ - donationType: type.val, - }) - } - className={`${donationType === type.val ? "active" : ""} ${ - type.disabled ? "disabled" : "" - }`} - > - -
-
-
- {type.label} {type.disabled && (no pots available) } -
-
- ))} -
+ + updateState({ + donationType: val, + }) + } + /> {donationType === "pot" && ( - {(activeRound || activeRounds?.length === 1) && ( + {activeRounds?.length === 1 && ( {Object.values(activeRoundsOptions)[0]?.label} )} {activeRounds?.length > 1 && ( @@ -249,17 +198,18 @@ const FormPage = (props) => { > Amount - + {!needsToVerify && ( + + )} -
{amountError}
{ftBalance} {selectedDenomination.text} @@ -267,17 +217,15 @@ const FormPage = (props) => {
available
+ {amountError && } + {needsToVerify && } + + ); +}; + +return { + FormPot, +}; diff --git a/apps/potlock/widget/ModalDonation/Main.jsx b/apps/potlock/widget/ModalDonation/Main.jsx index d9dce402..dd72882e 100644 --- a/apps/potlock/widget/ModalDonation/Main.jsx +++ b/apps/potlock/widget/ModalDonation/Main.jsx @@ -8,6 +8,16 @@ const Container = styled.div` 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); overflow: hidden; border-radius: 6px; + @media only screen and (max-width: 480px) { + top: 0; + position: fixed; + left: 0; + width: 100vw; + height: 100vh; + overflow-y: scroll; + display: flex; + z-index: 1000; + } `; const Banner = styled.div` @@ -36,6 +46,9 @@ const Banner = styled.div` transform: translate(10%, -10%); pointer-events: none; } + @media only screen and (max-width: 480px) { + padding: 1.125rem; + } `; const BannerBg = (props) => ( @@ -119,15 +132,16 @@ const HeaderIcons = styled.div` const DENOMINATION_OPTIONS = [{ text: "NEAR", value: "NEAR", decimals: 24 }]; -const { recipientId, referrerId, potId, onClose, NADABOT_CONTRACT_ID, POT } = props; +const { projectId, referrerId, potId, onClose, NADABOT_CONTRACT_ID, POT, multiple, potDetail } = + props; const DEFAULT_DONATION_AMOUNT = "1"; const accountId = context.accountId; const initialState = { - amount: DEFAULT_DONATION_AMOUNT, - donationType: "direct", + amount: "", + donationType: multiple ? "auto" : "direct", showBreakdown: false, bypassProtocolFee: false, bypassChefFee: false, @@ -135,15 +149,14 @@ const initialState = { donationNote: "", donationNoteError: "", allPots: null, - detailForPots: {}, - approvedProjectsForPots: {}, - activeRoundsForProject: potId ? [potId] : null, // mapping of potId to { potDetail } intervalId: null, ftBalances: null, selectedDenomination: DENOMINATION_OPTIONS[0], denominationOptions: DENOMINATION_OPTIONS, selectedRound: "", - currentPage: "confirm", + currentPage: multiple ? "formPot" : "form", + selectedProjects: {}, + toggleAmount: false, }; State.init(initialState); @@ -159,9 +172,6 @@ const { donationNote, donationNoteError, allPots, - detailForPots, - approvedProjectsForPots, - activeRoundsForProject, intervalId, nearBalance, ftBalances, @@ -173,8 +183,8 @@ const { const [activeRounds, setActiveRounds] = useState(null); -const profile = Social.getr(`${recipientId}/profile`); -const profileName = profile?.name || recipientId; +const profile = Social.getr(`${projectId}/profile`); +const profileName = profile?.name || projectId; const MAX_NOTE_LENGTH = 60; @@ -194,8 +204,6 @@ let ListsSDK = })); ListsSDK = ListsSDK({ env: props.env }); -const projects = ListsSDK.getRegistrations() || []; - let DonateSDK = VM.require("potlock.near/widget/SDK.donate") || (() => ({ @@ -231,16 +239,24 @@ const { addItemsToCart, clearCart } = VM.require("potlock.near/widget/SDK.cart") clearCart: () => {}, }; -const { FormPage } = VM.require("potlock.near/widget/ModalDonation.Form") || { - FormPage: () => {}, +const { FormDirect } = VM.require("potlock.near/widget/ModalDonation.Form") || { + FormDirect: () => {}, +}; +const { FormPot } = VM.require("potlock.near/widget/ModalDonation.FormPot") || { + FormPot: () => {}, }; -const { ConfirmPage } = VM.require("potlock.near/widget/ModalDonation.ConfirmPage") || { - ConfirmPage: () => {}, +const { ConfirmDirect } = VM.require("potlock.near/widget/ModalDonation.ConfirmDirect") || { + ConfirmDirect: () => {}, +}; +const { ConfirmPot } = VM.require("potlock.near/widget/ModalDonation.ConfirmPot") || { + ConfirmPot: () => {}, }; const pages = { - form: FormPage, - confirm: ConfirmPage, + form: FormDirect, + formPot: FormPot, + confirm: ConfirmDirect, + confirmPot: ConfirmPot, }; const ActivePageComponent = pages[currentPage]; @@ -268,30 +284,38 @@ const pots = useCache( "active-pots" ); -if (!activeRound && !activeRounds) { - (pots ?? []).forEach((pot, idx) => { - if (pot) { - PotSDK.asyncGetApplicationByProjectId(pot, recipientId) - .then((application) => { - if (application.status === "Approved") { - setActiveRounds((prev) => { - const prevRounds = prev || []; - if (!prevRounds.includes(pot)) { - return [...prevRounds, pot]; - } - }); - if (!selectedRound) - State.update({ - selectedRound: pot, +useEffect(() => { + if (potId && !activeRounds) { + setActiveRounds([potId]); + State.update({ + donationType: multiple ? "auto" : "pot", + }); + } else if (!activeRounds && projectId) { + (pots ?? []).forEach((pot, idx) => { + if (pot) { + PotSDK.asyncGetApplicationByProjectId(pot, projectId) + .then((application) => { + if (application.status === "Approved") { + setActiveRounds((prev) => { + const prevRounds = prev || []; + if (!prevRounds.includes(pot)) { + return [...prevRounds, pot]; + } }); - } else if (pots.length - 1 === idx && !activeRounds) { - setActiveRounds((prev) => [...(prev || [])]); - } - }) - .catch((err) => {}); - } - }); -} + if (!selectedRound) + State.update({ + selectedRound: pot, + }); + } else if (pots.length - 1 === idx && !activeRounds) { + setActiveRounds((prev) => [...(prev || [])]); + } + }) + .catch((err) => {}); + } + }); + } +}, [pots]); + // Get Ft Balances useEffect(() => { if (!ftBalances) { @@ -365,47 +389,51 @@ return ( }, children: ( - - - - - {currentPage !== "form" && ( -
- State.update({ - currentPage: "form", - }) - } +
+ + + + + {!["form", "formPot"].includes(currentPage) && ( +
+ State.update({ + currentPage: multiple ? "formPot" : "form", + }) + } + > + + + +
+ )} + + onClose()} + className="close-icon" + viewBox="0 0 14 14" + fill="none" + xmlns="http://www.w3.org/2000/svg" > - - - -
+ + + + {["confirmPot", "confirm"].includes(currentPage) ? ( +
Confirm donation
+ ) : currentPage === "formPot" ? ( +
Donate to Projects in {potDetail?.pot_name}
+ ) : ( +
Donate to {profileName}
)} - - onClose()} - className="close-icon" - viewBox="0 0 14 14" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - - - - {currentPage !== "form" ? ( -
Confirm donation
- ) : ( -
Donate to {profileName}
- )} - + +
), diff --git a/apps/potlock/widget/Pots/Header.jsx b/apps/potlock/widget/Pots/Header.jsx index 8677fb49..fb4bbc29 100644 --- a/apps/potlock/widget/Pots/Header.jsx +++ b/apps/potlock/widget/Pots/Header.jsx @@ -8,6 +8,7 @@ const { registryStatus, hrefWithParams, nav, + referrerId, } = props; const { @@ -26,7 +27,10 @@ const { } = potDetail; const [isMatchingPoolModalOpen, setIsMatchingPoolModalOpen] = useState(false); +const [isModalDonationOpen, setIsModalDonationOpen] = useState(false); +const [successfulDonation, setSuccessfulDonation] = useState(null); const [showChallengePayoutsModal, setShowChallengePayoutsModal] = useState(false); +const [projects, setProjects] = useState(null); const { IPFS_BASE_URL } = VM.require("potlock.near/widget/constants") || { IPFS_BASE_URL: "", @@ -41,10 +45,18 @@ const userIsChefOrGreater = userIsAdminOrGreater || chef === context.accountId; const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { getApplicationByProjectId: () => {}, + asyncGetApprovedApplications: () => {}, }; const existingApplication = PotSDK.getApplicationByProjectId(potId, context.accountId); +useEffect(() => { + if (!projects) { + PotSDK.asyncGetApprovedApplications(potId).then((projects) => { + setProjects(projects); + }); + } +}, []); const applicationExists = existingApplication || applicationSuccess; const now = Date.now(); @@ -163,6 +175,8 @@ const existingChallengeForUser = (payoutsChallenges || []).find( (challenge) => challenge.challenger_id === context.accountId ); +const canDonate = sybilRequirementMet && projects.length > 0; + return (
@@ -180,17 +194,20 @@ return (
- {publicRoundOpen && (nav !== "projects" || !sybilRequirementMet) && context.accountId && ( + {publicRoundOpen && context.accountId && ( { + setIsModalDonationOpen(true); + } + : null, + target: canDonate ? "_self" : "_blank", + iconSrc: canDonate ? null : NADABOT_ICON_URL, }} /> )} @@ -269,5 +286,34 @@ return ( onCancel: () => setShowChallengePayoutsModal(false), }} /> + setIsModalDonationOpen(false), + potId, + potDetail, + projects, + referrerId, + multiple: true, + openDonationModalSuccess: (donation) => { + setIsModalDonationOpen(false); + setSuccessfulDonation(donation); + }, + }} + /> + {successfulDonation && ( + setSuccessfulDonation(null), + }} + /> + )} ); diff --git a/apps/potlock/widget/Pots/Projects.jsx b/apps/potlock/widget/Pots/Projects.jsx index 2cdb28ed..9b5927b8 100644 --- a/apps/potlock/widget/Pots/Projects.jsx +++ b/apps/potlock/widget/Pots/Projects.jsx @@ -274,7 +274,8 @@ return ( loading={} props={{ ...props, - potId, + potDetail, + projects, projectId: project.project_id, allowDonate: sybilRequirementMet && diff --git a/apps/potlock/widget/Project/Card.jsx b/apps/potlock/widget/Project/Card.jsx index 2c49388d..151a5a07 100644 --- a/apps/potlock/widget/Project/Card.jsx +++ b/apps/potlock/widget/Project/Card.jsx @@ -347,7 +347,7 @@ const CardSkeleton = () => ( State.init({ donateModal: { isOpen: false, - recipientId: null, + projectId: null, referrerId: null, potId: null, potDetail: null, @@ -359,7 +359,7 @@ const openDonateModal = () => { State.update({ donateModal: { isOpen: true, - recipientId: projectId, + projectId: projectId, referrerId: null, potId: null, potDetail: null, @@ -552,7 +552,7 @@ return ( State.update({ donateModal: { isOpen: false, - recipientId: null, + projectId: null, referrerId: null, potId: null, potDetail: null, @@ -562,7 +562,7 @@ return ( State.update({ donateModal: { isOpen: false, - recipientId: null, + projectId: null, referrerId: null, potId: null, potDetail: null, @@ -570,9 +570,8 @@ return ( successfulDonation: donation, }); }, - recipientId: state.donateModal.recipientId, + projectId: state.donateModal.projectId, referrerId: props.referrerId, - potId, }} /> )} diff --git a/apps/potlock/widget/Project/DonationsInfo.jsx b/apps/potlock/widget/Project/DonationsInfo.jsx index 9815068e..7b598405 100644 --- a/apps/potlock/widget/Project/DonationsInfo.jsx +++ b/apps/potlock/widget/Project/DonationsInfo.jsx @@ -118,13 +118,11 @@ return ( ...props, isModalOpen: isModalDonationOpen, onClose: () => setIsModalDonationOpen(false), - recipientId: projectId, - referrerId: referrerId, + projectId, + referrerId, openDonationModalSuccess: (donation) => { setIsModalDonationOpen(false); - State.update({ - successfulDonation: donation, - }); + setSuccessfulDonation(donation); }, }} /> @@ -133,12 +131,9 @@ return ( src={`${ownerId}/widget/Project.ModalSuccess`} props={{ ...props, - successfulDonation: state.successfulDonation, - isModalOpen: state.successfulDonation != null, - onClose: () => - State.update({ - successfulDonation: null, - }), + successfulDonation: successfulDonation, + isModalOpen: successfulDonation != null, + onClose: () => setSuccessfulDonation(null), }} /> )} From 8a0f4b50a2bb2eb140db8b526f536b7de904ba9d Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 5 Apr 2024 02:07:58 +0400 Subject: [PATCH 06/16] successful modal --- apps/potlock/widget/Cart/BreakdownSummary.jsx | 1 - apps/potlock/widget/Components/Modal.jsx | 6 +- .../widget/ModalDonation/ConfirmDirect.jsx | 9 +- .../widget/ModalDonation/ConfirmPot.jsx | 27 +- apps/potlock/widget/ModalDonation/Form.jsx | 3 + apps/potlock/widget/Pots/Header.jsx | 2 - apps/potlock/widget/Project/Card.jsx | 2 +- apps/potlock/widget/Project/ListPage.jsx | 8 + apps/potlock/widget/Project/ModalSuccess.jsx | 424 ++++++++++-------- 9 files changed, 274 insertions(+), 208 deletions(-) diff --git a/apps/potlock/widget/Cart/BreakdownSummary.jsx b/apps/potlock/widget/Cart/BreakdownSummary.jsx index 21f61dc2..fad4f98a 100644 --- a/apps/potlock/widget/Cart/BreakdownSummary.jsx +++ b/apps/potlock/widget/Cart/BreakdownSummary.jsx @@ -42,7 +42,6 @@ const BreakdownSummary = styled.div` flex-direction: column; align-items: center; width: 100%; - cursor: pointer; .breakdown-details { display: flex; flex-direction: column; diff --git a/apps/potlock/widget/Components/Modal.jsx b/apps/potlock/widget/Components/Modal.jsx index 6fac3986..9df26301 100644 --- a/apps/potlock/widget/Components/Modal.jsx +++ b/apps/potlock/widget/Components/Modal.jsx @@ -15,14 +15,16 @@ const ModalOverlay = styled.div` `; const ModalContent = styled.div` - border-radius: 6px; width: 100%; max-width: 600px; padding: 24px 24px 18px 24px; background: white; - border: 1px rgba(41, 41, 41, 0.4) solid; display: flex; flex-direction: column; + border-radius: 12px; + overflow: hidden; + box-shadow: 0px 0px 0px 1px rgba(41, 41, 41, 0.1), 0px 8px 12px -4px rgba(41, 41, 41, 0.1), + 0px 20px 32px -10px rgba(41, 41, 41, 0.1), 0px 32px 44px -16px rgba(41, 41, 41, 0.1); `; const overlayStyle = props.overlayStyle || {}; diff --git a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx index 71f17f9a..83bdeb93 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx @@ -109,16 +109,13 @@ const FeesRemoval = styled.div` const Button = styled.div` display: flex; - margin-top: 4rem; + margin-top: 2rem; margin-bottom: 0.5rem; button { padding: 12px 16px; width: 100%; font-weight: 500; } - @media only screen and (max-width: 480px) { - margin-top: 2rem; - } `; const NearIcon = (props) => ( @@ -198,7 +195,9 @@ const pollForDonationSuccess = ({ ) { // display success message clearInterval(pollId); - openDonationSuccessModal(donation); + openDonationSuccessModal({ + projectId: donation, + }); } } }); diff --git a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx index 8569cc9d..e026260b 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx @@ -241,25 +241,30 @@ const getFeesBasisPoints = (protocolConfig, potDetail) => { }; const pollForDonationSuccess = ({ - projectId, + projectIds, afterTs, accountId, openDonationSuccessModal, - isPotDonation, + amount, }) => { // poll for updates const pollIntervalMs = 1000; // const totalPollTimeMs = 60000; // consider adding in to make sure interval doesn't run indefinitely const pollId = setInterval(() => { - PotSDK.asyncGetDonationsForDonor(accountId).then((donations) => { - for (const donation of donations) { + PotSDK.asyncGetDonationsForDonor(accountId).then((alldonations) => { + const donations = {}; + for (const donation of alldonations) { const { project_id, donated_at_ms, donated_at } = donation; - if ((project_id === projectId && donated_at_ms > afterTs) || donated_at > afterTs) { - // display success message - clearInterval(pollId); - openDonationSuccessModal(donation); + if ((projectIds.includes(project_id) && donated_at_ms > afterTs) || donated_at > afterTs) { + donations[project_id] = donation; } } + if (Object.keys(donations).length === projectIds.length) { + // display success message + clearInterval(pollId); + donations.total_amount = amount; + openDonationSuccessModal(donations); + } }); }, pollIntervalMs); }; @@ -272,7 +277,6 @@ const ConfirmPot = (props) => { updateState, potDetail, potId, - projectId, referrerId, accountId, amount, @@ -306,11 +310,12 @@ const ConfirmPot = (props) => { const now = Date.now(); const successArgs = { - projectId, + projectIds: Object.keys(selectedProjects), now, accountId, openDonationSuccessModal, isPotDonation: true, + amount, }; const transactions = []; @@ -341,7 +346,7 @@ const ConfirmPot = (props) => { Near.call(transactions); - // pollForDonationSuccess(successArgs); + pollForDonationSuccess(successArgs); }; return ( diff --git a/apps/potlock/widget/ModalDonation/Form.jsx b/apps/potlock/widget/ModalDonation/Form.jsx index b5f2300a..293183ea 100644 --- a/apps/potlock/widget/ModalDonation/Form.jsx +++ b/apps/potlock/widget/ModalDonation/Form.jsx @@ -21,6 +21,9 @@ const Form = styled.div` display: flex; flex-direction: column; padding: 1.5rem 2rem; + @media only screen and (max-width: 480px) { + padding: 1.5rem 1.125rem; + } `; const Label = styled.div` diff --git a/apps/potlock/widget/Pots/Header.jsx b/apps/potlock/widget/Pots/Header.jsx index fb4bbc29..8cdb0cff 100644 --- a/apps/potlock/widget/Pots/Header.jsx +++ b/apps/potlock/widget/Pots/Header.jsx @@ -61,7 +61,6 @@ const applicationExists = existingApplication || applicationSuccess; const now = Date.now(); const publicRoundOpen = now >= public_round_start_ms && now < public_round_end_ms; -console.log(potDetail); const applicationOpen = now >= application_start_ms && now < application_end_ms; @@ -87,7 +86,6 @@ const Container = styled.div` display: flex; flex-wrap: wrap; gap: 2rem; - padding: 64px 4rem 80px; .pool-table { max-width: 514px; width: 100%; diff --git a/apps/potlock/widget/Project/Card.jsx b/apps/potlock/widget/Project/Card.jsx index 151a5a07..8b60aae4 100644 --- a/apps/potlock/widget/Project/Card.jsx +++ b/apps/potlock/widget/Project/Card.jsx @@ -1,4 +1,4 @@ -const { potId, potDetail, payoutDetails } = props; +const { potId, potDetail, payoutDetails, projects } = props; const { nearToUsd, ipfsUrlFromCid, yoctosToNear, yoctosToUsdWithFallback } = VM.require( "potlock.near/widget/utils" ) || { diff --git a/apps/potlock/widget/Project/ListPage.jsx b/apps/potlock/widget/Project/ListPage.jsx index 26386ac1..8801ed4a 100644 --- a/apps/potlock/widget/Project/ListPage.jsx +++ b/apps/potlock/widget/Project/ListPage.jsx @@ -766,6 +766,13 @@ const handleTag = (key) => { setTagsList(tags); }; +const getRandomProject = () => { + if (projects) { + const randomIndex = Math.floor(Math.random() * projects.length); + return projects[randomIndex]; + } +}; + return ( <> @@ -817,6 +824,7 @@ return ( props={{ ...props, isModalOpen: state.isModalOpen, + projectId: getRandomProject(), onClose: () => State.update({ isModalOpen: false, diff --git a/apps/potlock/widget/Project/ModalSuccess.jsx b/apps/potlock/widget/Project/ModalSuccess.jsx index 0e19778f..2577cc9e 100644 --- a/apps/potlock/widget/Project/ModalSuccess.jsx +++ b/apps/potlock/widget/Project/ModalSuccess.jsx @@ -1,4 +1,4 @@ -const { onClose, successfulDonation } = props; +const { onClose } = props; const { ownerId, SUPPORTED_FTS: { NEAR }, @@ -8,7 +8,7 @@ const { SUPPORTED_FTS: {}, IPFS_BASE_URL: "", }; -const { yoctosToUsd } = VM.require("potlock.near/widget/utils") || { yoctosToUsd: () => "" }; +const { yoctosToUsd } = VM.require("potlock.near/widget/utils") || { yoctosToUsd: () => null }; let DonateSDK = VM.require("potlock.near/widget/SDK.donate") || @@ -51,8 +51,8 @@ const ModalMain = styled.div` align-items: center; justify-content: center; width: 100%; - gap: 24px; - padding: 80px 36px; + gap: 2rem; + padding: 40px 32px; `; const ModalFooter = styled.div` @@ -67,42 +67,87 @@ const ModalFooter = styled.div` border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; `; + +const ModalHeader = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +`; + const HeaderIcon = styled.div` - padding: 1rem; - width: 64px; - height: 64px; - border-radius: 44px; - background: radial-gradient(97.66% 97.66% at 50% 2.34%, #e84a5b 0%, #c02031 100%); - box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.25) inset; + padding: 12px; + width: 48px; + height: 48px; + border-radius: 50%; + background: #dd3345; + box-shadow: 0px 0px 0px 6px #fee6e5; svg { width: 100%; height: 100%; } `; + +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; + } +`; +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%; + } + } +`; + const Amount = styled.div` - color: #292929; - font-size: 32px; - font-weight: 600; - line-height: 40px; - font-family: "Lora"; + font-size: 22px; + font-weight: 500; + letter-spacing: -0.33px; + text-transform: uppercase; `; const AmountUsd = styled.div` color: #7b7b7b; - font-size: 14px; - font-weight: 400; - line-height: 24px; - word-wrap: break-word; -`; - -const NearIcon = styled.svg` - width: 28px; - height: 28px; + font-size: 22px; `; -const ImgIcon = styled.img` - width: 28px; - height: 28px; +const ProjectName = styled.div` + display: flex; + align-items: center; + gap: 3px; + font-size: 14px; + div { + color: #7b7b7b; + } + a { + color: #525252; + &:hover { + text-decoration: none; + } + } `; const H1 = styled.h1` @@ -165,30 +210,52 @@ const SocialIcon = styled.svg` State.init({ showBreakdown: false, - successfulDonation, + successfulDonation: props.successfulDonation, }); -const getProfileDataForSuccessfulDonation = (donation) => { - const { donor_id, recipient_id, project_id } = donation; - Near.asyncView("social.near", "get", { - keys: [`${recipient_id || project_id}/profile/**`], - }).then((recipientData) => { +const { recipientProfile, successfulDonation, successfulApplication } = state; + +const successfulDonationVals = successfulDonation ? Object.values(successfulDonation) : []; +const recipientProfileVals = recipientProfile ? Object.values(recipientProfile) : []; + +const getProfileDataForSuccessfulDonation = (donationsObj) => { + const donations = Object.values(donationsObj); + donations.forEach((donation) => { + const { donor_id, recipient_id, project_id } = donation; Near.asyncView("social.near", "get", { - keys: [`${donor_id}/profile/**`], - }).then((donorData) => { - State.update({ - recipientProfile: recipientData[recipient_id || project_id]?.profile || {}, - donorProfile: donorData[donor_id]?.profile || {}, + keys: [`${recipient_id || project_id}/profile/**`], + }).then((recipientData) => { + Near.asyncView("social.near", "get", { + keys: [`${donor_id}/profile/**`], + }).then((donorData) => { + State.update({ + recipientProfile: { + ...recipientProfile, + [recipient_id || project_id]: recipientData[recipient_id || project_id]?.profile || {}, + }, + donorProfile: donorData[donor_id]?.profile || {}, + }); }); }); }); }; -if (state.successfulDonation && !state.recipientProfile) { - getProfileDataForSuccessfulDonation(state.successfulDonation); +if (successfulDonation && !recipientProfile) { + getProfileDataForSuccessfulDonation(successfulDonation); } +const getTotalAmount = () => { + const totalAmount = Big(0); + if (successfulDonation) { + successfulDonationVals.forEach( + (doantion) => (totalAmount = totalAmount.plus(Big(doantion.total_amount))) + ); + } + return totalAmount.toString(); +}; + +const totalAmount = getTotalAmount(); -if (props.isModalOpen && !state.successfulDonation) { +if (props.isModalOpen && !successfulDonation) { const transactionHashes = props.transactionHashes.split(","); for (let i = 0; i < transactionHashes.length; i++) { const txHash = transactionHashes[i]; @@ -205,7 +272,6 @@ if (props.isModalOpen && !state.successfulDonation) { }, body, }); - // console.log("tx res: ", res); if (res.ok) { const methodName = res.body.result.transaction.actions[0].FunctionCall.method_name; const successVal = res.body.result.status?.SuccessValue; @@ -213,7 +279,9 @@ if (props.isModalOpen && !state.successfulDonation) { if (methodName === "donate") { // NEAR donation State.update({ - successfulDonation: decoded, + successfulDonation: { + [decoded?.recipient_id || decoded?.project_id]: decoded, + }, }); break; } else if (methodName === "apply") { @@ -237,7 +305,9 @@ if (props.isModalOpen && !state.successfulDonation) { if (donations.length) { const donation = donations.sort((a, b) => b.donated_at_ms - a.donated_at_ms)[0]; State.update({ - successfulDonation: donation, + successfulDonation: { + [donation.recipient_id]: donation, + }, }); } }) @@ -253,27 +323,42 @@ if (props.isModalOpen && !state.successfulDonation) { } } -const ftMetadata = Near.view(state.successfulDonation?.ft_id, "ft_metadata", {}); +const ftMetadata = []; -// console.log("state in modal success: ", state); +if (statusbar.successfulDonation) { + ftMetadata = Object.keys(successfulDonation).map((projectId) => + Near.view(successfulDonation[projectId]?.ft_id, "ft_metadata", {}) + ); +} const twitterIntent = useMemo(() => { - if (!state.recipientProfile) return; + if (!recipientProfile) return; const twitterIntentBase = "https://twitter.com/intent/tweet?text="; + + let tag = ""; + const recipient_id = + successfulDonationVals[0].recipient_id || successfulDonationVals[0].project_id; + const profile = recipientProfileVals[0]; + const singlePorject = profile + ? profile.linktree?.twitter + ? `@${profile.linktree.twitter}` + : profile.name + : recipient_id; + + if (recipientProfileVals?.length === 1) { + tag = singlePorject; + } else { + tag = `${singlePorject} and ${recipientProfileVals?.length - 1} more`; + } + let url = DEFAULT_GATEWAY + - `${ownerId}/widget/Index?tab=project&projectId=${state.successfulDonation.recipient_id}&referrerId=${context.accountId}`; - let text = `I just donated to ${ - state.recipientProfile - ? state.recipientProfile.linktree?.twitter - ? `@${state.recipientProfile.linktree.twitter}` - : state.recipientProfile.name - : state.successfulDonation.recipient_id - } on @${POTLOCK_TWITTER_ACCOUNT_ID}! Support public goods at `; + `${ownerId}/widget/Index?tab=project&projectId=${recipient_id}&referrerId=${context.accountId}`; + let text = `I just donated to ${tag} 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(",")}`; -}, [state.successfulDonation, state.recipientProfile]); +}, [successfulDonation, recipientProfile]); return (

Thank you for applying!

- Your application status: {state.successfulApplication.status} + Your application status: {successfulApplication.status}
- ) : state.successfulDonation ? ( - <> - + ) : successfulDonation ? ( + + - + - - - - {state.successfulDonation?.total_amount - ? parseFloat( - // NEAR.fromIndivisible(state.successfulDonation.total_amount).toString() - Big(state.successfulDonation.total_amount) - .div(Big(10).pow(ftMetadata?.decimals || 24)) - .toFixed(2) - ) - : "-"} - - {ftMetadata?.icon ? ( - - ) : ( - - +
Donation Successful
+ +
Share to
+
+ + + +
+
+
+ +
+ {ftMetadata[0]?.icon ? ( + + ) : ( + + + - - )} - - {state.successfulDonation?.total_amount && - yoctosToUsd(state.successfulDonation.total_amount) && ( - - {yoctosToUsd(state.successfulDonation.total_amount) + " USD"} - - )} - - - Has been donated to - + + + + + + + )} + + {totalAmount + ? parseFloat( + // NEAR.fromIndivisible(totalAmount).toString() + Big(totalAmount) + .div(Big(10).pow(ftMetadata[0]?.decimals || 24)) + .toFixed(2) + ) + : "-"} + {ftMetadata[0]?.text || "NEAR"} + + {totalAmount && yoctosToUsd(totalAmount) && ( + {yoctosToUsd(totalAmount)} + )} +
+ + +
Has been donated to
+ - {state.successfulDonation && ( - - )} - {state.recipientProfile?.name || "-"} - - - - - { - onClose(); - }, - style: { width: "100%" }, - }} - /> - { - onClose(); - }, - style: { width: "100%" }, - }} - /> - -
- - - From - - {state.donorProfile && ( - - )} - {state.donorProfile?.name || "-"} - - - - Share to - - - - + {recipientProfileVals[0]?.name || + successfulDonationVals[0]?.recipient_id || + successfulDonationVals[0]?.project_id || + "-"} - - - + {successfulDonationVals.length > 1 && ( +
and {successfulDonationVals.length - 1} others
+ )} + + + + +
) : ( "" ), From 689b3981a934726098c1996af1d96150c9704489 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 5 Apr 2024 02:18:32 +0400 Subject: [PATCH 07/16] comment out pollForDonationSuccess for now --- apps/potlock/widget/ModalDonation/ConfirmDirect.jsx | 4 ++-- apps/potlock/widget/ModalDonation/ConfirmPot.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx index 83bdeb93..b25ee08b 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx @@ -384,7 +384,7 @@ const ConfirmDirect = (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 --------> - pollForDonationSuccess(successArgs); + // pollForDonationSuccess(successArgs); }); } else { transactions.push({ @@ -397,7 +397,7 @@ const ConfirmDirect = (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 --------> - pollForDonationSuccess(successArgs); + // pollForDonationSuccess(successArgs); } }; diff --git a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx index e026260b..adfd9611 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx @@ -346,7 +346,7 @@ const ConfirmPot = (props) => { Near.call(transactions); - pollForDonationSuccess(successArgs); + // pollForDonationSuccess(successArgs); }; return ( From 29b50861352d731a7f6a8141c1a7e8bcdf2f2066 Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 5 Apr 2024 17:38:57 +0400 Subject: [PATCH 08/16] fix donation success modal --- .../widget/ModalDonation/ConfirmDirect.jsx | 13 ++++--- .../widget/ModalDonation/ConfirmPot.jsx | 39 ++++++++++--------- apps/potlock/widget/ModalDonation/Main.jsx | 4 +- apps/potlock/widget/Project/ListPage.jsx | 2 +- apps/potlock/widget/Project/ModalSuccess.jsx | 13 ++++--- 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx index b25ee08b..c4fa4a27 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx @@ -175,7 +175,7 @@ const getFeesBasisPoints = (protocolConfig, potDetail, donationContractConfig) = }; const pollForDonationSuccess = ({ - projectId, + projectId: donatedProject, afterTs, accountId, openDonationSuccessModal, @@ -189,12 +189,15 @@ const pollForDonationSuccess = ({ (isPotDonation ? PotSDK : DonateSDK).asyncGetDonationsForDonor(accountId).then((donations) => { for (const donation of donations) { const { recipient_id, project_id, donated_at_ms, donated_at } = donation; // donation contract uses recipient_id, pot contract uses project_id; donation contract uses donated_at_ms, pot contract uses donated_at + if ( - ((recipient_id === projectId || project_id === projectId) && donated_at_ms > afterTs) || + ((project_id || recipient_id) === donatedProject && + (donated_at_ms || donated_at) > afterTs) || donated_at > afterTs ) { // display success message clearInterval(pollId); + openDonationSuccessModal({ projectId: donation, }); @@ -277,7 +280,7 @@ const ConfirmDirect = (props) => { const successArgs = { projectId, - now, + afterTs: now, accountId, openDonationSuccessModal, isPotDonation, @@ -384,7 +387,7 @@ const ConfirmDirect = (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 --------> - // pollForDonationSuccess(successArgs); + pollForDonationSuccess(successArgs); }); } else { transactions.push({ @@ -397,7 +400,7 @@ const ConfirmDirect = (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 --------> - // pollForDonationSuccess(successArgs); + pollForDonationSuccess(successArgs); } }; diff --git a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx index adfd9611..a84af554 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx @@ -245,27 +245,30 @@ const pollForDonationSuccess = ({ afterTs, accountId, openDonationSuccessModal, - amount, + potId, }) => { // poll for updates const pollIntervalMs = 1000; // const totalPollTimeMs = 60000; // consider adding in to make sure interval doesn't run indefinitely const pollId = setInterval(() => { - PotSDK.asyncGetDonationsForDonor(accountId).then((alldonations) => { - const donations = {}; - for (const donation of alldonations) { - const { project_id, donated_at_ms, donated_at } = donation; - if ((projectIds.includes(project_id) && donated_at_ms > afterTs) || donated_at > afterTs) { - donations[project_id] = donation; + PotSDK.asyncGetDonationsForDonor(potId, accountId) + .then((alldonations) => { + const donations = {}; + 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; + } } - } - if (Object.keys(donations).length === projectIds.length) { - // display success message - clearInterval(pollId); - donations.total_amount = amount; - openDonationSuccessModal(donations); - } - }); + if (Object.keys(donations).length === projectIds.length) { + // display success message + clearInterval(pollId); + openDonationSuccessModal(donations); + } + }) + .catch((err) => { + console.log(err); + }); }, pollIntervalMs); }; @@ -311,11 +314,11 @@ const ConfirmPot = (props) => { const successArgs = { projectIds: Object.keys(selectedProjects), - now, + afterTs: now, accountId, openDonationSuccessModal, - isPotDonation: true, amount, + potId, }; const transactions = []; @@ -346,7 +349,7 @@ const ConfirmPot = (props) => { Near.call(transactions); - // pollForDonationSuccess(successArgs); + pollForDonationSuccess(successArgs); }; return ( diff --git a/apps/potlock/widget/ModalDonation/Main.jsx b/apps/potlock/widget/ModalDonation/Main.jsx index dd72882e..4aae0073 100644 --- a/apps/potlock/widget/ModalDonation/Main.jsx +++ b/apps/potlock/widget/ModalDonation/Main.jsx @@ -310,7 +310,9 @@ useEffect(() => { setActiveRounds((prev) => [...(prev || [])]); } }) - .catch((err) => {}); + .catch((err) => { + setActiveRounds((prev) => [...(prev || [])]); + }); } }); } diff --git a/apps/potlock/widget/Project/ListPage.jsx b/apps/potlock/widget/Project/ListPage.jsx index 8801ed4a..3ce8c3f3 100644 --- a/apps/potlock/widget/Project/ListPage.jsx +++ b/apps/potlock/widget/Project/ListPage.jsx @@ -820,7 +820,7 @@ return ( {state.isModalOpen && ( { if (!recipientProfile) return; const twitterIntentBase = "https://twitter.com/intent/tweet?text="; - let tag = ""; const recipient_id = successfulDonationVals[0].recipient_id || successfulDonationVals[0].project_id; const profile = recipientProfileVals[0]; @@ -345,11 +344,13 @@ const twitterIntent = useMemo(() => { : profile.name : recipient_id; - if (recipientProfileVals?.length === 1) { - tag = singlePorject; - } else { - tag = `${singlePorject} and ${recipientProfileVals?.length - 1} more`; - } + const tag = + `${singlePorject}` + + (successfulDonationVals.length > 1 + ? ` and ${successfulDonationVals?.length - 1} other${ + successfulDonationVals?.length === 2 ? "" : "s" + }` + : ""); let url = DEFAULT_GATEWAY + From 1d5623407fa478186d09f933e5fe9d38087ea37f Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 5 Apr 2024 17:44:48 +0400 Subject: [PATCH 09/16] fix random donation --- apps/potlock/widget/Project/ListPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/potlock/widget/Project/ListPage.jsx b/apps/potlock/widget/Project/ListPage.jsx index 3ce8c3f3..90e72148 100644 --- a/apps/potlock/widget/Project/ListPage.jsx +++ b/apps/potlock/widget/Project/ListPage.jsx @@ -769,7 +769,7 @@ const handleTag = (key) => { const getRandomProject = () => { if (projects) { const randomIndex = Math.floor(Math.random() * projects.length); - return projects[randomIndex]; + return projects[randomIndex]?.registered_by; } }; From 9068845546cf9560333c399d43c1314c8a5cea7c Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 5 Apr 2024 23:58:56 +0400 Subject: [PATCH 10/16] fix pot public donations error --- apps/potlock/widget/Pots/Detail.jsx | 27 +++++++++++++++++-- apps/potlock/widget/Pots/Donations.jsx | 11 ++++---- .../widget/Pots/PoolAllocationTable.jsx | 19 +++++++------ apps/potlock/widget/SDK/pot.jsx | 14 ++++++---- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/apps/potlock/widget/Pots/Detail.jsx b/apps/potlock/widget/Pots/Detail.jsx index 5668f4a2..b6b5d7e3 100644 --- a/apps/potlock/widget/Pots/Detail.jsx +++ b/apps/potlock/widget/Pots/Detail.jsx @@ -15,6 +15,7 @@ const { const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { getConfig: () => {}, asyncGetApplications: () => {}, + asyncGetPublicRoundDonations: () => {}, }; const potDetail = PotSDK.getConfig(potId); @@ -111,8 +112,6 @@ if (state.sybilRequirementMet === null) { } } -// console.log("state in pot detail: ", state); - const noPot = potDetail === undefined; const loading = potDetail === null; @@ -286,6 +285,28 @@ const registrationApprovedOrNoRegistryProvider = const isError = state.applicationMessageError || state.daoAddressError; +// 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(parseInt(donationsCount / limit) + (donationsCount % limit > 0 ? 1 : 0)).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 from ${index} to ${index * limit}`, error); + } +}, "pot-public-donations"); + +const allDonations = allDonationsPaginated ? allDonationsPaginated.flat() : null; + return ( @@ -319,6 +341,7 @@ return ( props={{ ...props, potDetail: potDetail, + allDonations, sybilRequirementMet: state.sybilRequirementMet, }} /> diff --git a/apps/potlock/widget/Pots/Donations.jsx b/apps/potlock/widget/Pots/Donations.jsx index 0ddc251e..afc62de6 100644 --- a/apps/potlock/widget/Pots/Donations.jsx +++ b/apps/potlock/widget/Pots/Donations.jsx @@ -1,6 +1,7 @@ // get donations const { potId, + allDonations, potDetail: { base_currency }, } = props; @@ -16,10 +17,8 @@ const { getTimePassed, _address } = VM.require(`${ownerId}/widget/Components.Don const PotSDK = VM.require("potlock.near/widget/SDK.pot") || { getPublicRoundDonations: () => {}, }; -const publicRoundDonations = PotSDK.getPublicRoundDonations(potId); State.init({ - allDonations: null, filteredDonations: [], currentFilter: "date", filter: { @@ -28,11 +27,11 @@ State.init({ }, }); -const { allDonations, filteredDonations, currentFilter, filter } = state; +const { filteredDonations, currentFilter, filter } = state; -if (publicRoundDonations && !allDonations) { - publicRoundDonations.reverse(); - State.update({ filteredDonations: publicRoundDonations, allDonations: publicRoundDonations }); +if (allDonations) { + const sortedDonations = [...allDonations].reverse(); + State.update({ filteredDonations: sortedDonations }); } if (!allDonations) return
; diff --git a/apps/potlock/widget/Pots/PoolAllocationTable.jsx b/apps/potlock/widget/Pots/PoolAllocationTable.jsx index 50fd386a..c6e45d9e 100644 --- a/apps/potlock/widget/Pots/PoolAllocationTable.jsx +++ b/apps/potlock/widget/Pots/PoolAllocationTable.jsx @@ -2,7 +2,13 @@ const { potId, env, hrefWithParams, - potDetail: { base_currency, total_public_donations, matching_pool_balance }, + allDonations, + potDetail: { + base_currency, + total_public_donations, + matching_pool_balance, + public_donations_count, + }, } = props; const { ownerId, NADA_BOT_URL, SUPPORTED_FTS } = VM.require("potlock.near/widget/constants") || { @@ -65,7 +71,6 @@ const calcUniqueDonors = (donations) => { }; const calcMatchedAmount = (donations) => { - console.log("donations", donations); const total = Big(0); donations.forEach((donation) => { total = total.plus(Big(donation.net_amount)); @@ -74,16 +79,14 @@ const calcMatchedAmount = (donations) => { return amount; }; -const allDonationsForPot = Near.view(potId, "get_public_round_donations", {}); - -const uniqueDonorIds = allDonationsForPot - ? new Set(allDonationsForPot.map((donation) => donation.donor_id)) +const uniqueDonorIds = allDonations + ? new Set(allDonations.map((donation) => donation.donor_id)) : new Set([]); const donorsCount = uniqueDonorIds.size; -if (!allPayouts && allDonationsForPot?.length > 0) { - const calculatedPayouts = calculatePayouts(allDonationsForPot, matching_pool_balance); +if (!allPayouts && allDonations?.length > 0) { + const calculatedPayouts = calculatePayouts(allDonations, matching_pool_balance); let allPayouts = []; diff --git a/apps/potlock/widget/SDK/pot.jsx b/apps/potlock/widget/SDK/pot.jsx index a7dd4c40..1000a0ab 100644 --- a/apps/potlock/widget/SDK/pot.jsx +++ b/apps/potlock/widget/SDK/pot.jsx @@ -27,11 +27,15 @@ return { asyncGetMatchingPoolDonations: (potId) => { return Near.asyncView(potId, "get_matching_pool_donations", {}); }, - getPublicRoundDonations: (potId) => { - return Near.view(potId, "get_public_round_donations", {}); - }, - asyncGetPublicRoundDonations: (potId) => { - return Near.asyncView(potId, "get_public_round_donations", {}); + getPublicRoundDonations: (potId, args) => { + return Near.view(potId, "get_public_round_donations", { + ...(args || {}), + }); + }, + asyncGetPublicRoundDonations: (potId, args) => { + return Near.asyncView(potId, "get_public_round_donations", { + ...(args || {}), + }); }, getDonationsForDonor: (potId, accountId) => { return Near.view(potId, "get_donations_for_donor", { donor_id: accountId }); From 2ecc31175778e6944a83e68db3a962e3485f9f5f Mon Sep 17 00:00:00 2001 From: M-RB3 Date: Fri, 5 Apr 2024 23:59:05 +0400 Subject: [PATCH 11/16] modal fixes --- apps/potlock/widget/Cart/BreakdownSummary.jsx | 2 +- apps/potlock/widget/Inputs/Dropdown.jsx | 2 +- .../widget/ModalDonation/AmountInput.jsx | 10 ++++-- .../widget/ModalDonation/ConfirmDirect.jsx | 2 +- .../widget/ModalDonation/ConfirmPot.jsx | 6 ++-- apps/potlock/widget/ModalDonation/Form.jsx | 33 ++++++++++--------- apps/potlock/widget/ModalDonation/FormPot.jsx | 15 +++++---- apps/potlock/widget/ModalDonation/Main.jsx | 9 ++--- apps/potlock/widget/Project/Card.jsx | 4 ++- apps/potlock/widget/Project/ListPage.jsx | 2 +- 10 files changed, 49 insertions(+), 36 deletions(-) diff --git a/apps/potlock/widget/Cart/BreakdownSummary.jsx b/apps/potlock/widget/Cart/BreakdownSummary.jsx index fad4f98a..5c622368 100644 --- a/apps/potlock/widget/Cart/BreakdownSummary.jsx +++ b/apps/potlock/widget/Cart/BreakdownSummary.jsx @@ -227,7 +227,7 @@ return ( // onClick={() => State.update({ showBreakdown: !state.showBreakdown })} >
- breakdown + Breakdown
{openFilter && ( - e.stopPropagation()}> + e.stopPropagation()} style={props.menuStyle || {}}> {sortList.map((option) => ( ); const AmountInput = (props) => { - const { value, HandleAmoutChange, donationType, denominationOptions } = props; + const { value, HandleAmoutChange, donationType, denominationOptions, selectedDenomination } = + props; return ( { placeholder="0" onChange={(e) => HandleAmoutChange(e.target.value)} /> -
{nearToUsd ? `~$ ${nearToUsd * value}` : ""}
+
+ {" "} + {nearToUsd && selectedDenomination.value === "NEAR" + ? `~$ ${(nearToUsd * value).toFixed(2)}` + : ""} +
{donationType === "pot" || denominationOptions.length === 1 ? ( diff --git a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx index c4fa4a27..0198f703 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmDirect.jsx @@ -415,7 +415,7 @@ const ConfirmDirect = (props) => {
{amount} {selectedDenomination.text}
- {nearToUsd &&
~${nearToUsd * amount}
} + {nearToUsd &&
~${(nearToUsd * amount).toFixed(2)}
}
diff --git a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx index a84af554..7b5a5e1e 100644 --- a/apps/potlock/widget/ModalDonation/ConfirmPot.jsx +++ b/apps/potlock/widget/ModalDonation/ConfirmPot.jsx @@ -64,11 +64,11 @@ const Amout = styled.div` .toggle-icon { width: 8px; display: flex; - transition: all 300ms ease-in-out; margin-left: auto; overflow: hidden; svg { width: 100%; + transition: all 300ms ease-in-out; } } img, @@ -370,14 +370,14 @@ const ConfirmPot = (props) => {
{amount} {selectedDenomination.text}
- {nearToUsd &&
~${nearToUsd * amount}
} + {nearToUsd &&
~${(nearToUsd * amount).toFixed(2)}
}
div:last-of-type { + width: 100%; + } `; const Pot = styled.div` @@ -94,6 +97,10 @@ const SelectPot = ({ selectedRound, activeRoundsOptions, updateState }) => ( padding: "0.75rem 1rem", borderBottomWidth: "2px", borderRadius: "6px", + justifyContent: "space-between", + }, + menuStyle: { + top: "120%", }, FilterMenuCustomStyle: `left:0; right:auto;`, handleSortChange: ({ val }) => { @@ -118,7 +125,6 @@ const FormDirect = (props) => { donationType, ftBalance, activeRounds, - activeRound, NADABOT_CONTRACT_ID, accountId, } = props; @@ -159,7 +165,7 @@ const FormDirect = (props) => { if (amount === ".") amount = "0."; updateState({ amount, amountError: "" }); // error if amount is greater than balance - if (amount > ftBalance) { + if (amount > ftBalance && ftBalance !== null) { updateState({ amountError: "You don’t have enough balance to complete this transaction." }); } else if (!isFtDonation && parseFloat(amount) < 0.1) { updateState({ amountError: "Minimum donation is 0.1 NEAR" }); @@ -186,12 +192,7 @@ const FormDirect = (props) => { {donationType === "pot" && ( - {activeRounds?.length === 1 && ( - {Object.values(activeRoundsOptions)[0]?.label} - )} - {activeRounds?.length > 1 && ( - - )} + )}
-
- {ftBalance} {selectedDenomination.text} + {ftBalance && ( + +
+
+ {ftBalance} {selectedDenomination.text} +
+
available
-
available
-
- + + )} {amountError && } {needsToVerify && }