diff --git a/packages/kintsugi-next/package.json b/packages/kintsugi-next/package.json index 71a84f5e87..c035b4a85a 100644 --- a/packages/kintsugi-next/package.json +++ b/packages/kintsugi-next/package.json @@ -12,7 +12,7 @@ "dependencies": { "@interlay/monetary-js": "^0.5.3", "@next/env": "^13.3.0", - "@osn/icons": "^1.74.0", + "@osn/icons": "^1.80.0", "@svgr/webpack": "^7.0.0", "bignumber.js": "^9.0.2", "clsx": "^1.2.1", diff --git a/packages/next-common/components/addressCombo.js b/packages/next-common/components/addressCombo.js index 0c77c10271..ba2bf1f6c1 100644 --- a/packages/next-common/components/addressCombo.js +++ b/packages/next-common/components/addressCombo.js @@ -127,6 +127,9 @@ export default function AddressCombo({ const { identity } = useChainSettings(); const [identities, setIdentities] = useState({}); + const isValidAddress = + !allowInvalidAddress && (isAddress(address) || normalizeAddress(address)); + const fetchAddressIdentity = useCallback( (address) => { const identityAddress = encodeAddressToChain(address, identity); @@ -291,6 +294,12 @@ export default function AddressCombo({ )} {show && (accounts || []).length > 0 && listOptions} + + {!isValidAddress && ( +
+ Please fill a valid address +
+ )} ); } diff --git a/packages/next-common/components/avatar.js b/packages/next-common/components/avatar.js index 038faa09ba..29de5da28d 100644 --- a/packages/next-common/components/avatar.js +++ b/packages/next-common/components/avatar.js @@ -6,6 +6,7 @@ import makeBlockie from "ethereum-blockies-base64"; import { isEthereumAddress } from "@polkadot/util-crypto"; import { isPolkadotAddress } from "next-common/utils/viewfuncs"; import { tryConvertToEvmAddress } from "next-common/utils/hydradxUtil"; +import { useAddressAvatarMap } from "next-common/context/avatar"; const StyledIdenticon = styled(Identicon)` circle:first-child { @@ -33,9 +34,24 @@ export default function Avatar({ address, size = 24 }) { const maybeEvmAddress = tryConvertToEvmAddress(address); - if (isEthereumAddress(maybeEvmAddress)) { - const imgSize = (size / 10) * 8; + const addressAvatarMap = useAddressAvatarMap(); + const image = addressAvatarMap?.get(maybeEvmAddress); + const imgSize = (size / 10) * 8; + if (image) { + return ( + + + + ); + } + if (isEthereumAddress(maybeEvmAddress)) { return ( { + if (module === "referenda") { + dispatch(setReferendaDelegatesTriggerUpdate()); + } else if (module === "democracy") { + dispatch(setDemocracyDelegatesTriggerUpdate()); + } + }, [module]); + + const handleSubmit = useCallback( + async (api, signerAccount) => { + try { + const data = { + shortBio, + announcement, + announcementContentType, + }; + data.signature = await signMessage( + JSON.stringify(data), + signerAccount.realAddress, + signerAccount.meta.source, + ); + data.signerWallet = signerAccount.meta.source; + + const { error } = await nextApi.post( + `delegation/${module}/delegates/${address}/announcement`, + data, + ); + if (error) { + dispatch(newErrorToast(error.message)); + return; + } + dispatch(newSuccessToast("Announcement published successfully")); + triggerUpdate(); + onClose(); + } catch (e) { + if (e.message === "Cancelled") { + return; + } + dispatch(newErrorToast(e.message)); + } + }, + [ + module, + address, + shortBio, + announcement, + announcementContentType, + signMessage, + onClose, + triggerUpdate, + ], + ); + + return ( + +
+ Short Bio + +
+
+ Announcement + []} + minHeight={100} + previewerPlugins={[]} + setQuillRef={() => {}} + /> +
+
+ ); +} diff --git a/packages/next-common/components/delegation/delegate/common/cardListContainer.js b/packages/next-common/components/delegation/delegate/common/cardListContainer.js new file mode 100644 index 0000000000..3bf2e25a84 --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/cardListContainer.js @@ -0,0 +1,21 @@ +import { cn } from "next-common/utils"; +import { useNavCollapsed } from "next-common/context/nav"; + +export default function MemberCardListContainer({ children }) { + const [navCollapsed] = useNavCollapsed(); + + return ( +
+ {children} +
+ ); +} diff --git a/packages/next-common/components/delegation/delegate/common/detailButtons.js b/packages/next-common/components/delegation/delegate/common/detailButtons.js new file mode 100644 index 0000000000..b9978df206 --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/detailButtons.js @@ -0,0 +1,52 @@ +import SecondaryButton from "next-common/lib/button/secondary"; +import { SystemEdit2, SystemSubtract } from "@osn/icons/subsquare"; +import { useState } from "react"; +import AnnouncementEditPopup from "../AnnouncementEditPopup"; +import useRealAddress from "next-common/utils/hooks/useRealAddress"; + +export function EditButton({ address }) { + const [showEdit, setShowEdit] = useState(false); + return ( + <> + } + onClick={() => setShowEdit(true)} + > + Edit + + {showEdit && ( + setShowEdit(false)} + address={address} + /> + )} + + ); +} + +export function RevokeButton() { + return ( + } + > + Revoke + + ); +} + +export default function DetailButtons({ address }) { + const realAddress = useRealAddress(); + const isMyDelegate = address === realAddress; + + return ( + isMyDelegate && ( +
+ + +
+ ) + ); +} diff --git a/packages/next-common/components/delegation/delegate/common/empty.js b/packages/next-common/components/delegation/delegate/common/empty.js new file mode 100644 index 0000000000..fe638c23ce --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/empty.js @@ -0,0 +1,11 @@ +import { SystemMember } from "@osn/icons/subsquare"; +import NoData from "next-common/components/noData"; + +export default function DelegateEmpty() { + return ( + } + text="No delegates" + /> + ); +} diff --git a/packages/next-common/components/delegation/delegate/common/info/line.js b/packages/next-common/components/delegation/delegate/common/info/line.js new file mode 100644 index 0000000000..5fd4e4789c --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/info/line.js @@ -0,0 +1,3 @@ +export default function InfoLine({ children }) { + return
{children}
; +} diff --git a/packages/next-common/components/delegation/delegate/common/info/title.js b/packages/next-common/components/delegation/delegate/common/info/title.js new file mode 100644 index 0000000000..6e41552097 --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/info/title.js @@ -0,0 +1,7 @@ +export default function InfoTitle({ children }) { + return ( +

+ {children} +

+ ); +} diff --git a/packages/next-common/components/delegation/delegate/common/info/wrapper.js b/packages/next-common/components/delegation/delegate/common/info/wrapper.js new file mode 100644 index 0000000000..783ac14e91 --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/info/wrapper.js @@ -0,0 +1,3 @@ +export default function InfoWrapper({ children }) { + return
{children}
; +} diff --git a/packages/next-common/components/delegation/delegate/common/loadable.js b/packages/next-common/components/delegation/delegate/common/loadable.js new file mode 100644 index 0000000000..b8e977779e --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/loadable.js @@ -0,0 +1,12 @@ +import { isNil } from "lodash-es"; +import { SystemLoading } from "@osn/icons/subsquare"; + +export default function DelegatesLoadable({ delegates, children }) { + if (isNil(delegates)) { + return ( + + ); + } + + return children; +} diff --git a/packages/next-common/components/delegation/delegate/common/searchInput.jsx b/packages/next-common/components/delegation/delegate/common/searchInput.jsx new file mode 100644 index 0000000000..cfc26c14c6 --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/searchInput.jsx @@ -0,0 +1,25 @@ +import { MenuDelegation } from "@osn/icons/subsquare"; +import { noop } from "lodash-es"; +import Input from "next-common/components/input"; + +export default function DelegationSearchInput({ + address = "", + setAddress = noop, + delegateButton, +}) { + return ( +
+ } + value={address} + onChange={(e) => { + setAddress(e.target.value); + }} + /> + + {delegateButton} +
+ ); +} diff --git a/packages/next-common/components/delegation/delegate/common/sortSelect.jsx b/packages/next-common/components/delegation/delegate/common/sortSelect.jsx new file mode 100644 index 0000000000..d1d2294459 --- /dev/null +++ b/packages/next-common/components/delegation/delegate/common/sortSelect.jsx @@ -0,0 +1,36 @@ +import { noop } from "lodash-es"; +import Select from "next-common/components/select"; + +export default function DelegationSortSelect({ sort = "", setSort = noop }) { + return ( +
+ Sort by + setSelectedTrackId(value)} + small + /> + ); +} diff --git a/packages/next-common/components/statistics/styled.js b/packages/next-common/components/statistics/styled.js index bbedfe8577..ee797e18f3 100644 --- a/packages/next-common/components/statistics/styled.js +++ b/packages/next-common/components/statistics/styled.js @@ -14,11 +14,10 @@ export const OriginWrapper = styled.div` 0px 1.34018px 1.56354px rgba(30, 33, 52, 0.0119221), 0px 0.399006px 0.465507px rgba(30, 33, 52, 0.00807786); border-radius: 8px; - - margin-top: 16px; `; + export const Wrapper = tw(OriginWrapper)` -max-sm:!rounded-none + max-sm:!rounded-none `; export const Header = styled.div` diff --git a/packages/next-common/components/summary/allDelegation/allBeenDelegated.js b/packages/next-common/components/summary/allDelegation/allBeenDelegated.js index d8f1f2272b..c94f06beb6 100644 --- a/packages/next-common/components/summary/allDelegation/allBeenDelegated.js +++ b/packages/next-common/components/summary/allDelegation/allBeenDelegated.js @@ -1,10 +1,10 @@ import HStack from "next-common/components/styled/hStack"; -import GreyInfoPanel from "next-common/components/summary/styled/greyInfoPanel"; import ListSVG from "next-common/assets/imgs/icons/list.svg"; import Tooltip from "next-common/components/tooltip"; import { useState } from "react"; import AllBeenDelegatedListPopup from "next-common/components/summary/democracyAllBeenDelegatedPopup"; -import { Count, ListButton } from "./styled"; +import { ListButton } from "./styled"; +import AllBeenDelegatedInfo from "./allBeenDelegatedInfo"; export default function AllBeenDelegated({ beenDelegatedList }) { const [showAllBeenDelegatedPopup, setShowAllBeenDelegatedPopup] = @@ -13,9 +13,7 @@ export default function AllBeenDelegated({ beenDelegatedList }) { return ( <> - - Been delegated {beenDelegatedList.length} - +
diff --git a/packages/next-common/components/summary/allDelegation/allBeenDelegatedInfo.jsx b/packages/next-common/components/summary/allDelegation/allBeenDelegatedInfo.jsx new file mode 100644 index 0000000000..e752974c9d --- /dev/null +++ b/packages/next-common/components/summary/allDelegation/allBeenDelegatedInfo.jsx @@ -0,0 +1,14 @@ +import GreyInfoPanel from "../styled/greyInfoPanel"; +import { Count } from "./styled"; + +export default function AllBeenDelegatedInfo({ beenDelegatedList }) { + if (!beenDelegatedList?.length) { + return null; + } + + return ( + + Been delegated {beenDelegatedList.length} + + ); +} diff --git a/packages/next-common/components/summary/allDelegation/allMyDelegation.js b/packages/next-common/components/summary/allDelegation/allMyDelegation.js index c62ad58871..cd1790c221 100644 --- a/packages/next-common/components/summary/allDelegation/allMyDelegation.js +++ b/packages/next-common/components/summary/allDelegation/allMyDelegation.js @@ -1,10 +1,10 @@ import HStack from "next-common/components/styled/hStack"; -import GreyInfoPanel from "next-common/components/summary/styled/greyInfoPanel"; import ListSVG from "next-common/assets/imgs/icons/list.svg"; import Tooltip from "next-common/components/tooltip"; import { useState } from "react"; import AllMyDelegationPopup from "next-common/components/summary/democracyAllMyDelegationPopup"; -import { Count, ListButton } from "./styled"; +import { ListButton } from "./styled"; +import AllMyDelegationInfo from "./allMyDelegationInfo"; export default function AllMyDelegation({ delegations }) { const [showAllMyDelegationPopup, setShowAllMyDelegationPopup] = @@ -13,9 +13,7 @@ export default function AllMyDelegation({ delegations }) { return ( <> - - My delegation {delegations.length} - +
diff --git a/packages/next-common/components/summary/allDelegation/allMyDelegationInfo.jsx b/packages/next-common/components/summary/allDelegation/allMyDelegationInfo.jsx new file mode 100644 index 0000000000..25fe6047a7 --- /dev/null +++ b/packages/next-common/components/summary/allDelegation/allMyDelegationInfo.jsx @@ -0,0 +1,14 @@ +import GreyInfoPanel from "../styled/greyInfoPanel"; +import { Count } from "./styled"; + +export default function AllMyDelegationInfo({ delegations }) { + if (!delegations?.length) { + return null; + } + + return ( + + My delegation {delegations.length} + + ); +} diff --git a/packages/next-common/components/summary/allDelegation/delegationLink.js b/packages/next-common/components/summary/allDelegation/delegationLink.js new file mode 100644 index 0000000000..9d47681162 --- /dev/null +++ b/packages/next-common/components/summary/allDelegation/delegationLink.js @@ -0,0 +1,18 @@ +import { useChainSettings } from "next-common/context/chain"; +import Link from "next/link"; + +export default function ReferendaDelegationLink() { + const { + modules: { referenda: hasReferenda, democracy: hasDemocracy }, + } = useChainSettings(); + let delegationLink = "/delegation"; + if (hasReferenda && hasDemocracy) { + delegationLink = delegationLink + "?type=referenda"; + } + + return ( + + Delegate + + ); +} diff --git a/packages/next-common/components/summary/allDelegation/index.js b/packages/next-common/components/summary/allDelegation/index.js index 01d13a656b..34b4f9c52d 100644 --- a/packages/next-common/components/summary/allDelegation/index.js +++ b/packages/next-common/components/summary/allDelegation/index.js @@ -7,9 +7,9 @@ import { useSelector } from "react-redux"; import { useChainSettings } from "next-common/context/chain"; import useFetchMyReferendaDelegations from "next-common/utils/hooks/referenda/useFetchMyReferendaDelegations"; import { myReferendaDelegationsSelector } from "next-common/store/reducers/myOnChainData/referenda/myReferendaDelegations"; -import NewDelegateButton from "./newDelegateButton"; import AllMyDelegation from "./allMyDelegation"; import AllBeenDelegated from "./allBeenDelegated"; +import ReferendaDelegationLink from "./delegationLink"; const Wrapper = styled(flexBetweenCenter)` gap: 8px; @@ -41,7 +41,7 @@ export default function AllDelegation() { {!hideActionButtons && ( - + )} diff --git a/packages/next-common/components/summary/allDelegation/newDelegateButton.js b/packages/next-common/components/summary/allDelegation/newDelegateButton.js index 5aea174958..019e841be2 100644 --- a/packages/next-common/components/summary/allDelegation/newDelegateButton.js +++ b/packages/next-common/components/summary/allDelegation/newDelegateButton.js @@ -1,16 +1,27 @@ import { useCallback, useState } from "react"; import { useDispatch } from "react-redux"; -import { Button } from "next-common/components/summary/styled"; +import SecondaryButton from "next-common/lib/button/secondary"; import DelegatePopup from "next-common/components/gov2/delegatePopup"; import MoonDelegatePopup from "next-common/components/gov2/delegatePopup/moonPopup"; -import AddSVG from "next-common/assets/imgs/icons/add.svg"; +import { SystemPlus } from "@osn/icons/subsquare"; import useIsUseMetamask from "next-common/hooks/useIsUseMetamask"; import isMoonChain from "next-common/utils/isMoonChain"; import { clearVotingForEntries } from "next-common/utils/gov2/gov2ReferendumVote"; import { incMyReferendaDelegationsTrigger } from "next-common/store/reducers/myOnChainData/referenda/myReferendaDelegations"; import { newSuccessToast } from "next-common/store/reducers/toastSlice"; -export default function NewDelegateButton() { +/** + * @param {{ + * defaultTargetAddress?: string + * targetDisabled?: boolean + * } & ButtonProps} props + */ +export default function NewDelegateButton({ + defaultTargetAddress, + targetDisabled, + size = "small", + ...props +}) { const dispatch = useDispatch(); const [showDelegatePopup, setShowDelegatePopup] = useState(false); const isUseMetamask = useIsUseMetamask(); @@ -28,12 +39,20 @@ export default function NewDelegateButton() { return ( <> - + } + {...props} + onClick={() => { + setShowDelegatePopup(true); + }} + > + {props.children || "Delegate"} + {showDelegatePopup && ( setShowDelegatePopup(false)} /> diff --git a/packages/next-common/components/summary/delegation/delegationButton.js b/packages/next-common/components/summary/delegation/delegationButton.js index 250bdf247f..11f3d609c9 100644 --- a/packages/next-common/components/summary/delegation/delegationButton.js +++ b/packages/next-common/components/summary/delegation/delegationButton.js @@ -16,12 +16,7 @@ const RemoveButton = styled(Button)` padding: 7px; `; -export default function DelegationButton({ - delegating, - trackId, - onUndelegateInBlock, - onDelegateInBlock, -}) { +export default function DelegationButton({ delegating, trackId }) { const [isLoading, setIsLoading] = useState(false); const [showDelegatePopup, setShowDelegatePopup] = useState(false); const [showUndelegatePopup, setShowUndelegatePopup] = useState(false); @@ -60,14 +55,12 @@ export default function DelegationButton({ {showDelegatePopup && ( setShowDelegatePopup(false)} /> )} {showUndelegatePopup && ( setShowUndelegatePopup(false)} isLoading={isLoading} setIsLoading={setIsLoading} diff --git a/packages/next-common/components/summary/delegation/index.js b/packages/next-common/components/summary/delegation/index.js index 302ec2b702..c2bc330f3d 100644 --- a/packages/next-common/components/summary/delegation/index.js +++ b/packages/next-common/components/summary/delegation/index.js @@ -1,15 +1,9 @@ -import { useCallback } from "react"; -import { useDispatch } from "react-redux"; import styled from "styled-components"; -import { useTrackDelegating } from "next-common/utils/hooks/referenda/useTrackDelegating"; import useRealAddress from "next-common/utils/hooks/useRealAddress"; -import { newSuccessToast } from "next-common/store/reducers/toastSlice"; import DelegationInfo from "next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationInfo"; import DelegationButton from "./delegationButton"; -import { clearVotingForEntries } from "next-common/utils/gov2/gov2ReferendumVote"; import { useChainSettings } from "next-common/context/chain"; -import { incMyReferendaDelegationsTrigger } from "next-common/store/reducers/myOnChainData/referenda/myReferendaDelegations"; -import { useContextApi } from "next-common/context/api"; +import useSubAddressTrackDelegating from "next-common/utils/hooks/referenda/useSubAddressTrackDelegating"; const Wrapper = styled.div` display: flex; @@ -20,35 +14,15 @@ const Wrapper = styled.div` `; export default function Delegation({ trackId }) { - const dispatch = useDispatch(); - const api = useContextApi(); const realAddress = useRealAddress(); - const { delegating, refresh } = useTrackDelegating(api, trackId, realAddress); + const delegating = useSubAddressTrackDelegating(realAddress, trackId); const { hideActionButtons } = useChainSettings(); - const onDelegateInBlock = useCallback(() => { - clearVotingForEntries(); - refresh(); - dispatch(incMyReferendaDelegationsTrigger()); - dispatch(newSuccessToast("Delegate success")); - }, [dispatch, refresh]); - - const onUndelegateInBlock = useCallback(() => { - clearVotingForEntries(); - refresh(); - dispatch(newSuccessToast("Undelegated")); - }, [dispatch, refresh]); - return ( {!hideActionButtons && ( - + )} ); diff --git a/packages/next-common/components/summary/democracyAllMyDelegationPopup/allDelegationBar.js b/packages/next-common/components/summary/democracyAllMyDelegationPopup/allDelegationBar.js new file mode 100644 index 0000000000..cc80785058 --- /dev/null +++ b/packages/next-common/components/summary/democracyAllMyDelegationPopup/allDelegationBar.js @@ -0,0 +1,67 @@ +import React, { useMemo, useState } from "react"; +import styled from "styled-components"; +import HStack from "next-common/components/styled/hStack"; +import GreyInfoPanel from "../styled/greyInfoPanel"; +import Tooltip from "next-common/components/tooltip"; +import RemoveButton from "next-common/components/removeButton"; +import { + incMyReferendaDelegationsTrigger, + myReferendaDelegationsSelector, +} from "next-common/store/reducers/myOnChainData/referenda/myReferendaDelegations"; +import { useSelector } from "react-redux"; +import { useDispatch } from "react-redux"; +import useIsUseMetamask from "next-common/hooks/useIsUseMetamask"; +import isMoonChain from "next-common/utils/isMoonChain"; +import UndelegateAllPopup from "../delegation/undelegateAllPopup"; +import MoonUndelegateAllPopup from "../delegation/undelegateAllPopup/moonPopup"; + +const Count = styled.span` + color: var(--textSecondary); +`; + +export default function AllDelegationsBar() { + const dispatch = useDispatch(); + const delegations = useSelector(myReferendaDelegationsSelector); + const [showPopup, setShowPopup] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const isUseMetamask = useIsUseMetamask(); + + let TheUndelegatePopup = UndelegateAllPopup; + if (isMoonChain() && isUseMetamask) { + TheUndelegatePopup = MoonUndelegateAllPopup; + } + + const trackIds = useMemo(() => { + return (delegations || []).map((item) => item.trackId); + }, [delegations]); + + return ( + <> + + + My delegation {delegations?.length || 0} + + + +
+ setShowPopup(true)} + /> +
+
+
+ {showPopup && ( + setShowPopup(false)} + isLoading={isLoading} + setIsLoading={setIsLoading} + onInBlock={() => { + dispatch(incMyReferendaDelegationsTrigger()); + }} + /> + )} + + ); +} diff --git a/packages/next-common/components/summary/democracyAllMyDelegationPopup/index.js b/packages/next-common/components/summary/democracyAllMyDelegationPopup/index.js index 42b9280734..b6cc1dd0cf 100644 --- a/packages/next-common/components/summary/democracyAllMyDelegationPopup/index.js +++ b/packages/next-common/components/summary/democracyAllMyDelegationPopup/index.js @@ -1,79 +1,15 @@ -import React, { useMemo, useState } from "react"; +import React from "react"; import styled from "styled-components"; import BaseVotesPopup from "next-common/components/popup/baseVotesPopup"; import VStack from "next-common/components/styled/vStack"; import AllMyDelegationPopupList from "next-common/components/summary/democracyAllMyDelegationPopup/list"; import { noop } from "lodash-es"; -import HStack from "next-common/components/styled/hStack"; -import GreyInfoPanel from "../styled/greyInfoPanel"; -import Tooltip from "next-common/components/tooltip"; -import RemoveButton from "next-common/components/removeButton"; -import { - incMyReferendaDelegationsTrigger, - myReferendaDelegationsSelector, -} from "next-common/store/reducers/myOnChainData/referenda/myReferendaDelegations"; -import { useSelector } from "react-redux"; -import { useDispatch } from "react-redux"; -import useIsUseMetamask from "next-common/hooks/useIsUseMetamask"; -import isMoonChain from "next-common/utils/isMoonChain"; -import UndelegateAllPopup from "../delegation/undelegateAllPopup"; -import MoonUndelegateAllPopup from "../delegation/undelegateAllPopup/moonPopup"; +import AllDelegationsBar from "./allDelegationBar"; const StyledPopup = styled(BaseVotesPopup)` width: 610px; `; -const Count = styled.span` - color: var(--textSecondary); -`; - -function AllDelegationsBar() { - const dispatch = useDispatch(); - const delegations = useSelector(myReferendaDelegationsSelector); - const [showPopup, setShowPopup] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const isUseMetamask = useIsUseMetamask(); - - let TheUndelegatePopup = UndelegateAllPopup; - if (isMoonChain() && isUseMetamask) { - TheUndelegatePopup = MoonUndelegateAllPopup; - } - - const trackIds = useMemo(() => { - return (delegations || []).map((item) => item.trackId); - }, [delegations]); - - return ( - <> - - - My delegation {delegations?.length || 0} - - - -
- setShowPopup(true)} - /> -
-
-
- {showPopup && ( - setShowPopup(false)} - isLoading={isLoading} - setIsLoading={setIsLoading} - onInBlock={() => { - dispatch(incMyReferendaDelegationsTrigger()); - }} - /> - )} - - ); -} - export default function AllMyDelegationPopup({ setShow = noop }) { return ( setShow(false)}> diff --git a/packages/next-common/components/summary/democracyBeenDelegated/beenDelegatedInfo.js b/packages/next-common/components/summary/democracyBeenDelegated/beenDelegatedInfo.js index 30ac91b908..e60a0cc529 100644 --- a/packages/next-common/components/summary/democracyBeenDelegated/beenDelegatedInfo.js +++ b/packages/next-common/components/summary/democracyBeenDelegated/beenDelegatedInfo.js @@ -19,7 +19,7 @@ export default function BeenDelegatedInfo({ delegations, addressesCount }) { const { decimals, symbol } = useChainSettings(); return ( - + Been delegated , + Delegate + + ); +} diff --git a/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationButton.js b/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationButton.js deleted file mode 100644 index 8782a1ea50..0000000000 --- a/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationButton.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from "react"; -import { useState } from "react"; -import { Button } from "../styled"; -import DelegatePopup from "next-common/components/democracy/delegatePopup"; -import MoonDelegatePopup from "next-common/components/democracy/delegatePopup/moonPopup"; -import AddSVG from "next-common/assets/imgs/icons/add.svg"; -import RemoveSVG from "next-common/assets/imgs/icons/remove.svg"; -import UndelegatePopup from "./undelegatePopup"; -import MoonUndelegatePopup from "./undelegatePopup/moonPopup"; -import styled from "styled-components"; -import Tooltip from "../../tooltip"; -import isMoonChain from "next-common/utils/isMoonChain"; -import useIsUseMetamask from "next-common/hooks/useIsUseMetamask"; - -const RemoveButton = styled(Button)` - display: flex; - padding: 7px; -`; - -export default function DemocracySummaryDelegationButton({ - delegating, - onUndelegateInBlock, - onDelegateInBlock, -}) { - const [isLoading, setIsLoading] = useState(false); - const [showDelegatePopup, setShowDelegatePopup] = useState(false); - const [showUndelegatePopup, setShowUndelegatePopup] = useState(false); - const isUseMetamask = useIsUseMetamask(); - - let TheDelegatePopup = DelegatePopup; - let TheUndelegatePopup = UndelegatePopup; - if (isMoonChain() && isUseMetamask) { - TheDelegatePopup = MoonDelegatePopup; - TheUndelegatePopup = MoonUndelegatePopup; - } - - const addDelegationButton = ( - - ); - - const removeDelegationButton = ( - -
- setShowUndelegatePopup(true)} - > - - -
-
- ); - - return ( - <> - {delegating ? removeDelegationButton : addDelegationButton} - {showDelegatePopup && ( - setShowDelegatePopup(false)} - /> - )} - {showUndelegatePopup && ( - setShowUndelegatePopup(false)} - isLoading={isLoading} - setIsLoading={setIsLoading} - /> - )} - - ); -} diff --git a/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationInfo.js b/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationInfo.js index c75644cf04..886ed6512e 100644 --- a/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationInfo.js +++ b/packages/next-common/components/summary/democracySummaryDelegation/democracySummaryDelegationInfo.js @@ -27,7 +27,7 @@ export default function DemocracySummaryDelegationInfo({ delegating }) { const conviction = Conviction[delegating.conviction]; return ( - + Delegating to diff --git a/packages/next-common/components/summary/democracySummaryDelegation/index.js b/packages/next-common/components/summary/democracySummaryDelegation/index.js index 1db1152070..1358b5ceb9 100644 --- a/packages/next-common/components/summary/democracySummaryDelegation/index.js +++ b/packages/next-common/components/summary/democracySummaryDelegation/index.js @@ -1,13 +1,11 @@ -import React, { useCallback } from "react"; -import { useDispatch } from "react-redux"; +import React from "react"; import styled from "styled-components"; -import { newSuccessToast } from "next-common/store/reducers/toastSlice"; import DemocracySummaryDelegationInfo from "./democracySummaryDelegationInfo"; -import DemocracySummaryDelegationButton from "./democracySummaryDelegationButton"; -import useDemocracyDelegating from "../../../utils/hooks/referenda/useDemocracyDelegating"; import useRealAddress from "../../../utils/hooks/useRealAddress"; import { useChainSettings } from "next-common/context/chain"; -import { useContextApi } from "next-common/context/api"; +import RemoveDelegation from "./removeDelegation"; +import useSubDemocracyDelegating from "next-common/utils/hooks/referenda/useSubDemocracyDelegating"; +import DemocracyDelegationLink from "./delegationLink"; const Wrapper = styled.div` display: flex; @@ -17,32 +15,15 @@ const Wrapper = styled.div` `; export default function DemocracySummaryDelegation() { - const dispatch = useDispatch(); - const api = useContextApi(); const realAddress = useRealAddress(); - const { delegating, refresh } = useDemocracyDelegating(api, realAddress); + const { delegating } = useSubDemocracyDelegating(realAddress); const { hideActionButtons } = useChainSettings(); - const onDelegateInBlock = useCallback(() => { - refresh(); - dispatch(newSuccessToast("Delegate success")); - }, [dispatch, refresh]); - - const onUndelegateInBlock = useCallback(() => { - refresh(); - dispatch(newSuccessToast("Undelegated")); - }, [dispatch, refresh]); - return ( - {!hideActionButtons && ( - - )} + {!hideActionButtons && + (delegating ? : )} ); } diff --git a/packages/next-common/components/summary/democracySummaryDelegation/newDelegation.js b/packages/next-common/components/summary/democracySummaryDelegation/newDelegation.js new file mode 100644 index 0000000000..39f24ff179 --- /dev/null +++ b/packages/next-common/components/summary/democracySummaryDelegation/newDelegation.js @@ -0,0 +1,52 @@ +import React, { useState } from "react"; +import SecondaryButton from "next-common/lib/button/secondary"; +import DelegatePopup from "next-common/components/democracy/delegatePopup"; +import MoonDelegatePopup from "next-common/components/democracy/delegatePopup/moonPopup"; +import { SystemPlus } from "@osn/icons/subsquare"; +import isMoonChain from "next-common/utils/isMoonChain"; +import useIsUseMetamask from "next-common/hooks/useIsUseMetamask"; + +/** + * @param {{ + * defaultTargetAddress?: string + * targetDisabled?: boolean + * } & ButtonProps} props + */ +export default function DemocracyNewDelegation({ + defaultTargetAddress, + targetDisabled, + disabled, + size = "small", + ...props +}) { + const [showDelegatePopup, setShowDelegatePopup] = useState(false); + const isUseMetamask = useIsUseMetamask(); + + let TheDelegatePopup = DelegatePopup; + if (isMoonChain() && isUseMetamask) { + TheDelegatePopup = MoonDelegatePopup; + } + + return ( + <> + } + {...props} + onClick={() => { + setShowDelegatePopup(true); + }} + > + {props.children || "Delegate"} + + {showDelegatePopup && ( + setShowDelegatePopup(false)} + /> + )} + + ); +} diff --git a/packages/next-common/components/summary/democracySummaryDelegation/removeDelegation.js b/packages/next-common/components/summary/democracySummaryDelegation/removeDelegation.js new file mode 100644 index 0000000000..2d943d5202 --- /dev/null +++ b/packages/next-common/components/summary/democracySummaryDelegation/removeDelegation.js @@ -0,0 +1,54 @@ +import React, { useState } from "react"; +import { Button } from "../styled"; +import RemoveSVG from "next-common/assets/imgs/icons/remove.svg"; +import UndelegatePopup from "./undelegatePopup"; +import MoonUndelegatePopup from "./undelegatePopup/moonPopup"; +import styled from "styled-components"; +import Tooltip from "../../tooltip"; +import isMoonChain from "next-common/utils/isMoonChain"; +import useIsUseMetamask from "next-common/hooks/useIsUseMetamask"; + +const RemoveButton = styled(Button)` + display: flex; + padding: 7px; +`; + +export default function DemocracyRemoveDelegation({ ButtonComponent }) { + const [isLoading, setIsLoading] = useState(false); + const [showUndelegatePopup, setShowUndelegatePopup] = useState(false); + const isUseMetamask = useIsUseMetamask(); + + let TheUndelegatePopup = UndelegatePopup; + if (isMoonChain() && isUseMetamask) { + TheUndelegatePopup = MoonUndelegatePopup; + } + + const button = ButtonComponent ? ( + setShowUndelegatePopup(true)} + /> + ) : ( + setShowUndelegatePopup(true)} + > + + + ); + + return ( + <> + +
{button}
+
+ {showUndelegatePopup && ( + setShowUndelegatePopup(false)} + isLoading={isLoading} + setIsLoading={setIsLoading} + /> + )} + + ); +} diff --git a/packages/next-common/components/tooltip.jsx b/packages/next-common/components/tooltip.jsx index bf7d6d2554..b3b18b4195 100644 --- a/packages/next-common/components/tooltip.jsx +++ b/packages/next-common/components/tooltip.jsx @@ -24,6 +24,7 @@ export default function Tooltip({ sideOffset = 2, keepTooltipOpenAfterClick, icon, + delayDuration = 0, }) { const [open, setOpen] = React.useState(false); @@ -52,7 +53,7 @@ export default function Tooltip({ ); - const rootProps = { delayDuration: 0 }; + const rootProps = { delayDuration }; if (keepTooltipOpenAfterClick) { rootProps.open = open; } diff --git a/packages/next-common/context/avatar.js b/packages/next-common/context/avatar.js new file mode 100644 index 0000000000..7d1bf6189c --- /dev/null +++ b/packages/next-common/context/avatar.js @@ -0,0 +1,18 @@ +import React, { useContext } from "react"; + +const AvatarContext = React.createContext(); + +export default AvatarContext; + +export function AvatarContextProvider({ addressAvatarMap, children }) { + return ( + + {children} + + ); +} + +export function useAddressAvatarMap() { + const { addressAvatarMap } = useContext(AvatarContext) || {}; + return addressAvatarMap; +} diff --git a/packages/next-common/hooks/useSignMessage.js b/packages/next-common/hooks/useSignMessage.js index 87ada8373f..7af43c4e55 100644 --- a/packages/next-common/hooks/useSignMessage.js +++ b/packages/next-common/hooks/useSignMessage.js @@ -20,7 +20,7 @@ export function useSignMessage() { const extension = injectedWeb3?.[walletName]; if (!extension) { - throw new Error("Wallet not found"); + throw new Error("Wallet not found: " + walletName); } const wallet = await extension.enable("subsquare"); diff --git a/packages/next-common/package.json b/packages/next-common/package.json index bf04be05f6..77db796744 100644 --- a/packages/next-common/package.json +++ b/packages/next-common/package.json @@ -16,7 +16,7 @@ "@interlay/monetary-js": "0.5.4", "@mimirdev/apps-inject": "^0.2.0", "@mimirdev/apps-sdk": "^0.1.0", - "@osn/icons": "^1.74.0", + "@osn/icons": "^1.80.0", "@osn/polkadot-react-identicon": "^1.0.8", "@osn/previewer": "^1.3.5", "@osn/provider-options": "1.1.0", diff --git a/packages/next-common/services/url.js b/packages/next-common/services/url.js index 5c301a17c8..4ca2958815 100644 --- a/packages/next-common/services/url.js +++ b/packages/next-common/services/url.js @@ -107,3 +107,11 @@ export const overviewApi = { treasuryCouncilMotions: "overview/motions", openTCMotions: "overview/open-tc-motion", }; + +// delegation +// referenda +export const delegationReferendaDelegatesAddressApi = (address) => + `delegation/referenda/delegates/${address}`; +// democracy +export const delegationDemocracyDelegatesAddressApi = (address) => + `delegation/democracy/delegates/${address}`; diff --git a/packages/next-common/store/reducers/democracy/delegates.js b/packages/next-common/store/reducers/democracy/delegates.js new file mode 100644 index 0000000000..505840b1f5 --- /dev/null +++ b/packages/next-common/store/reducers/democracy/delegates.js @@ -0,0 +1,56 @@ +import { createSlice } from "@reduxjs/toolkit"; +import nextApi from "next-common/services/nextApi"; + +const name = "democracyDelegates"; + +const democracyDelegatesSlice = createSlice({ + name, + initialState: { + delegates: null, + isLoading: false, + trigger: 0, + }, + reducers: { + setDelegates(state, { payload }) { + state.delegates = payload; + }, + setLoading(state, { payload }) { + state.isLoading = payload; + }, + triggerUpdate(state) { + state.trigger = state.trigger + 1; + }, + }, +}); + +export const { + setDelegates: setDemocracyDelegates, + setLoading: setDemocracyDelegatesLoading, + triggerUpdate: setDemocracyDelegatesTriggerUpdate, +} = democracyDelegatesSlice.actions; + +export const fetchDemocracyDelegates = (sort, page = 1, pageSize = 18) => { + if (sort === "participation") { + sort = "participation_rate"; + } + + return async (dispatch) => { + try { + dispatch(setDemocracyDelegatesLoading(true)); + const { result } = await nextApi.fetch("delegation/democracy/delegates", { + sort, + page, + pageSize, + }); + dispatch(setDemocracyDelegates(result)); + } finally { + dispatch(setDemocracyDelegatesLoading(false)); + } + }; +}; + +export const democracyDelegatesSelector = (state) => state[name].delegates; +export const democracyDelegatesTriggerUpdateSelector = (state) => + state[name].trigger; + +export default democracyDelegatesSlice.reducer; diff --git a/packages/next-common/store/reducers/democracy/index.js b/packages/next-common/store/reducers/democracy/index.js index 8d9451e6e0..75570c25ef 100644 --- a/packages/next-common/store/reducers/democracy/index.js +++ b/packages/next-common/store/reducers/democracy/index.js @@ -1,9 +1,11 @@ import democracyVotes from "./votes"; import democracyVoteCalls from "./voteCalls"; import democracyInfo from "./info"; +import democracyDelegates from "./delegates"; export default { democracyVotes, democracyVoteCalls, democracyInfo, + democracyDelegates, }; diff --git a/packages/next-common/store/reducers/referenda/delegates.js b/packages/next-common/store/reducers/referenda/delegates.js new file mode 100644 index 0000000000..8a78f7b2bd --- /dev/null +++ b/packages/next-common/store/reducers/referenda/delegates.js @@ -0,0 +1,56 @@ +import { createSlice } from "@reduxjs/toolkit"; +import nextApi from "next-common/services/nextApi"; + +const name = "referendaDelegates"; + +const referendaDelegatesSlice = createSlice({ + name, + initialState: { + delegates: null, + isLoading: false, + trigger: 0, + }, + reducers: { + setDelegates(state, { payload }) { + state.delegates = payload; + }, + setLoading(state, { payload }) { + state.isLoading = payload; + }, + setTriggerUpdate(state) { + state.trigger = state.trigger + 1; + }, + }, +}); + +export const { + setDelegates: setReferendaDelegates, + setLoading: setReferendaDelegatesLoading, + setTriggerUpdate: setReferendaDelegatesTriggerUpdate, +} = referendaDelegatesSlice.actions; + +export const fetchReferendaDelegates = (sort, page = 1, pageSize = 18) => { + if (sort === "participation") { + sort = "participation_rate"; + } + + return async (dispatch) => { + try { + dispatch(setReferendaDelegatesLoading(true)); + const { result } = await nextApi.fetch("delegation/referenda/delegates", { + sort, + page, + pageSize, + }); + dispatch(setReferendaDelegates(result)); + } finally { + dispatch(setReferendaDelegatesLoading(false)); + } + }; +}; + +export const referendaDelegatesSelector = (state) => state[name].delegates; +export const referendaDelegatesTriggerUpdateSelector = (state) => + state[name].trigger; + +export default referendaDelegatesSlice.reducer; diff --git a/packages/next-common/store/reducers/referenda/index.js b/packages/next-common/store/reducers/referenda/index.js index 363b306393..844f82c11c 100644 --- a/packages/next-common/store/reducers/referenda/index.js +++ b/packages/next-common/store/reducers/referenda/index.js @@ -3,6 +3,7 @@ import referendaReferendumInfo from "./info"; import referendaVotes from "./votes"; import referendaMeta from "./meta"; import referendaTallyHistory from "./tallyHistory"; +import referendaDelegates from "./delegates"; export default { referendaIssuance, @@ -10,4 +11,5 @@ export default { referendaVotes, referendaMeta, referendaTallyHistory, + referendaDelegates, }; diff --git a/packages/next-common/utils/constants.js b/packages/next-common/utils/constants.js index 31369cd8e5..fa4a9bdcbe 100644 --- a/packages/next-common/utils/constants.js +++ b/packages/next-common/utils/constants.js @@ -101,6 +101,7 @@ export const CACHE_KEY = { navCollapsed: "nav-collapsed", navSubmenuVisible: "nav-submenu-visible", connectedAccount: "connected-account", + delegationPromptVisible: "delegation-prompt-visible", }; export const CHAIN = process.env.NEXT_PUBLIC_CHAIN; diff --git a/packages/next-common/utils/consts/menu/common.js b/packages/next-common/utils/consts/menu/common.js index ba18ebd59e..7310653318 100644 --- a/packages/next-common/utils/consts/menu/common.js +++ b/packages/next-common/utils/consts/menu/common.js @@ -3,9 +3,11 @@ import { MenuDiscussions, MenuCalendar, MenuOffChainVoting, + MenuDelegation, } from "@osn/icons/subsquare"; import getChainSettings from "../settings"; import { CHAIN } from "next-common/utils/constants"; +import { isKintsugiChain } from "next-common/utils/chain"; const chainSettings = getChainSettings(CHAIN); @@ -36,6 +38,24 @@ if (chainSettings.hasDiscussions !== false) { commonMenus.items.push(discussionsMenu); } +if ( + (chainSettings.hasReferenda || !chainSettings.noDemocracy) && + !isKintsugiChain(CHAIN) +) { + commonMenus.items.push({ + value: "delegation", + name: "Delegation", + pathname: "/delegation", + extraMatchNavMenuActivePathnames: [ + "/delegation/statistics", + "/delegation/mine", + "/delegation/mine/received", + "/delegation/mine/delegations", + ], + icon: , + }); +} + commonMenus.items.push({ value: "calendar", name: "Calendar", diff --git a/packages/next-common/utils/consts/settings/acala.js b/packages/next-common/utils/consts/settings/acala.js index 1b97403051..3e489cf9a2 100644 --- a/packages/next-common/utils/consts/settings/acala.js +++ b/packages/next-common/utils/consts/settings/acala.js @@ -87,6 +87,9 @@ const acala = { hasMultisig: true, multisigApiPrefix: "acala", description: "Cross-chain DeFi Hub for Polkadot, Kusama and beyond.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(100,90,255,0.10)", theme300: "rgba(100,90,255,0.40)", diff --git a/packages/next-common/utils/consts/settings/altair.js b/packages/next-common/utils/consts/settings/altair.js index fd95d6f255..a58b870704 100644 --- a/packages/next-common/utils/consts/settings/altair.js +++ b/packages/next-common/utils/consts/settings/altair.js @@ -58,6 +58,9 @@ const altair = { hasTipsModule: false, description: "The home for financing assets on Kusama. Powered by Centrifuge.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(255,192,18,0.10)", theme300: "rgba(255,192,18,0.40)", diff --git a/packages/next-common/utils/consts/settings/basilisk.js b/packages/next-common/utils/consts/settings/basilisk.js index 6caddd7e0b..df4627de4b 100644 --- a/packages/next-common/utils/consts/settings/basilisk.js +++ b/packages/next-common/utils/consts/settings/basilisk.js @@ -67,6 +67,9 @@ const basilisk = { useVoteCall: true, description: "Snek brings permissionless liquidity to the Kusama ecosystem. Swap tokens, flip NFTs and earn rewards. Help young crypto projects bootstrap liquidity and receive tokens.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(63,227,154,0.10)", theme300: "rgba(63,227,154,0.40)", diff --git a/packages/next-common/utils/consts/settings/bifrost.js b/packages/next-common/utils/consts/settings/bifrost.js index dedaa871ed..8d13602f4e 100644 --- a/packages/next-common/utils/consts/settings/bifrost.js +++ b/packages/next-common/utils/consts/settings/bifrost.js @@ -80,6 +80,11 @@ const bifrost = { showReferendaReferendumDelegationPercentage: true, description: "Provide LSD for 9+ blockchains and beyond, dedicated layer-1 built on Substrate with XCM for cross-chain staking.", + modules: { + referenda: true, + fellowship: true, + democracy: true, + }, useVoteCall: true, hasMultisig: true, multisigApiPrefix: "kbnc", diff --git a/packages/next-common/utils/consts/settings/bifrostPolkadot.js b/packages/next-common/utils/consts/settings/bifrostPolkadot.js index 4e36419f46..fe95579271 100644 --- a/packages/next-common/utils/consts/settings/bifrostPolkadot.js +++ b/packages/next-common/utils/consts/settings/bifrostPolkadot.js @@ -54,6 +54,11 @@ const bifrostPolkadot = { multisigApiPrefix: "pbnc", description: "Provide LSD for 9+ blockchains and beyond, dedicated layer-1 built on Substrate with XCM for cross-chain staking.", + modules: { + referenda: true, + fellowship: true, + democracy: true, + }, cssVarsLight: { theme100: "rgba(84,43,251,0.10)", theme300: "rgba(84,43,251,0.40)", diff --git a/packages/next-common/utils/consts/settings/centrifuge.js b/packages/next-common/utils/consts/settings/centrifuge.js index d553e11533..54ca08a3cd 100644 --- a/packages/next-common/utils/consts/settings/centrifuge.js +++ b/packages/next-common/utils/consts/settings/centrifuge.js @@ -76,6 +76,9 @@ const centrifuge = { hasDotreasury: true, hasMultisig: true, multisigApiPrefix: "cfg", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(18,83,255,0.10)", theme300: "rgba(18,83,255,0.40)", diff --git a/packages/next-common/utils/consts/settings/collectives.js b/packages/next-common/utils/consts/settings/collectives.js index 923c296f1f..dbae0c0df4 100644 --- a/packages/next-common/utils/consts/settings/collectives.js +++ b/packages/next-common/utils/consts/settings/collectives.js @@ -92,6 +92,9 @@ const collectives = { showAchainableLabels: true, description: "Collectives on Polkadot network. Polkadot Collectives Common Good Parachain", + modules: { + fellowship: true, + }, ...polkadotThemeVars, }; diff --git a/packages/next-common/utils/consts/settings/crust.js b/packages/next-common/utils/consts/settings/crust.js index f6994b873f..fa0e6dfb9f 100644 --- a/packages/next-common/utils/consts/settings/crust.js +++ b/packages/next-common/utils/consts/settings/crust.js @@ -69,6 +69,9 @@ const crust = { showAccountManagementTab: false, description: "CRUST implements the incentive layer protocol for decentralized storage. It is adaptable to multiple storage layer protocols such as IPFS, and provides support for the application layer. ", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(250,140,22,0.10)", theme300: "rgba(250,140,22,0.40)", diff --git a/packages/next-common/utils/consts/settings/darwinia2.js b/packages/next-common/utils/consts/settings/darwinia2.js index a2502b1762..953a54de2b 100644 --- a/packages/next-common/utils/consts/settings/darwinia2.js +++ b/packages/next-common/utils/consts/settings/darwinia2.js @@ -95,6 +95,10 @@ const darwinia2 = { description: "Darwinia Provides Cross-Chain Smart Contract Platform And Message Port Network.", useVoteCall: true, + modules: { + referenda: true, + democracy: true, + }, cssVarsLight: { theme100: "rgba(219,55,138,0.10)", theme300: "rgba(219,55,138,0.40)", diff --git a/packages/next-common/utils/consts/settings/hydradx.js b/packages/next-common/utils/consts/settings/hydradx.js index f7ba1c3c5e..525d1b9d78 100644 --- a/packages/next-common/utils/consts/settings/hydradx.js +++ b/packages/next-common/utils/consts/settings/hydradx.js @@ -82,6 +82,9 @@ const hydradx = { multisigApiPrefix: "hydradx", description: "HydraDX is a next-gen DeFi protocol which is designed to bring an ocean of liquidity to Polkadot. Our tool for the job the HydraDX Omnipool - an innovative Automated Market Maker (AMM) which unlocks unparalleled efficiencies by combining all assets in a single trading pool.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(246,41,124,0.10)", theme300: "rgba(246,41,124,0.40)", diff --git a/packages/next-common/utils/consts/settings/hydradxTestnet.js b/packages/next-common/utils/consts/settings/hydradxTestnet.js index 20e9738d25..2243df87be 100644 --- a/packages/next-common/utils/consts/settings/hydradxTestnet.js +++ b/packages/next-common/utils/consts/settings/hydradxTestnet.js @@ -29,6 +29,9 @@ const hydradxTestnet = { hasStatescan: true, hasSubscan: false, ethereumNetwork, + modules: { + democracy: true, + }, }; export default hydradxTestnet; diff --git a/packages/next-common/utils/consts/settings/interlay.js b/packages/next-common/utils/consts/settings/interlay.js index 3fd31d1ef8..26c2fe3a84 100644 --- a/packages/next-common/utils/consts/settings/interlay.js +++ b/packages/next-common/utils/consts/settings/interlay.js @@ -77,6 +77,9 @@ const interlay = { multisigApiPrefix: "interlay", hasTreasuryModule: false, hasTipsModule: false, + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(7,90,188,0.10)", theme300: "rgba(7,90,188,0.40)", diff --git a/packages/next-common/utils/consts/settings/karura.js b/packages/next-common/utils/consts/settings/karura.js index b90c7a84d0..94717d28fe 100644 --- a/packages/next-common/utils/consts/settings/karura.js +++ b/packages/next-common/utils/consts/settings/karura.js @@ -85,6 +85,9 @@ const karura = { noIdentityModule: true, useVoteCall: true, description: "Cross-chain DeFi Hub for Polkadot, Kusama and beyond.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(229,15,89,0.10)", theme300: "rgba(229,15,89,0.40)", diff --git a/packages/next-common/utils/consts/settings/khala.js b/packages/next-common/utils/consts/settings/khala.js index 7c25349460..a46f429142 100644 --- a/packages/next-common/utils/consts/settings/khala.js +++ b/packages/next-common/utils/consts/settings/khala.js @@ -49,6 +49,9 @@ const khala = { useVoteCall: true, hasMultisig: true, multisigApiPrefix: "khala", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(5,227,227,0.10)", theme300: "rgba(5,227,227,0.40)", diff --git a/packages/next-common/utils/consts/settings/kintsugi.js b/packages/next-common/utils/consts/settings/kintsugi.js index fea1e53e13..939bbc3ca3 100644 --- a/packages/next-common/utils/consts/settings/kintsugi.js +++ b/packages/next-common/utils/consts/settings/kintsugi.js @@ -83,6 +83,9 @@ const kintsugi = { useVoteCall: true, hasTreasuryModule: false, hasTipsModule: false, + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(247,205,69,0.10)", theme300: "rgba(247,205,69,0.40)", diff --git a/packages/next-common/utils/consts/settings/kusama.js b/packages/next-common/utils/consts/settings/kusama.js index 4d4d2032cd..9534660e88 100644 --- a/packages/next-common/utils/consts/settings/kusama.js +++ b/packages/next-common/utils/consts/settings/kusama.js @@ -112,6 +112,10 @@ const kusama = { description: "Kusama is a scalable multi-chain network for radical innovation and early stage Polkadot deployments. Expect Chaos. No promises.", hideNewTreasuryProposalButton: true, + modules: { + referenda: true, + fellowship: true, + }, cssVarsLight: { theme100: "rgba(230,0,122,0.10)", theme300: "rgba(230,0,122,0.40)", diff --git a/packages/next-common/utils/consts/settings/litentry.js b/packages/next-common/utils/consts/settings/litentry.js index 4cefacbba6..3cffea402f 100644 --- a/packages/next-common/utils/consts/settings/litentry.js +++ b/packages/next-common/utils/consts/settings/litentry.js @@ -82,6 +82,9 @@ const litentry = { multisigApiPrefix: "litentry", description: "The Litentry identity Hub allows you to aggregate your personal data from blockchains and platforms to manage granular access to dApps. Exist in a digital world without KYC. Get Maximum Privacy & Authorization Control. Share Interoperable Verifiable Credentials. Use Multi-chain Data from Web3 + Web2 Platforms", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(21,184,135,0.10)", theme300: "rgba(21,184,135,0.40)", diff --git a/packages/next-common/utils/consts/settings/litmus.js b/packages/next-common/utils/consts/settings/litmus.js index 3a1aa1f809..88c72d5533 100644 --- a/packages/next-common/utils/consts/settings/litmus.js +++ b/packages/next-common/utils/consts/settings/litmus.js @@ -66,6 +66,9 @@ const litmus = { multisigApiPrefix: "litmus", description: "Litmus is a companion canary network to Litentry and connects to the Kusama ecosystem as parachain.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(104,34,251,0.10)", theme300: "rgba(104,34,251,0.40)", diff --git a/packages/next-common/utils/consts/settings/moonbeam.js b/packages/next-common/utils/consts/settings/moonbeam.js index 4f3487b8f1..3a395decc6 100644 --- a/packages/next-common/utils/consts/settings/moonbeam.js +++ b/packages/next-common/utils/consts/settings/moonbeam.js @@ -57,6 +57,10 @@ const moonbeam = { }, description: "Solidity Smart Contracts on Polkadot. Moonbeam is a Polkadot parachain designed for developers that combines full Ethereum compatibility with the power of Polkadot.", + modules: { + referenda: true, + democracy: true, + }, cssVarsLight: { theme100: "rgba(230,0,122,0.10)", theme300: "rgba(230,0,122,0.40)", diff --git a/packages/next-common/utils/consts/settings/moonriver.js b/packages/next-common/utils/consts/settings/moonriver.js index c47efff1d6..4c982c456e 100644 --- a/packages/next-common/utils/consts/settings/moonriver.js +++ b/packages/next-common/utils/consts/settings/moonriver.js @@ -65,6 +65,10 @@ const moonriver = { description: "Solidity Smart Contracts on Kusama. Moonriver is a community-led cousin parachain on Kusama and will provide a permanently incentivized canary network for Moonbeam.", useVoteCall: true, + modules: { + referenda: true, + democracy: true, + }, cssVarsLight: { theme100: "rgba(79,204,198,0.10)", theme300: "rgba(79,204,198,0.40)", diff --git a/packages/next-common/utils/consts/settings/phala.js b/packages/next-common/utils/consts/settings/phala.js index f602ed4a27..88b643927b 100644 --- a/packages/next-common/utils/consts/settings/phala.js +++ b/packages/next-common/utils/consts/settings/phala.js @@ -73,6 +73,9 @@ const phala = { useVoteCall: true, hasMultisig: true, multisigApiPrefix: "phala", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(192,236,69,0.10)", theme300: "rgba(192,236,69,0.40)", diff --git a/packages/next-common/utils/consts/settings/polkadot.js b/packages/next-common/utils/consts/settings/polkadot.js index 14428bc7de..76451d80f0 100644 --- a/packages/next-common/utils/consts/settings/polkadot.js +++ b/packages/next-common/utils/consts/settings/polkadot.js @@ -119,6 +119,9 @@ const polkadot = { hideNewTreasuryProposalButton: true, noDemocracyModule: true, ...polkadotThemeVars, + modules: { + referenda: true, + }, }; export default polkadot; diff --git a/packages/next-common/utils/consts/settings/rococo.js b/packages/next-common/utils/consts/settings/rococo.js index 990157a4cf..2f79006e38 100644 --- a/packages/next-common/utils/consts/settings/rococo.js +++ b/packages/next-common/utils/consts/settings/rococo.js @@ -53,6 +53,10 @@ const rococo = { discourseForumLink: "https://forum.polkadot.network", description: "Polkadot’s Parachain Testnet", ...polkadotThemeVars, + modules: { + referenda: true, + fellowship: true, + }, }; export default rococo; diff --git a/packages/next-common/utils/consts/settings/turing.js b/packages/next-common/utils/consts/settings/turing.js index f388bfca38..439d3ba4ba 100644 --- a/packages/next-common/utils/consts/settings/turing.js +++ b/packages/next-common/utils/consts/settings/turing.js @@ -72,6 +72,9 @@ const turing = { hasSubscan: true, useVoteCall: true, description: "The Web 3.0 Hub for Automated DeFi and Payments", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(168,44,190,0.10)", theme300: "rgba(168,44,190,0.40)", diff --git a/packages/next-common/utils/consts/settings/vara.js b/packages/next-common/utils/consts/settings/vara.js index ec73d21058..e188bd55ed 100644 --- a/packages/next-common/utils/consts/settings/vara.js +++ b/packages/next-common/utils/consts/settings/vara.js @@ -87,6 +87,10 @@ const vara = { multisigApiPrefix: "vara", description: "Vara is an ultra-fast and scalable Layer-1 decentralized network powered by the Gear Protocol.", + modules: { + referenda: true, + fellowship: true, + }, cssVarsLight: { theme100: "rgba(11,234,179,0.10)", theme300: "rgba(11,234,179,0.40)", diff --git a/packages/next-common/utils/consts/settings/westendCollectives.js b/packages/next-common/utils/consts/settings/westendCollectives.js index 030d23b93a..47b9568e1b 100644 --- a/packages/next-common/utils/consts/settings/westendCollectives.js +++ b/packages/next-common/utils/consts/settings/westendCollectives.js @@ -57,6 +57,9 @@ const westendCollectives = { hasTreasuryModule: false, noIdentityModule: true, noDemocracyModule: true, + modules: { + fellowship: true, + }, cssVarsLight: { theme100: "rgba(239,72,106,0.10)", theme300: "rgba(239,72,106,0.40)", diff --git a/packages/next-common/utils/consts/settings/zeitgeist.js b/packages/next-common/utils/consts/settings/zeitgeist.js index b0322ce18d..b83791f6f3 100644 --- a/packages/next-common/utils/consts/settings/zeitgeist.js +++ b/packages/next-common/utils/consts/settings/zeitgeist.js @@ -66,6 +66,9 @@ const zeitgeist = { hasTipsModule: false, description: "A Prediction Markets protocol built on Polkadot. With the world’s leading forecasting minds, we’re building the best prediction markets app available.", + modules: { + democracy: true, + }, cssVarsLight: { theme100: "rgba(28,100,242,0.10)", theme300: "rgba(28,100,242,0.40)", diff --git a/packages/next-common/utils/gov2/gov2ReferendumVote.js b/packages/next-common/utils/gov2/gov2ReferendumVote.js index fe632fb84e..d957a62868 100644 --- a/packages/next-common/utils/gov2/gov2ReferendumVote.js +++ b/packages/next-common/utils/gov2/gov2ReferendumVote.js @@ -1,38 +1,68 @@ import { isNil } from "lodash-es"; -import { extractAddressAndTrackId } from "./utils"; import { calcVotes } from "next-common/utils/democracy/votes/passed/common"; +import { isSameAddress } from ".."; +import { u8aToHex } from "@polkadot/util"; -export async function getGov2TrackDelegation(api, trackId, address) { - const voting = await api.query.convictionVoting.votingFor(address, trackId); - const jsonVoting = voting?.toJSON(); - if (!jsonVoting) { - return null; - } +let votingForEntries = null; - return jsonVoting?.delegating; +async function queryEntries(api, startKey, num = 1000) { + return api.query.convictionVoting.votingFor.entriesPaged({ + args: [], + pageSize: num, + startKey, + }); } -let votingForEntries = null; +function normalizeVotingForEntry([storageKey, voting]) { + const account = storageKey.args[0].toString(); + const trackId = storageKey.args[1].toNumber(); + + return { + account, + trackId, + voting, + }; +} + +async function queryAllDelegatingEntries(api) { + let startKey = null; + let result = []; + let entries = await queryEntries(api, startKey, 1000); + while (entries.length > 0) { + const normalizedVotes = entries.map((item) => + normalizeVotingForEntry(item), + ); + const delegatingVotes = normalizedVotes.filter( + ({ voting }) => voting.isDelegating, + ); + result.push(...delegatingVotes); + + startKey = u8aToHex(entries[entries.length - 1][0]); + entries = await queryEntries(api, startKey, 1000); + } + + return result; +} export async function getGov2BeenDelegatedListByAddress(api, address, trackId) { if (!votingForEntries) { - votingForEntries = await api.query.convictionVoting.votingFor.entries(); + votingForEntries = await queryAllDelegatingEntries(api); } const beenDelegated = []; - for (const [storageKey, votingFor] of votingForEntries) { - const { address: delegator, trackId: _trackId } = - extractAddressAndTrackId(storageKey); + for (const { + account: delegator, + trackId: _trackId, + voting: votingFor, + } of votingForEntries) { if (!isNil(trackId)) { if (_trackId !== trackId) { continue; } } - if (!votingFor.isDelegating) { - continue; - } + const voting = votingFor.asDelegating.toJSON(); - if (voting.target !== address) { + if (!isSameAddress(voting.target, address)) { continue; } const votes = calcVotes( diff --git a/packages/next-common/utils/hooks/referenda/useDemocracyDelegating.js b/packages/next-common/utils/hooks/referenda/useDemocracyDelegating.js deleted file mode 100644 index c6b554f1c5..0000000000 --- a/packages/next-common/utils/hooks/referenda/useDemocracyDelegating.js +++ /dev/null @@ -1,36 +0,0 @@ -// copied from `useTrackDelegation` - -import { useCallback, useEffect, useState } from "react"; -import { getDemocracyDelegation } from "../../democracy/getDemocracyDelegation"; -import useIsMounted from "../useIsMounted"; - -export default function useDemocracyDelegating(api, address) { - const [delegating, setDelegating] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const isMounted = useIsMounted(); - - const refresh = useCallback(async () => { - if (!api || !address) { - return; - } - - setIsLoading(true); - try { - const delegating = await getDemocracyDelegation(api, address); - if (isMounted.current) { - setDelegating(delegating); - } - } finally { - if (isMounted.current) { - setIsLoading(false); - } - } - }, [api, isMounted, address]); - - useEffect(() => { - setDelegating(null); - refresh(); - }, [refresh]); - - return { delegating, isLoading, refresh }; -} diff --git a/packages/next-common/utils/hooks/referenda/useFetchMyReferendaDelegations/index.js b/packages/next-common/utils/hooks/referenda/useFetchMyReferendaDelegations/index.js index 2a72e96251..0859b7d552 100644 --- a/packages/next-common/utils/hooks/referenda/useFetchMyReferendaDelegations/index.js +++ b/packages/next-common/utils/hooks/referenda/useFetchMyReferendaDelegations/index.js @@ -5,7 +5,7 @@ import { myReferendaDelegationsTrigger, setMyReferendaDelegations, } from "next-common/store/reducers/myOnChainData/referenda/myReferendaDelegations"; -import getAddressTrackDelegations from "../useFetchReferendaDelegations/addressDelegations"; +import getAddressTrackDelegations from "../useFetchProfileReferendaDelegations/addressDelegations"; import { useContextApi } from "next-common/context/api"; export default function useFetchMyReferendaDelegations() { diff --git a/packages/next-common/utils/hooks/referenda/useFetchReferendaDelegations/addressDelegations.js b/packages/next-common/utils/hooks/referenda/useFetchProfileReferendaDelegations/addressDelegations.js similarity index 100% rename from packages/next-common/utils/hooks/referenda/useFetchReferendaDelegations/addressDelegations.js rename to packages/next-common/utils/hooks/referenda/useFetchProfileReferendaDelegations/addressDelegations.js diff --git a/packages/next-common/utils/hooks/referenda/useFetchReferendaDelegations/index.js b/packages/next-common/utils/hooks/referenda/useFetchProfileReferendaDelegations/index.js similarity index 100% rename from packages/next-common/utils/hooks/referenda/useFetchReferendaDelegations/index.js rename to packages/next-common/utils/hooks/referenda/useFetchProfileReferendaDelegations/index.js diff --git a/packages/next-common/utils/hooks/referenda/useSubAddressTrackDelegating.js b/packages/next-common/utils/hooks/referenda/useSubAddressTrackDelegating.js new file mode 100644 index 0000000000..b8218f6ec2 --- /dev/null +++ b/packages/next-common/utils/hooks/referenda/useSubAddressTrackDelegating.js @@ -0,0 +1,36 @@ +import { useContextApi } from "next-common/context/api"; +import { useEffect, useState } from "react"; +import { isNil } from "lodash-es"; + +export default function useSubAddressTrackDelegating(address, trackId) { + const api = useContextApi(); + const [delegating, setDelegating] = useState(null); + + useEffect(() => { + if (!api || !address || isNil(trackId)) { + return; + } + + let unsub; + api.query.convictionVoting + .votingFor(address, trackId, (optionalVotingFor) => { + const jsonVoting = optionalVotingFor?.toJSON(); + if (!jsonVoting) { + return setDelegating(null); + } + + return setDelegating(jsonVoting?.delegating); + }) + .then((result) => { + unsub = result; + }); + + return () => { + if (unsub) { + unsub(); + } + }; + }, [api, trackId, address]); + + return delegating; +} diff --git a/packages/next-common/utils/hooks/referenda/useSubDemocracyDelegating.js b/packages/next-common/utils/hooks/referenda/useSubDemocracyDelegating.js new file mode 100644 index 0000000000..8da60fd2b4 --- /dev/null +++ b/packages/next-common/utils/hooks/referenda/useSubDemocracyDelegating.js @@ -0,0 +1,38 @@ +import { useContextApi } from "next-common/context/api"; +import { useEffect, useState } from "react"; + +export default function useSubDemocracyDelegating(address) { + const api = useContextApi(); + const [delegating, setDelegating] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (!api || !address) { + return; + } + + setIsLoading(true); + let unsub; + api.query.democracy + ?.votingOf(address, (voting) => { + setIsLoading(false); + const jsonVoting = voting?.toJSON(); + if (!jsonVoting) { + return setDelegating(null); + } + + return setDelegating(jsonVoting?.delegating); + }) + .then((result) => { + unsub = result; + }); + + return () => { + if (unsub) { + unsub(); + } + }; + }, [api]); + + return { delegating, isLoading }; +} diff --git a/packages/next-common/utils/hooks/referenda/useTrackDelegating.js b/packages/next-common/utils/hooks/referenda/useTrackDelegating.js deleted file mode 100644 index 906bde511b..0000000000 --- a/packages/next-common/utils/hooks/referenda/useTrackDelegating.js +++ /dev/null @@ -1,35 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import useIsMounted from "../useIsMounted"; -import { getGov2TrackDelegation } from "../../gov2/gov2ReferendumVote"; -import { isNil } from "lodash-es"; - -export function useTrackDelegating(api, trackId, address) { - const [delegating, setDelegating] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const isMounted = useIsMounted(); - - const refresh = useCallback(async () => { - if (!api || !address || isNil(trackId)) { - return; - } - - setIsLoading(true); - try { - const delegating = await getGov2TrackDelegation(api, trackId, address); - if (isMounted.current) { - setDelegating(delegating); - } - } finally { - if (isMounted.current) { - setIsLoading(false); - } - } - }, [api, trackId, address, isMounted]); - - useEffect(() => { - setDelegating(null); - refresh(); - }, [api, trackId, address]); - - return { delegating, isLoading, refresh }; -} diff --git a/packages/next/components/myDelegation/layout.jsx b/packages/next/components/myDelegation/layout.jsx new file mode 100644 index 0000000000..4dde8ed523 --- /dev/null +++ b/packages/next/components/myDelegation/layout.jsx @@ -0,0 +1,21 @@ +import AccountSubTabs from "next-common/components/overview/account/subTabs"; +import PalletTabs from "next-common/components/profile/delegation/palletTabs"; +import { ModuleTab } from "next-common/components/profile/votingHistory/common"; +import { cn } from "next-common/utils"; + +export default function MyDelegationLayout({ children }) { + return ( + +
+ + +
+ {children} +
+ ); +} diff --git a/packages/next/components/myDelegation/myDelegations.jsx b/packages/next/components/myDelegation/myDelegations.jsx new file mode 100644 index 0000000000..6002ad2422 --- /dev/null +++ b/packages/next/components/myDelegation/myDelegations.jsx @@ -0,0 +1,12 @@ +import DelegatedVotes from "next-common/components/delegation/my-delegation/delegatedVotes"; +import MyDelegationLayout from "./layout"; + +export default function MyDelegations() { + return ( + +
+ +
+
+ ); +} diff --git a/packages/next/components/myDelegation/myReceivedDelegations.jsx b/packages/next/components/myDelegation/myReceivedDelegations.jsx new file mode 100644 index 0000000000..5ea936aff6 --- /dev/null +++ b/packages/next/components/myDelegation/myReceivedDelegations.jsx @@ -0,0 +1,12 @@ +import BeenDelegated from "next-common/components/delegation/my-delegation/beenDelegated"; +import MyDelegationLayout from "./layout"; + +export default function MyReceivedDelegations() { + return ( + +
+ +
+
+ ); +} diff --git a/packages/next/package.json b/packages/next/package.json index 9b9e9ec298..f36bd9c4ef 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@next/env": "^13.3.0", - "@osn/icons": "^1.74.0", + "@osn/icons": "^1.80.0", "@svgr/webpack": "^7.0.0", "bignumber.js": "^9.0.2", "clsx": "^1.2.1", diff --git a/packages/next/pages/account/been-delegated.jsx b/packages/next/pages/account/been-delegated.jsx new file mode 100644 index 0000000000..9db0340aa9 --- /dev/null +++ b/packages/next/pages/account/been-delegated.jsx @@ -0,0 +1,23 @@ +import { withCommonProps } from "next-common/lib"; +import AccountLayout from "next-common/components/layout/AccountLayout"; +import { fetchOpenGovTracksProps } from "next-common/services/serverSide"; +import MyReceivedDelegations from "components/myDelegation/myReceivedDelegations"; + +export default function AccountMyReceivedDelegationsPage() { + return ( + + + + ); +} + +export const getServerSideProps = withCommonProps(async () => { + const tracksProps = await fetchOpenGovTracksProps(); + + return { + props: { + summary: tracksProps.summary, + ...tracksProps, + }, + }; +}); diff --git a/packages/next/pages/account/delegations.jsx b/packages/next/pages/account/delegations.jsx new file mode 100644 index 0000000000..80ba7e261a --- /dev/null +++ b/packages/next/pages/account/delegations.jsx @@ -0,0 +1,23 @@ +import { withCommonProps } from "next-common/lib"; +import AccountLayout from "next-common/components/layout/AccountLayout"; +import { fetchOpenGovTracksProps } from "next-common/services/serverSide"; +import MyDelegations from "components/myDelegation/myDelegations"; + +export default function AccountMyDelegationsPage() { + return ( + + + + ); +} + +export const getServerSideProps = withCommonProps(async () => { + const tracksProps = await fetchOpenGovTracksProps(); + + return { + props: { + summary: tracksProps.summary, + ...tracksProps, + }, + }; +}); diff --git a/packages/next/pages/delegation/index.js b/packages/next/pages/delegation/index.js new file mode 100644 index 0000000000..5502c31a0d --- /dev/null +++ b/packages/next/pages/delegation/index.js @@ -0,0 +1,22 @@ +import { withCommonProps } from "next-common/lib"; +import { fetchOpenGovTracksProps } from "next-common/services/serverSide"; +import DelegateContainer from "next-common/components/delegation/delegate/container"; +import DelegationLayout from "next-common/components/delegation/layout"; + +export default function ReferendaPage() { + return ( + + + + ); +} + +export const getServerSideProps = withCommonProps(async () => { + const tracksProps = await fetchOpenGovTracksProps(); + + return { + props: { + ...tracksProps, + }, + }; +}); diff --git a/packages/next/pages/delegation/mine/delegations.js b/packages/next/pages/delegation/mine/delegations.js new file mode 100644 index 0000000000..b59ee9008c --- /dev/null +++ b/packages/next/pages/delegation/mine/delegations.js @@ -0,0 +1,2 @@ +export { default } from "."; +export { getServerSideProps } from "."; diff --git a/packages/next/pages/delegation/mine/index.js b/packages/next/pages/delegation/mine/index.js new file mode 100644 index 0000000000..fb21ca481f --- /dev/null +++ b/packages/next/pages/delegation/mine/index.js @@ -0,0 +1,22 @@ +import { withCommonProps } from "next-common/lib"; +import { fetchOpenGovTracksProps } from "next-common/services/serverSide"; +import MyDelegation from "next-common/components/delegation/my-delegation"; +import DelegationLayout from "next-common/components/delegation/layout"; + +export default function MyDelegationPage() { + return ( + + + + ); +} + +export const getServerSideProps = withCommonProps(async () => { + const tracksProps = await fetchOpenGovTracksProps(); + + return { + props: { + ...tracksProps, + }, + }; +}); diff --git a/packages/next/pages/delegation/mine/received.js b/packages/next/pages/delegation/mine/received.js new file mode 100644 index 0000000000..b59ee9008c --- /dev/null +++ b/packages/next/pages/delegation/mine/received.js @@ -0,0 +1,2 @@ +export { default } from "."; +export { getServerSideProps } from "."; diff --git a/packages/next/pages/delegation/statistics.js b/packages/next/pages/delegation/statistics.js new file mode 100644 index 0000000000..83c07eddbf --- /dev/null +++ b/packages/next/pages/delegation/statistics.js @@ -0,0 +1,108 @@ +import { withCommonProps } from "next-common/lib"; +import { fetchOpenGovTracksProps } from "next-common/services/serverSide"; +import DelegationLayout from "next-common/components/delegation/layout"; +import ReferendaStats from "next-common/components/delegation/stats/referendaStats"; +import DemocracyStats from "next-common/components/delegation/stats/democracyStats"; +import nextApi from "next-common/services/nextApi"; +import { EmptyList } from "next-common/utils/constants"; +import { + Democracy, + ModuleTab, + Referenda, + useModuleTab, +} from "next-common/components/profile/votingHistory/common"; +import PalletTabs from "next-common/components/profile/delegation/palletTabs"; +import getChainSettings from "next-common/utils/consts/settings"; + +function Content() { + const selectedTabId = useModuleTab(); + + if (selectedTabId === Referenda) { + return ; + } + + if (selectedTabId === Democracy) { + return ; + } + + return null; +} + +export default function DelegationStatsPage() { + return ( + + +
+ + Delegation Statistics + + +
+ +
+
+ ); +} + +export const getServerSideProps = withCommonProps(async (ctx) => { + const { type } = ctx.query; + const { hasReferenda } = getChainSettings(process.env.CHAIN); + const defaultType = hasReferenda ? Referenda : Democracy; + + const tracksProps = await fetchOpenGovTracksProps(); + + if (type === Democracy || (!type && defaultType === Democracy)) { + const [ + { result: delegatee }, + { result: delegators }, + { result: democracySummary }, + ] = await Promise.all([ + nextApi.fetch("democracy/delegatee", { + sort: JSON.stringify(["delegatedVotes", "desc"]), + pageSize: 25, + }), + nextApi.fetch("democracy/delegators", { + sort: JSON.stringify(["votes", "desc"]), + pageSize: 25, + }), + nextApi.fetch("democracy/summary"), + ]); + + return { + props: { + delegatee: delegatee ?? EmptyList, + delegators: delegators ?? EmptyList, + democracySummary: democracySummary ?? {}, + ...tracksProps, + }, + }; + } else if (type === Referenda || (!type && defaultType === Referenda)) { + const [ + { result: tracksStats }, + { result: delegatee }, + { result: tracksReferendaSummary }, + ] = await Promise.all([ + nextApi.fetch("referenda/tracks"), + nextApi.fetch("referenda/delegatee", { + sort: JSON.stringify(["votes", "desc"]), + pageSize: 25, + }), + nextApi.fetch("referenda/summary"), + ]); + + return { + props: { + tracksStats: tracksStats ?? [], + delegatee: delegatee ?? EmptyList, + tracksReferendaSummary: tracksReferendaSummary ?? [], + ...tracksProps, + }, + }; + } else { + return { + props: { + ...tracksProps, + }, + }; + } +}); diff --git a/packages/next/pages/democracy/statistics.js b/packages/next/pages/democracy/statistics.js index e5d22f289b..5fa1f279f6 100644 --- a/packages/next/pages/democracy/statistics.js +++ b/packages/next/pages/democracy/statistics.js @@ -54,7 +54,7 @@ export default function DemocracyStatisticsPage({ {hasDemocracy !== false && (
Delegation
-
+
- - + +
Delegation
-
_div]:min-w-[calc(50%-16px)] [&_>_div]:flex-1", - !navCollapsed ? "max-md:flex-col" : "max-sm:flex-col", - )} - > - - -
-
-
- +
+ +
@@ -70,9 +62,9 @@ export const getServerSideProps = withCommonProps(async () => { const [ { result: tracksStats }, { result: delegatee }, - { result: democracyReferendaSummary }, - tracksProps, + { result: tracksReferendaSummary }, { result: gov2ReferendaSummary }, + tracksProps, ] = await Promise.all([ nextApi.fetch("referenda/tracks"), nextApi.fetch("referenda/delegatee", { @@ -80,18 +72,16 @@ export const getServerSideProps = withCommonProps(async () => { pageSize: 25, }), nextApi.fetch("referenda/summary"), - - fetchOpenGovTracksProps(), nextApi.fetch(gov2ReferendumsSummaryApi), + fetchOpenGovTracksProps(), ]); return { props: { tracksStats: tracksStats ?? [], delegatee: delegatee ?? EmptyList, - democracyReferendaSummary: democracyReferendaSummary ?? [], + tracksReferendaSummary: tracksReferendaSummary ?? [], gov2ReferendaSummary: gov2ReferendaSummary ?? {}, - ...tracksProps, }, }; diff --git a/yarn.lock b/yarn.lock index 0e673534f5..ef9f236fc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2184,10 +2184,10 @@ __metadata: languageName: node linkType: hard -"@osn/icons@npm:^1.74.0": - version: 1.74.0 - resolution: "@osn/icons@npm:1.74.0" - checksum: 68741cac5a8741b49f0aa64d662d0101db05460741b3965075e78a66e1d503f8e902e3146629529e4c4c6a2a3c9be396536d0d0864de30cd8f5fcd44f5fbc946 +"@osn/icons@npm:^1.80.0": + version: 1.80.0 + resolution: "@osn/icons@npm:1.80.0" + checksum: f59a0971fd5975dd9ef17615e977b1648b75ed7cfc207149bb3ed2d2d1531dddb92d4ed822a952f1965719c4d0b176cdde9f14e0b5a5ca24e320a2aef4046d81 languageName: node linkType: hard @@ -3458,7 +3458,7 @@ __metadata: "@interlay/monetary-js": ^0.5.3 "@next/env": ^13.3.0 "@osn/eslint-config": ^1.0.2 - "@osn/icons": ^1.74.0 + "@osn/icons": ^1.80.0 "@svgr/webpack": ^7.0.0 "@types/lodash-es": ^4.17.12 bignumber.js: ^9.0.2 @@ -3492,7 +3492,7 @@ __metadata: dependencies: "@next/env": ^13.3.0 "@osn/eslint-config": ^1.0.2 - "@osn/icons": ^1.74.0 + "@osn/icons": ^1.80.0 "@svgr/webpack": ^7.0.0 "@types/lodash-es": ^4.17.12 bignumber.js: ^9.0.2 @@ -8589,7 +8589,7 @@ __metadata: "@mimirdev/apps-inject": ^0.2.0 "@mimirdev/apps-sdk": ^0.1.0 "@osn/eslint-config": ^1.0.2 - "@osn/icons": ^1.74.0 + "@osn/icons": ^1.80.0 "@osn/polkadot-react-identicon": ^1.0.8 "@osn/previewer": ^1.3.5 "@osn/provider-options": 1.1.0