From f45cd32a3753f97b3d8f10293f531b1a3b098d28 Mon Sep 17 00:00:00 2001 From: fabiolalombardim Date: Thu, 14 Sep 2023 18:26:07 +0200 Subject: [PATCH 1/3] minor clean & disable new poll when user doesnt have tokens --- .../components/ProposalActionsDialog.tsx | 26 ++++++++++++++++--- src/services/services/lite/lite-services.ts | 10 +------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/modules/explorer/components/ProposalActionsDialog.tsx b/src/modules/explorer/components/ProposalActionsDialog.tsx index fcd5ff25..8a995d18 100644 --- a/src/modules/explorer/components/ProposalActionsDialog.tsx +++ b/src/modules/explorer/components/ProposalActionsDialog.tsx @@ -16,6 +16,8 @@ import { ProposalAction, ProposalFormLambda } from "modules/explorer/components/ import { useDAO } from "services/services/dao/hooks/useDAO" import { ProposalCreatorModal } from "modules/lite/explorer/pages/CreateProposal/ProposalCreatorModal" import { useIsProposalButtonDisabled } from "services/contracts/baseDAO/hooks/useCycleInfo" +import { useIsMember } from "modules/lite/explorer/hooks/useIsMember" +import { useTezos } from "services/beacon/hooks/useTezos" type RecursivePartial = { [P in keyof T]?: RecursivePartial @@ -125,6 +127,8 @@ export const ProposalActionsDialog: React.FC = ({ open, handleClose }) => const [openLiteProposal, setOpenLiteProposal] = useState(false) const liteDAOId = data?.liteDAOData?._id const shouldDisable = useIsProposalButtonDisabled(daoId) + const { network, account } = useTezos() + const isMember = useIsMember(network, data?.liteDAOData?.tokenAddress || "", account) const handleOpenCustomProposalModal = (key: ProposalAction) => { setProposalAction(key) @@ -167,8 +171,10 @@ export const ProposalActionsDialog: React.FC = ({ open, handleClose }) => - elem.id === "off-chain" + elem.id === "off-chain" && isMember ? handleLiteProposal() + : elem.id === "off-chain" && !isMember + ? null : !shouldDisable ? elem.isLambda ? handleOpenCustomProposalModal(elem.id) @@ -176,11 +182,25 @@ export const ProposalActionsDialog: React.FC = ({ open, handleClose }) => : null } > - + {elem.name} {elem.description}{" "} diff --git a/src/services/services/lite/lite-services.ts b/src/services/services/lite/lite-services.ts index 62150ee2..27202071 100644 --- a/src/services/services/lite/lite-services.ts +++ b/src/services/services/lite/lite-services.ts @@ -1,19 +1,11 @@ import { client, client_v2 } from "../graphql" import { Community, DAOListItem, DAOXTZTransferDTO, FetchedDAO, FetchedProposal, FetchedProposals } from "../types" -import { - GET_DAOS_QUERY, - GET_DAOS_QUERY_V2, - GET_DAO_QUERY, - GET_PROPOSALS_QUERY, - GET_PROPOSAL_QUERY, - GET_XTZ_TRANSFERS -} from "../dao/queries" +import { GET_DAO_QUERY, GET_PROPOSALS_QUERY, GET_PROPOSAL_QUERY, GET_XTZ_TRANSFERS } from "../dao/queries" import { LambdaProposal, Proposal } from "../dao/mappers/proposal/types" import dayjs from "dayjs" import { BaseDAO } from "../../contracts/baseDAO" import axios from "axios" import { EnvKey, getEnv } from "services/config" -import { getTokenMetadata } from "services/bakingBad/tokenBalances" import { Network } from "services/beacon" interface GetDAODTO { From 660f70319087942295a7a2dc74904c5fe03ef495 Mon Sep 17 00:00:00 2001 From: fabiolalombardim Date: Thu, 14 Sep 2023 18:46:31 +0200 Subject: [PATCH 2/3] set poll duration label --- .../explorer/pages/CreateProposal/index.tsx | 198 ++++++++++-------- 1 file changed, 108 insertions(+), 90 deletions(-) diff --git a/src/modules/lite/explorer/pages/CreateProposal/index.tsx b/src/modules/lite/explorer/pages/CreateProposal/index.tsx index 6b24d0c2..5c237ceb 100644 --- a/src/modules/lite/explorer/pages/CreateProposal/index.tsx +++ b/src/modules/lite/explorer/pages/CreateProposal/index.tsx @@ -335,6 +335,11 @@ export const ProposalForm = ({ {isMobileSmall ? ( + + + Set Poll Duration + + {!isMobileSmall ? ( - - - { - if (getIn(values, "endTimeDays") === 0) { - setFieldValue("endTimeDays", "") - setFieldTouched("endTimeDays") - } - }} - onChange={(newValue: any) => { - if (newValue.target.value === "") { - setFieldValue("endTimeDays", null) - } else { - setFieldValue("endTimeDays", parseInt(newValue.target.value, 10)) - } - }} - /> - - - { - if (getIn(values, "endTimeHours") === 0) { - setFieldValue("endTimeHours", "") - } - }} - onChange={(newValue: any) => { - if (newValue.target.value === "") { - setFieldValue("endTimeHours", null) - } else { - setFieldValue("endTimeHours", parseInt(newValue.target.value, 10)) - } - }} - /> - - - - { - if (getIn(values, "endTimeMinutes") === 0) { - setFieldValue("endTimeMinutes", "") - } - }} - onChange={(newValue: any) => { - if (newValue.target.value === "") { - setFieldValue("endTimeMinutes", null) - } else { - setFieldValue("endTimeMinutes", parseInt(newValue.target.value, 10)) - } - }} - /> - - {getIn(values, "endTimeDays") !== null && - getIn(values, "endTimeHours") !== null && - getIn(values, "endTimeMinutes") !== null && - !hasErrors ? ( - - - End date: - - - {" "} - {dayjs(Number(finalDate)).format("MM/DD/YYYY h:mm A")} - + <> + + + Set Poll Duration - ) : null} + + { + if (getIn(values, "endTimeDays") === 0) { + setFieldValue("endTimeDays", "") + setFieldTouched("endTimeDays") + } + }} + onChange={(newValue: any) => { + if (newValue.target.value === "") { + setFieldValue("endTimeDays", null) + } else { + setFieldValue("endTimeDays", parseInt(newValue.target.value, 10)) + } + }} + /> + + + { + if (getIn(values, "endTimeHours") === 0) { + setFieldValue("endTimeHours", "") + } + }} + onChange={(newValue: any) => { + if (newValue.target.value === "") { + setFieldValue("endTimeHours", null) + } else { + setFieldValue("endTimeHours", parseInt(newValue.target.value, 10)) + } + }} + /> + - - {errors?.endTimeDays && touched.endTimeDays ? ( - {errors.endTimeDays} + + { + if (getIn(values, "endTimeMinutes") === 0) { + setFieldValue("endTimeMinutes", "") + } + }} + onChange={(newValue: any) => { + if (newValue.target.value === "") { + setFieldValue("endTimeMinutes", null) + } else { + setFieldValue("endTimeMinutes", parseInt(newValue.target.value, 10)) + } + }} + /> + + {getIn(values, "endTimeDays") !== null && + getIn(values, "endTimeHours") !== null && + getIn(values, "endTimeMinutes") !== null && + !hasErrors ? ( + + + End date: + + + {" "} + {dayjs(Number(finalDate)).format("MM/DD/YYYY h:mm A")} + + ) : null} - - + + + {errors?.endTimeDays && touched.endTimeDays ? ( + {errors.endTimeDays} + ) : null} + + + ) : null} From 754801bbb641f057b5badc2807bfd3b4b6ac7219 Mon Sep 17 00:00:00 2001 From: Manank Patni Date: Sat, 16 Sep 2023 00:02:57 +0530 Subject: [PATCH 3/3] Change logic for delegation voting power and add validation for user balance (#654) * Change logic for delegation voting power and add validation for user balance Signed-off-by: Manank Patni * Change voting power voting logic Signed-off-by: Manank Patni --------- Signed-off-by: Manank Patni --- src/models/Community.ts | 1 + .../User/components/DelegationBanner.tsx | 16 +-- .../lite/explorer/components/Choices.tsx | 26 ++++- .../lite/explorer/components/ProposalList.tsx | 3 +- .../lite/explorer/hooks/useCommunity.tsx | 3 +- .../lite/explorer/hooks/useCommunityToken.tsx | 5 +- .../lite/explorer/hooks/useHasVoted.tsx | 3 +- src/modules/lite/explorer/hooks/usePoll.tsx | 3 +- .../lite/explorer/hooks/usePollChoices.tsx | 3 +- src/modules/lite/explorer/hooks/usePolls.tsx | 4 +- src/modules/lite/explorer/hooks/useToken.tsx | 3 +- .../explorer/pages/CreateProposal/index.tsx | 5 +- .../explorer/pages/ProposalDetails/index.tsx | 10 +- src/services/bakingBad/delegations/index.ts | 104 +++--------------- src/services/bakingBad/tokenBalances/index.ts | 31 +++++- .../token/hooks/useDelegationVoteWeight.ts | 8 +- .../token/hooks/useUserTokenBalance.ts | 24 ++++ 17 files changed, 124 insertions(+), 128 deletions(-) create mode 100644 src/services/contracts/token/hooks/useUserTokenBalance.ts diff --git a/src/models/Community.ts b/src/models/Community.ts index f64b7658..46bc878c 100644 --- a/src/models/Community.ts +++ b/src/models/Community.ts @@ -24,4 +24,5 @@ export interface CommunityToken { symbol: string tokenAddress: string decimals: string + message?: string } diff --git a/src/modules/explorer/pages/User/components/DelegationBanner.tsx b/src/modules/explorer/pages/User/components/DelegationBanner.tsx index cc2465d2..a7948286 100644 --- a/src/modules/explorer/pages/User/components/DelegationBanner.tsx +++ b/src/modules/explorer/pages/User/components/DelegationBanner.tsx @@ -60,13 +60,12 @@ export const matchTextToStatus = (value: DelegationsType | undefined) => { export const Delegation: React.FC<{ daoId: string }> = ({ daoId }) => { const { data: dao } = useDAO(daoId) - const { network, account, connect } = useTezos() + const { account } = useTezos() const { data: delegatedTo, isLoading, refetch } = useDelegationStatus(dao?.data.token.contract) const [delegationStatus, setDelegationStatus] = useState(DelegationsType.NOT_DELEGATING) const [openModal, setOpenModal] = useState(false) - const { data: delegateVoteBalances } = useDelegationVoteWeight(dao?.data.token.contract, dao?.data.address) - const [voteWeight, setVoteWeight] = useState(new BigNumber(0)) + const { data: voteWeight } = useDelegationVoteWeight(dao?.data.token.contract) const [loadingRes, setLoadingRes] = useState(true) const [shouldRefetch, setShouldRefetch] = useState(true) @@ -97,15 +96,6 @@ export const Delegation: React.FC<{ daoId: string }> = ({ daoId }) => { setShouldRefetch(false) }, [delegatedTo]) - useEffect(() => { - let totalVoteWeight = new BigNumber(0) - delegateVoteBalances?.forEach(delegatedVote => { - const balance = new BigNumber(delegatedVote.balance) - totalVoteWeight = totalVoteWeight.plus(balance) - }) - setVoteWeight(totalVoteWeight) - }, [delegateVoteBalances]) - return ( @@ -114,7 +104,7 @@ export const Delegation: React.FC<{ daoId: string }> = ({ daoId }) => { These settings only affect your participation in off-chain polls - {dao && ( + {dao && voteWeight && ( Voting Weight diff --git a/src/modules/lite/explorer/components/Choices.tsx b/src/modules/lite/explorer/components/Choices.tsx index 686972c8..6361a10d 100644 --- a/src/modules/lite/explorer/components/Choices.tsx +++ b/src/modules/lite/explorer/components/Choices.tsx @@ -16,6 +16,11 @@ import { theme } from "theme" import { AddCircleOutline, RemoveCircleOutline } from "@material-ui/icons" import { FieldArray, Field } from "formik" import { TextField as FormikTextField } from "formik-material-ui" +import { useDAOID } from "modules/explorer/pages/DAO/router" +import { useDAO } from "services/services/dao/hooks/useDAO" +import { useToken } from "../hooks/useToken" +import { useUserTokenBalance } from "services/contracts/token/hooks/useUserTokenBalance" +import BigNumber from "bignumber.js" const ChoicesContainer = styled(Grid)(({ theme }) => ({ paddingBottom: 19, @@ -76,9 +81,24 @@ const CustomFormikTextField = withStyles({ disabled: {} })(FormikTextField) -export const Choices: React.FC = ({ choices, submitForm, isLoading, votingStrategy, setFieldValue }) => { +const MainButton = styled(Button)(({ theme }) => ({ + "&$disabled": { + boxShadow: "none" + } +})) + +export const Choices: React.FC = ({ choices, submitForm, isLoading, votingStrategy, setFieldValue, id }) => { const isMobileExtraSmall = useMediaQuery(theme.breakpoints.down("sm")) + const daoId = useDAOID() + const { data } = useDAO(daoId) + const liteDAOId = data?.liteDAOData?._id ? data?.liteDAOData?._id : id + const tokenAddress = useToken(liteDAOId) + const { data: userBalance } = useUserTokenBalance(tokenAddress) + console.log("userBalance: ", userBalance) + const canCreateProposal = userBalance && new BigNumber(userBalance).gt(0) ? true : false + console.log("canCreateProposal: ", canCreateProposal) + return ( @@ -175,9 +195,9 @@ export const Choices: React.FC = ({ choices, submitForm, isLoading, votingS {!isLoading ? ( - + ) : ( )} diff --git a/src/modules/lite/explorer/components/ProposalList.tsx b/src/modules/lite/explorer/components/ProposalList.tsx index 1bc59033..d5c2bc28 100644 --- a/src/modules/lite/explorer/components/ProposalList.tsx +++ b/src/modules/lite/explorer/components/ProposalList.tsx @@ -68,8 +68,9 @@ export const ProposalList: React.FC<{ polls: Poll[]; id: string | undefined; dao polls.forEach(async poll => { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/token/${communityId}`).then(async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) diff --git a/src/modules/lite/explorer/hooks/useCommunity.tsx b/src/modules/lite/explorer/hooks/useCommunity.tsx index d8b85245..a424b886 100644 --- a/src/modules/lite/explorer/hooks/useCommunity.tsx +++ b/src/modules/lite/explorer/hooks/useCommunity.tsx @@ -15,8 +15,9 @@ export const useCommunity = (daoId: string, isUpdated?: number) => { try { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/daos/${daoId.toString()}`).then(async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) diff --git a/src/modules/lite/explorer/hooks/useCommunityToken.tsx b/src/modules/lite/explorer/hooks/useCommunityToken.tsx index 4c0e76ab..551e064c 100644 --- a/src/modules/lite/explorer/hooks/useCommunityToken.tsx +++ b/src/modules/lite/explorer/hooks/useCommunityToken.tsx @@ -9,8 +9,9 @@ export const useCommunityToken = (communityId: any) => { if (communityId !== undefined) { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/token/${String(communityId)}`).then(async response => { if (!response.ok) { - const message = `An error has occurred: ${response.statusText}` - return + const data = await response.json() + const message = data.message + return message } const record: CommunityToken = await response.json() diff --git a/src/modules/lite/explorer/hooks/useHasVoted.tsx b/src/modules/lite/explorer/hooks/useHasVoted.tsx index 7f35c636..8766505a 100644 --- a/src/modules/lite/explorer/hooks/useHasVoted.tsx +++ b/src/modules/lite/explorer/hooks/useHasVoted.tsx @@ -19,8 +19,9 @@ export const useHasVoted = (refresh?: number) => { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/choices/${String(account)}/user`).then( async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) diff --git a/src/modules/lite/explorer/hooks/usePoll.tsx b/src/modules/lite/explorer/hooks/usePoll.tsx index 31e11307..cdf5c8fb 100644 --- a/src/modules/lite/explorer/hooks/usePoll.tsx +++ b/src/modules/lite/explorer/hooks/usePoll.tsx @@ -15,8 +15,9 @@ export const useSinglePoll = (pollId: string | undefined, id?: any, community?: try { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/polls/${pollId}/polls`).then(async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) diff --git a/src/modules/lite/explorer/hooks/usePollChoices.tsx b/src/modules/lite/explorer/hooks/usePollChoices.tsx index 87fcc996..a4ddfe40 100644 --- a/src/modules/lite/explorer/hooks/usePollChoices.tsx +++ b/src/modules/lite/explorer/hooks/usePollChoices.tsx @@ -14,8 +14,9 @@ export const usePollChoices = (poll: Poll | undefined, refresh?: number) => { if (poll) { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/choices/${poll._id}/find`).then(async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) diff --git a/src/modules/lite/explorer/hooks/usePolls.tsx b/src/modules/lite/explorer/hooks/usePolls.tsx index edfe2d5a..deabe3b3 100644 --- a/src/modules/lite/explorer/hooks/usePolls.tsx +++ b/src/modules/lite/explorer/hooks/usePolls.tsx @@ -14,8 +14,9 @@ export const usePolls = (id: any) => { async function fetchPoll() { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/polls/${id}/list`).then(async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) @@ -36,7 +37,6 @@ export const usePolls = (id: any) => { if (poll) { await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/choices/${poll._id}/votes`).then(async response => { if (!response.ok) { - console.log("error in query") return } const records: number = await response.json() diff --git a/src/modules/lite/explorer/hooks/useToken.tsx b/src/modules/lite/explorer/hooks/useToken.tsx index af8cad5b..a277f118 100644 --- a/src/modules/lite/explorer/hooks/useToken.tsx +++ b/src/modules/lite/explorer/hooks/useToken.tsx @@ -14,8 +14,9 @@ export const useToken = (daoId: string | undefined) => { const communityId = daoId await fetch(`${getEnv(EnvKey.REACT_APP_LITE_API_URL)}/token/${communityId}`).then(async response => { if (!response.ok) { + const data = await response.json() openNotification({ - message: "An error has occurred", + message: data.message, autoHideDuration: 2000, variant: "error" }) diff --git a/src/modules/lite/explorer/pages/CreateProposal/index.tsx b/src/modules/lite/explorer/pages/CreateProposal/index.tsx index 5c237ceb..50c74117 100644 --- a/src/modules/lite/explorer/pages/CreateProposal/index.tsx +++ b/src/modules/lite/explorer/pages/CreateProposal/index.tsx @@ -29,6 +29,7 @@ import { useToken } from "../../hooks/useToken" import { isWebUri } from "valid-url" import { useDAO } from "services/services/dao/hooks/useDAO" import { useDAOID } from "modules/explorer/pages/DAO/router" +import { useUserTokenBalance } from "services/contracts/token/hooks/useUserTokenBalance" dayjs.extend(duration) const ProposalContainer = styled(Grid)(({ theme }) => ({ @@ -435,6 +436,7 @@ export const ProposalForm = ({ ) : null} = props = } const res = await saveLiteProposal(signature, publicKey, payloadBytes) + const respData = await res.json() if (res.ok) { openNotification({ message: "Proposal created!", @@ -627,7 +630,7 @@ export const ProposalCreator: React.FC<{ id?: string; onClose?: any }> = props = : navigate.push(`/explorer/lite/dao/${id}/community`) } else { openNotification({ - message: "Proposal could not be created", + message: respData.message, autoHideDuration: 3000, variant: "error" }) diff --git a/src/modules/lite/explorer/pages/ProposalDetails/index.tsx b/src/modules/lite/explorer/pages/ProposalDetails/index.tsx index 99819c83..1e91affc 100644 --- a/src/modules/lite/explorer/pages/ProposalDetails/index.tsx +++ b/src/modules/lite/explorer/pages/ProposalDetails/index.tsx @@ -18,6 +18,8 @@ import { BackButton } from "modules/lite/components/BackButton" import { voteOnLiteProposal } from "services/services/lite/lite-services" import { useDelegationStatus } from "services/contracts/token/hooks/useDelegationStatus" import { useDAO } from "services/services/dao/hooks/useDAO" +import { useDelegationVoteWeight } from "services/contracts/token/hooks/useDelegationVoteWeight" +import BigNumber from "bignumber.js" const PageContainer = styled("div")({ marginBottom: 50, @@ -61,7 +63,7 @@ export const ProposalDetails: React.FC<{ id: string }> = ({ id }) => { const community = useCommunity(id) const poll = useSinglePoll(proposalId, id, community) const choices = usePollChoices(poll, refresh) - const { data: delegatedTo } = useDelegationStatus(dao?.data.token.contract) + const { data: voteWeight } = useDelegationVoteWeight(dao?.data.token.contract) const [selectedVotes, setSelectedVotes] = useState([]) useEffect(() => { @@ -97,6 +99,7 @@ export const ProposalDetails: React.FC<{ id: string }> = ({ id }) => { return } const resp = await voteOnLiteProposal(signature, publicKey, payloadBytes) + const response = await resp.json() if (resp.ok) { openNotification({ message: "Your vote has been submitted", @@ -107,13 +110,14 @@ export const ProposalDetails: React.FC<{ id: string }> = ({ id }) => { setSelectedVotes([]) } else { openNotification({ - message: `Something went wrong!!`, + message: response.message, autoHideDuration: 3000, variant: "error" }) return } } catch (error) { + console.log("error: ", error) openNotification({ message: `Something went wrong!!`, autoHideDuration: 3000, @@ -156,7 +160,7 @@ export const ProposalDetails: React.FC<{ id: string }> = ({ id }) => { {poll?.isActive === ProposalStatus.ACTIVE ? (