diff --git a/pages/_app.tsx b/pages/_app.tsx index 3e434a2ed2..a056c3f72c 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -10,7 +10,7 @@ import { useRouter } from 'next/router'; import NextNProgress from 'nextjs-progressbar'; import { useEffect, useState } from 'react'; import AppLayout from 'src/components/AppLayout'; -import CMDK from 'src/components/CMDK'; +// import CMDK from 'src/components/CMDK'; import { antdTheme } from 'styles/antdTheme'; import { ApiContextProvider } from '~src/context/ApiContext'; @@ -93,7 +93,7 @@ function App({ Component, pageProps }: AppProps) { {showSplashScreen && }
- + {/* */} ) { const network = String(req.headers['x-network']); if (!network || !isValidNetwork(network)) return res.status(400).json({ message: 'Missing network name in request headers' }); - const { userId, postId, reaction, postType, trackNumber = null } = req.body; + const { userId, postId, reaction, postType, trackNumber } = req.body; if (!userId || isNaN(postId) || !reaction || !postType) return res.status(400).json({ message: 'Missing parameters in request body' }); const token = getTokenFromReq(req); @@ -65,10 +65,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { // delete referendum v2 redis cache if (postType == ProposalType.REFERENDUM_V2) { const trackListingKey = `${network}_${subsquidProposalType}_trackId_${trackNumber}_*`; + const referendumDetailKey = `${network}_OpenGov_${subsquidProposalType}_postId_${postId}`; + await redisDel(referendumDetailKey); await deleteKeys(trackListingKey); } } else if (postType == ProposalType.DISCUSSIONS) { const discussionListingKey = `${network}_${ProposalType.DISCUSSIONS}_page_*`; + const discussionDetailKey = `${network}_${ProposalType.DISCUSSIONS}_postId_${postId}`; + await redisDel(discussionDetailKey); await deleteKeys(discussionListingKey); } } diff --git a/pages/api/v1/auth/actions/addProfile.ts b/pages/api/v1/auth/actions/addProfile.ts index d51aa147fb..fef92f0171 100644 --- a/pages/api/v1/auth/actions/addProfile.ts +++ b/pages/api/v1/auth/actions/addProfile.ts @@ -9,6 +9,7 @@ import authServiceInstance from '~src/auth/auth'; import { ISocial, MessageType, TokenType } from '~src/auth/types'; import getTokenFromReq from '~src/auth/utils/getTokenFromReq'; import isValidEmail from '~src/auth/utils/isValidEmail'; +import isValidPassowrd from '~src/auth/utils/isValidPassowrd'; import messages from '~src/auth/utils/messages'; import nameBlacklist from '~src/auth/utils/nameBlacklist'; import firebaseAdmin from '~src/services/firebaseInit'; @@ -17,7 +18,7 @@ import apiErrorWithStatusCode from '~src/util/apiErrorWithStatusCode'; async function handler(req: NextApiRequest, res: NextApiResponse) { const firestore = firebaseAdmin.firestore(); if (req.method !== 'POST') return res.status(405).json({ message: 'Invalid request method, POST required.' }); - const { badges: badgesString, bio, image, title, social_links: socialLinksString, username, custom_username = false, email } = req.body; + const { badges: badgesString, bio, image, title, social_links: socialLinksString, username, custom_username = false, email, password } = req.body; if (!username) return res.status(400).json({ message: 'Missing parameters in request body' }); for (let i = 0; i < nameBlacklist.length; i++) { @@ -65,6 +66,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse = async (req, res) => { if (isNaN(Number(userId)) || isNaN(Number(postId))) return res.status(400).json({ message: 'Invalid parameters in request body' }); - if (discussionId < 0 || isNaN(discussionId)) return res.status(400).json({ message: messages.INVALID_DISCUSSION_ID }); - const token = getTokenFromReq(req); if (!token) return res.status(400).json({ message: 'Invalid token' }); @@ -40,20 +38,22 @@ const handler: NextApiHandler = async (req, res) => { } const current_datetime = new Date(); - const discussionDoc = postsByTypeRef(network, ProposalType.DISCUSSIONS).doc(String(discussionId)); - if (!discussionDoc) { - return res.status(400).json({ message: `No discussion post found with id ${discussionId}.` }); + if (Number(discussionId) >= 0 && !isNaN(Number(discussionId))) { + const discussionDoc = postsByTypeRef(network, ProposalType.DISCUSSIONS).doc(String(discussionId)); + + await discussionDoc.set( + { + last_edited_at: current_datetime, + post_link: { + id: Number(postId), + type: ProposalType.REFERENDUM_V2 + } + }, + { merge: true } + ); } - discussionDoc.update({ - last_edited_at: current_datetime, - post_link: { - id: Number(postId), - type: ProposalType.REFERENDUM_V2 - } - }); - const newPost: Post = { content, createdOnPolkassembly: true, diff --git a/pages/api/v1/auth/actions/createPost.ts b/pages/api/v1/auth/actions/createPost.ts index d31df93c65..9820a31fba 100644 --- a/pages/api/v1/auth/actions/createPost.ts +++ b/pages/api/v1/auth/actions/createPost.ts @@ -16,6 +16,7 @@ import messages from '~src/auth/utils/messages'; import { ProposalType } from '~src/global/proposalType'; import { firestore_db } from '~src/services/firebaseInit'; import { IPostTag, Post } from '~src/types'; +import getSubstrateAddress from '~src/util/getSubstrateAddress'; import isContentBlacklisted from '~src/util/isContentBlacklisted'; async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -24,7 +25,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') return res.status(405).json({ message: 'Invalid request method, POST required.' }); @@ -18,7 +20,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const network = String(req.headers['x-network']); if (!network || !isValidNetwork(network)) return res.status(400).json({ message: 'Missing network name in request headers' }); - const { userId, postId, reaction, postType } = req.body; + const { userId, postId, reaction, postType, trackNumber } = req.body; if (!userId || isNaN(postId) || !reaction || !postType) return res.status(400).json({ message: 'Missing parameters in request body' }); const token = getTokenFromReq(req); @@ -30,6 +32,25 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const postRef = postsByTypeRef(network, postType).doc(String(postId)); const userReactionsSnapshot = await postRef.collection('post_reactions').where('user_id', '==', user.id).limit(1).get(); + const subsquidProposalType = getSubsquidLikeProposalType(postType); + + if (process.env.IS_CACHING_ALLOWED == '1') { + if (!isNaN(trackNumber)) { + // delete referendum v2 redis cache + if (postType == ProposalType.REFERENDUM_V2) { + const trackListingKey = `${network}_${subsquidProposalType}_trackId_${trackNumber}_*`; + const referendumDetailKey = `${network}_OpenGov_${subsquidProposalType}_postId_${postId}`; + await redisDel(referendumDetailKey); + await deleteKeys(trackListingKey); + } + } else if (postType == ProposalType.DISCUSSIONS) { + const discussionListingKey = `${network}_${ProposalType.DISCUSSIONS}_page_*`; + const discussionDetailKey = `${network}_${ProposalType.DISCUSSIONS}_postId_${postId}`; + await redisDel(discussionDetailKey); + await deleteKeys(discussionListingKey); + } + } + if (!userReactionsSnapshot.empty) { const reactionDocRef = userReactionsSnapshot.docs[0].ref; await reactionDocRef diff --git a/pages/api/v1/verification/index.ts b/pages/api/v1/verification/index.ts index fe662b0d72..e7e2fa79a0 100644 --- a/pages/api/v1/verification/index.ts +++ b/pages/api/v1/verification/index.ts @@ -115,7 +115,7 @@ const handler: NextApiHandler = async (req, const twitterData = twitterVerificationDoc.data(); - if (twitterData?.twitter_handle !== account) return res.status(400).json({ message: 'Twitter handle does not match' }); + if (`${twitterData?.twitter_handle}`.toLowerCase() !== `${account}`.toLowerCase()) return res.status(400).json({ message: 'Twitter handle does not match' }); if (twitterData?.verified && twitterData?.user_id === userId) { return res.status(200).json({ message: VerificationStatus.ALREADY_VERIFIED }); @@ -124,7 +124,7 @@ const handler: NextApiHandler = async (req, } else { await twitterVerificationDoc.ref.set({ created_at: new Date(), - twitter_handle: account, + twitter_handle: twitterData?.twitter_handle, user_id: userId, verified: false }); diff --git a/pages/api/v1/verification/twitter-callback.ts b/pages/api/v1/verification/twitter-callback.ts index 7a3e23e3b2..c74ae91440 100644 --- a/pages/api/v1/verification/twitter-callback.ts +++ b/pages/api/v1/verification/twitter-callback.ts @@ -80,8 +80,7 @@ export const getTwitterCallback = async ({ network, oauthVerifier, oauthRequestT oauthAccessToken, oauthAccessTokenSecret }); - - if (twitterDocData?.twitter_handle !== twitterUser?.screen_name) throw apiErrorWithStatusCode('Twitter handle does not match', 400); + if (twitterDocData?.twitter_handle.toLowerCase() !== twitterUser?.screen_name.toLowerCase()) throw apiErrorWithStatusCode('Twitter handle does not match', 400); await twitterDoc.ref.set({ ...twitterDocData, diff --git a/pages/api/v1/votes/ayeNayTotalCount.ts b/pages/api/v1/votes/ayeNayTotalCount.ts new file mode 100644 index 0000000000..272c6ad352 --- /dev/null +++ b/pages/api/v1/votes/ayeNayTotalCount.ts @@ -0,0 +1,39 @@ +// Copyright 2019-2025 @polkassembly/polkassembly authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import type { NextApiHandler } from 'next'; +import withErrorHandling from '~src/api-middlewares/withErrorHandling'; +import { MessageType } from '~src/auth/types'; +import messages from '~src/auth/utils/messages'; +import { isProposalTypeValid, isValidNetwork } from '~src/api-utils'; +import fetchSubsquid from '~src/util/fetchSubsquid'; +import { GET_AYE_NAY_TOTAL_COUNT } from '~src/queries'; + +interface Props { + proposalType: string; + postId: number; +} + +const handler: NextApiHandler = async (req, res) => { + const { proposalType, postId } = req.body as unknown as Props; + + const network = String(req.headers['x-network']); + if (network === 'undefined' || !isValidNetwork(network)) return res.status(400).json({ message: messages.INVALID_NETWORK }); + if (isNaN(Number(postId)) || isProposalTypeValid(proposalType)) return res.status(400).json({ message: messages.INVALID_PARAMS }); + const query = GET_AYE_NAY_TOTAL_COUNT; + + const variables: any = { + proposalIndex_eq: postId, + type_eq: proposalType + }; + + const data = await fetchSubsquid({ + network, + query, + variables + }); + res.json(data['data']); +}; + +export default withErrorHandling(handler); diff --git a/pages/delegation/[track].tsx b/pages/delegation/[track].tsx index 479be5fd44..2e9b996408 100644 --- a/pages/delegation/[track].tsx +++ b/pages/delegation/[track].tsx @@ -17,6 +17,7 @@ import { setNetwork } from '~src/redux/network'; import { ErrorState } from '~src/ui-components/UIStates'; import checkRouteNetworkWithRedirect from '~src/util/checkRouteNetworkWithRedirect'; import getQueryToTrack from '~src/util/getQueryToTrack'; +import { useTheme } from 'next-themes'; export const getServerSideProps: GetServerSideProps = async ({ req, query }) => { const network = getNetworkFromReqHeaders(req.headers); @@ -57,6 +58,7 @@ interface ITrackProps { const DashboardTracks: FC = (props) => { const { data, error, trackDetails } = props; const dispatch = useDispatch(); + const { resolvedTheme: theme } = useTheme(); useEffect(() => { dispatch(setNetwork(props.network)); @@ -76,6 +78,7 @@ const DashboardTracks: FC = (props) => { ); diff --git a/pages/delegation/index.tsx b/pages/delegation/index.tsx index a184f0bf60..d76f82c4e2 100644 --- a/pages/delegation/index.tsx +++ b/pages/delegation/index.tsx @@ -15,6 +15,7 @@ import { useRouter } from 'next/router'; import checkRouteNetworkWithRedirect from '~src/util/checkRouteNetworkWithRedirect'; import { useDispatch } from 'react-redux'; import { setNetwork } from '~src/redux/network'; +import { delegationSupportedNetworks } from '~src/components/DelegationDashboard'; export const getServerSideProps: GetServerSideProps = async ({ req }) => { const network = getNetworkFromReqHeaders(req.headers); @@ -22,7 +23,7 @@ export const getServerSideProps: GetServerSideProps = async ({ req }) => { const networkRedirect = checkRouteNetworkWithRedirect(network); if (networkRedirect) return networkRedirect; - if (!['kusama', 'polkadot'].includes(network)) { + if (!delegationSupportedNetworks.includes(network)) { return { props: {}, redirect: { diff --git a/pages/user/[username].tsx b/pages/user/[username].tsx index a030a251b7..c84236aa93 100644 --- a/pages/user/[username].tsx +++ b/pages/user/[username].tsx @@ -180,6 +180,12 @@ const UserProfile: FC = (props) => { }; }); + const sortTabItemsByCount = (tabItems: any) => { + return tabItems.sort((a: any, b: any) => b?.label?.props?.count - a?.label?.props?.count); + }; + + const sortedTabItems = sortTabItemsByCount(tabItems); + return ( <> = (props) => { theme={theme} className='ant-tabs-tab-bg-white font-medium text-sidebarBlue dark:bg-section-dark-overlay' type='card' - items={tabItems as any} + items={sortedTabItems as any} /> )} diff --git a/public/assets/delegate-icon.svg b/public/assets/delegate-icon.svg new file mode 100644 index 0000000000..fdc86184a9 --- /dev/null +++ b/public/assets/delegate-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/Beneficiary.svg b/public/assets/icons/Beneficiary.svg new file mode 100644 index 0000000000..bd2b234b41 --- /dev/null +++ b/public/assets/icons/Beneficiary.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/BeneficiaryGrey.svg b/public/assets/icons/BeneficiaryGrey.svg new file mode 100644 index 0000000000..f28e424010 --- /dev/null +++ b/public/assets/icons/BeneficiaryGrey.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/Discussion-Unselected-white.svg b/public/assets/icons/Discussion-Unselected-white.svg new file mode 100644 index 0000000000..8fda44896d --- /dev/null +++ b/public/assets/icons/Discussion-Unselected-white.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/icons/Discussion-Unselected.svg b/public/assets/icons/Discussion-Unselected.svg new file mode 100644 index 0000000000..f01a042649 --- /dev/null +++ b/public/assets/icons/Discussion-Unselected.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/icons/arrow-white.svg b/public/assets/icons/arrow-white.svg new file mode 100644 index 0000000000..075b343e7a --- /dev/null +++ b/public/assets/icons/arrow-white.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/polkasafe-white-logo.svg b/public/assets/icons/polkasafe-white-logo.svg new file mode 100644 index 0000000000..4668a8df34 --- /dev/null +++ b/public/assets/icons/polkasafe-white-logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/icons/shield-user-icon.svg b/public/assets/icons/shield-user-icon.svg new file mode 100644 index 0000000000..90ae60f4a2 --- /dev/null +++ b/public/assets/icons/shield-user-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/tags-white-icon.svg b/public/assets/icons/tags-white-icon.svg new file mode 100644 index 0000000000..4ffbdcd241 --- /dev/null +++ b/public/assets/icons/tags-white-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/stake-icon.svg b/public/assets/stake-icon.svg new file mode 100644 index 0000000000..5a7074dcd1 --- /dev/null +++ b/public/assets/stake-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/auth/auth.ts b/src/auth/auth.ts index b8cf70e782..ac4d4b8f75 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -853,6 +853,18 @@ class AuthService { await redisDel(key); } + public async ResetPasswordFromAuth(token: string, newPassword: string): Promise { + const userId = getUserIdFromJWT(token, jwtPublicKey); + const { password, salt } = await this.getSaltAndHashedPassword(newPassword); + + await firebaseAdmin.firestore().collection('users').doc(String(userId)).update({ + password, + salt + }); + const user = await getUserFromUserId(userId); + return this.getSignedToken(user); + } + public async UndoEmailChange(token: string): Promise<{ email: string; updatedToken: string }> { const firestore = firebaseAdmin.firestore(); diff --git a/src/components/AppLayout/Footer.tsx b/src/components/AppLayout/Footer.tsx index 6217db4cf7..3f16303ab7 100644 --- a/src/components/AppLayout/Footer.tsx +++ b/src/components/AppLayout/Footer.tsx @@ -118,7 +118,7 @@ const Footer = ({ className }: { className?: string }) => {
diff --git a/src/components/AppLayout/NavHeader.tsx b/src/components/AppLayout/NavHeader.tsx index b2d07de5ad..23b88e404c 100644 --- a/src/components/AppLayout/NavHeader.tsx +++ b/src/components/AppLayout/NavHeader.tsx @@ -19,7 +19,9 @@ import { chainProperties } from '~src/global/networkConstants'; import SearchBar from '~src/ui-components/SearchBar'; import TownHall from '~assets/icons/TownHall.svg'; import Mail from '~assets/icons/mail.svg'; +import MailWhite from '~assets/icons/mailIconWhite.svg'; import Arrow from '~assets/icons/arrow.svg'; +import ArrowWhite from '~assets/icons/arrow-white.svg'; import PolkaSafe from '~assets/icons/PolkaSafe.svg'; import PaLogo from './PaLogo'; import PaLogoDark from '~assets/PALogoDark.svg'; @@ -37,8 +39,11 @@ import { useNetworkSelector, useUserDetailsSelector } from '~src/redux/selectors import { useDispatch } from 'react-redux'; import { logout, setUserDetailsState } from '~src/redux/userDetails'; import { useTheme } from 'next-themes'; -import PolkasafeWhiteIcon from '~assets/polkasafe-white-logo.svg'; +import PolkasafeWhiteIcon from '~assets/icons/polkasafe-white-logo.svg'; import { trackEvent } from 'analytics'; +import StakeIcon from '~assets/stake-icon.svg'; +import DelegateIcon from '~assets/delegate-icon.svg'; +import { delegationSupportedNetworks } from '../DelegationDashboard'; const RPCDropdown = dynamic(() => import('~src/ui-components/RPCDropdown'), { loading: () => , @@ -117,9 +122,9 @@ const NavHeader = ({ className, sidedrawer, setSidedrawer, displayName, isVerifi href='https://townhallgov.com/' target='_blank' rel='noreferrer' - className='custom-link' + className='custom-link after:hidden' > - + TownHall @@ -134,23 +139,60 @@ const NavHeader = ({ className, sidedrawer, setSidedrawer, displayName, isVerifi href='https://polkasafe.xyz/' target='_blank' rel='noreferrer' - className='custom-link' + className='custom-link after:hidden' > - - {theme === 'dark' ? : } + + {theme === 'dark' ? : } Polkasafe ) + }, + { + className: 'logo-class', + key: 'Staking', + label: ( + + + + Staking + + + ) } ]; + if (delegationSupportedNetworks?.includes(network)) { + menudropDownItems.push({ + className: 'logo-class', + key: 'Delegation', + label: ( + + + + Delegation + + + ) + }); + } + const dropdownMenuItems: ItemType[] = [ { key: 'view profile', label: ( @@ -162,7 +204,7 @@ const NavHeader = ({ className, sidedrawer, setSidedrawer, displayName, isVerifi key: 'settings', label: ( @@ -175,7 +217,7 @@ const NavHeader = ({ className, sidedrawer, setSidedrawer, displayName, isVerifi label: ( { e.preventDefault(); e.stopPropagation(); @@ -195,7 +237,7 @@ const NavHeader = ({ className, sidedrawer, setSidedrawer, displayName, isVerifi key: 'set on-chain identity', label: ( { e.stopPropagation(); @@ -305,11 +347,11 @@ const NavHeader = ({ className, sidedrawer, setSidedrawer, displayName, isVerifi ) : ( {!web3signup ? ( -
- +
+ {theme === 'dark' ? : }
- {displayName || username || ''} - + {displayName || username || ''} + {theme === 'dark' ? : }
) : ( diff --git a/src/components/AppLayout/index.tsx b/src/components/AppLayout/index.tsx index 33a56f790b..49ef36ecd7 100644 --- a/src/components/AppLayout/index.tsx +++ b/src/components/AppLayout/index.tsx @@ -71,6 +71,7 @@ import { useTheme } from 'next-themes'; import { Dropdown } from '~src/ui-components/Dropdown'; import ToggleButton from '~src/ui-components/ToggleButton'; import BigToggleButton from '~src/ui-components/ToggleButton/BigToggleButton'; +import SetIdentityNudge from '~src/ui-components/SetIdentityNudge'; const OnChainIdentity = dynamic(() => import('~src/components/OnchainIdentity'), { ssr: false @@ -249,7 +250,7 @@ interface Props { const AppLayout = ({ className, Component, pageProps }: Props) => { const { network } = useNetworkSelector(); const { api, apiReady } = useApiContext(); - const { username, picture, loginAddress } = useUserDetailsSelector(); + const { username, picture, loginAddress, id: userId } = useUserDetailsSelector(); const [sidedrawer, setSidedrawer] = useState(false); const router = useRouter(); const [previousRoute, setPreviousRoute] = useState(router.asPath); @@ -259,6 +260,7 @@ const AppLayout = ({ className, Component, pageProps }: Props) => { const [openAddressLinkedModal, setOpenAddressLinkedModal] = useState(false); const { resolvedTheme: theme } = useTheme(); const [isIdentityUnverified, setIsIdentityUnverified] = useState(true); + const [isIdentitySet, setIsIdentitySet] = useState(false); const [isGood, setIsGood] = useState(false); const [mainDisplay, setMainDisplay] = useState(''); const dispatch = useDispatch(); @@ -318,6 +320,7 @@ const AppLayout = ({ className, Component, pageProps }: Props) => { const judgementProvided = infoCall?.some(([, judgement]): boolean => judgement.isFeePaid); const isGood = info.identity?.judgements.some(([, judgement]): boolean => judgement.isKnownGood || judgement.isReasonable); setIsGood(Boolean(isGood)); + setIsIdentitySet(!!(info.identity.display && !info?.identity?.judgements?.length)); setIsIdentityUnverified(judgementProvided || !info?.identity?.judgements?.length); }) .then((unsub) => { @@ -671,7 +674,9 @@ const AppLayout = ({ className, Component, pageProps }: Props) => { const handleMenuClick = (menuItem: any) => { if (['userMenu', 'tracksHeading', 'pipsHeading'].includes(menuItem.key)) return; router.push(menuItem.key); - setSidedrawer(false); + { + isMobile && setSidedrawer(false); + } }; const handleLogout = async (username: string) => { dispatch(logout()); @@ -740,6 +745,12 @@ const AppLayout = ({ className, Component, pageProps }: Props) => { displayName={mainDisplay} isVerified={isGood && !isIdentityUnverified} /> + {!!userId && isIdentityUnverified && onchainIdentitySupportedNetwork.includes(network) && ( + + )} { left: 0 }} contentWrapperStyle={{ position: 'fixed', height: '100vh', bottom: 0, left: 0 }} - footer={} + // footer={} > -
+
setSidedrawer(false)} + > { items={sidebarItems} onClick={handleMenuClick} className={`${username ? 'auth-sider-menu' : ''} dark:bg-section-dark-overlay`} - onMouseLeave={() => setSidedrawer(false)} /> +
{[AllNetworks.MOONBEAM, AllNetworks.MOONRIVER].includes(network) && ['/', 'opengov', '/gov-2'].includes(router.asPath) ? ( diff --git a/src/components/DelegationDashboard/Coloumn.tsx b/src/components/DelegationDashboard/Coloumn.tsx index 131de72d88..e64efd618a 100644 --- a/src/components/DelegationDashboard/Coloumn.tsx +++ b/src/components/DelegationDashboard/Coloumn.tsx @@ -199,9 +199,11 @@ const GetColumns = (status: ETrackDelegationStatus) => { {status.map((item: ETrackDelegationStatus, index: number) => (

1 && 'w-[95px] truncate'} `} + className={`text-xs ${item === ETrackDelegationStatus.Received_Delegation && 'bg-[#E7DCFF] dark:bg-[#6C2CF8]'} ${ + item === ETrackDelegationStatus.Delegated && 'bg-[#FFFBD8] dark:bg-[#69600B]' + } ${item === ETrackDelegationStatus.Undelegated && 'bg-[#FFDAD8] dark:bg-[#EF6158]'} rounded-[26px] px-[12px] py-[6px] text-center ${ + item === ETrackDelegationStatus.Received_Delegation && status.length > 1 && 'w-[95px] truncate' + } `} > {item?.split('_').join(' ').charAt(0).toUpperCase() + item?.split('_').join(' ').slice(1)}

@@ -458,10 +460,10 @@ const GetTracksColumns = (status: ETrackDelegationStatus, setOpen: (pre: boolean
), diff --git a/src/components/DelegationDashboard/DashboardTrack.tsx b/src/components/DelegationDashboard/DashboardTrack.tsx index 8174510794..09bcb0c72b 100644 --- a/src/components/DelegationDashboard/DashboardTrack.tsx +++ b/src/components/DelegationDashboard/DashboardTrack.tsx @@ -61,6 +61,7 @@ export interface ITrackRowData { balance: string; delegatedOn: Date; action: string; + theme?: string; } export const handleTrack = (track: string) => { @@ -212,9 +213,9 @@ const DashboardTrackListing = ({ className, posts, trackDetails }: Props) => { status.map((item: ETrackDelegationStatus, index: number) => ( {item?.split('_').join(' ').charAt(0).toUpperCase() + item?.split('_').join(' ').slice(1)} @@ -359,4 +360,28 @@ export default styled(DashboardTrackListing)` font-weight: 600px; line-height: 21px; } + .column .ant-table-thead > tr > th { + color: ${(props) => (props.theme === 'dark' ? '#909090' : '#485F7D')} !important; + font-size: 14px; + font-weight: ${(props) => (props.theme === 'dark' ? '500' : '600')} !important; + line-height: 21px; + white-space: nowrap; + border-bottom: ${(props) => (props.theme === 'dark' ? '1px solid #4B4B4B' : '')} !important; + } + .column .ant-table-thead > tr > th:nth-child(1) { + text-align: center; + } + .ant-table-cell { + background: ${(props) => (props.theme === 'dark' ? '#0D0D0D' : '')} !important; + border-bottom: ${(props) => (props.theme === 'dark' ? '1px solid #4B4B4B' : '')} !important; + } + .ant-table-wrapper .ant-table-thead > tr > th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before, + .ant-table-wrapper .ant-table-thead > tr > td:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before { + background-color: transparent !important; + } + @media only screen and (max-width: 1024px) { + .column .ant-table-thead > tr > th:nth-child(2) { + text-align: center; + } + } `; diff --git a/src/components/DelegationDashboard/Delegate.tsx b/src/components/DelegationDashboard/Delegate.tsx index 01588f2a3f..e8f63437e1 100644 --- a/src/components/DelegationDashboard/Delegate.tsx +++ b/src/components/DelegationDashboard/Delegate.tsx @@ -102,9 +102,9 @@ const Delegate = ({ className, trackDetails, disabled }: Props) => {
{disabled && ( You have already delegated for this track.} /> )}

@@ -130,7 +130,9 @@ const Delegate = ({ className, trackDetails, disabled }: Props) => { getEncodedAddress(address, network) === delegationDashboardAddress || disabled } - className={`ml-1 mr-1 flex h-[40px] items-center justify-around gap-2 rounded-md bg-pink_primary px-4 py-1 ${disabled && 'opacity-50'}`} + className={`ml-1 mr-1 flex h-[40px] items-center justify-around gap-2 rounded-md bg-pink_primary px-4 py-1 dark:border-pink_primary dark:bg-[#33071E] ${ + disabled && 'opacity-50' + }`} > Delegate @@ -146,9 +148,10 @@ const Delegate = ({ className, trackDetails, disabled }: Props) => { (!(getEncodedAddress(address, network) || Web3.utils.isAddress(address)) && )} {addressAlert && ( The substrate address has been changed to Kusama address.} /> )} diff --git a/src/components/DelegationDashboard/DelegateCard.tsx b/src/components/DelegationDashboard/DelegateCard.tsx index 50767fe2ce..9ff155a339 100644 --- a/src/components/DelegationDashboard/DelegateCard.tsx +++ b/src/components/DelegationDashboard/DelegateCard.tsx @@ -103,6 +103,7 @@ const DelegateCard = ({ delegate, className, trackNum, disabled }: Props) => {
import('~src/ui-components/AddressConnectModal'), { loading: () => , ssr: false diff --git a/src/components/ExtensionNotDetected.tsx b/src/components/ExtensionNotDetected.tsx index db4e48e43c..b13abf58e0 100644 --- a/src/components/ExtensionNotDetected.tsx +++ b/src/components/ExtensionNotDetected.tsx @@ -15,23 +15,25 @@ const ExtensionNotDetected: React.FC = ({ chosenWallet }) => { return ( +
{chosenWallet === Wallet.SUBWALLET ? chosenWallet.split('-')[0] : chosenWallet || 'Wallet'} extension not detected.
} description={ getExtensionUrl() ? ( -
No web 3 account integration could be found. To be able to vote on-chain, visit this page on a computer with polkadot-js extension.
+
+ No web 3 account integration could be found. To be able to vote on-chain, visit this page on a computer with polkadot-js extension. +
) : ( -
+
Please install Firefox or Chrome browser to use this feature.
) } type='info' showIcon - className='changeColor text-blue-light-high' + className='changeColor dark:border-infoAlertBorderDark dark:bg-infoAlertBgDark' /> ); }; diff --git a/src/components/GovernanceCard.tsx b/src/components/GovernanceCard.tsx index 5aede2f81a..f0b30e29ea 100644 --- a/src/components/GovernanceCard.tsx +++ b/src/components/GovernanceCard.tsx @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ClockCircleOutlined, DislikeOutlined, LikeOutlined, PaperClipOutlined } from '@ant-design/icons'; -import { Divider, Modal, Progress, Skeleton, Tooltip } from 'antd'; +import { Divider, Progress, Skeleton, Tooltip } from 'antd'; import dynamic from 'next/dynamic'; import Link from 'next/link'; import { poppins } from 'pages/_app'; @@ -18,7 +18,6 @@ import TopicTag from '~src/ui-components/TopicTag'; import BN from 'bn.js'; import { chainProperties } from 'src/global/networkConstants'; import { CommentsIcon } from '~src/ui-components/CustomIcons'; -import TagsIcon from '~assets/icons/tags-icon.svg'; import { getFormattedLike } from '~src/util/getFormattedLike'; import { useApiContext } from '~src/context'; import { useRouter } from 'next/router'; @@ -28,7 +27,6 @@ import styled from 'styled-components'; import { getStatusBlock } from '~src/util/getStatusBlock'; import { IPeriod } from '~src/types'; import { getPeriodData } from '~src/util/getPeriodData'; -import { CloseIcon } from '~src/ui-components/CustomIcons'; import { ProposalType } from '~src/global/proposalType'; import { getTrackNameFromId } from '~src/util/trackNameFromId'; import { useNetworkSelector, useUserDetailsSelector } from '~src/redux/selectors'; @@ -36,6 +34,7 @@ import { useTheme } from 'next-themes'; import { getTrackData } from './Listing/Tracks/AboutTrackCard'; import { isOpenGovSupported } from '~src/global/openGovNetworks'; import Markdown from '~src/ui-components/Markdown'; +import TagsModal from '~src/ui-components/TagsModal'; const BlockCountdown = dynamic(() => import('src/components/BlockCountdown'), { loading: () => , @@ -114,7 +113,7 @@ const GovernanceCard: FC = (props) => { proposalType, votesData, identityId = null, - truncateUsername = true, + truncateUsername = false, showSimilarPost, description } = props; @@ -248,11 +247,11 @@ const GovernanceCard: FC = (props) => {
{requestedAmount > 100 ? ( - {requestedAmountFormatted} {chainProperties[network]?.tokenSymbol} + {Number(requestedAmountFormatted).toLocaleString()} {chainProperties[network]?.tokenSymbol} ) : ( - {requestedAmountFormatted} {chainProperties[network]?.tokenSymbol} + {Number(requestedAmountFormatted).toLocaleString()} {chainProperties[network]?.tokenSymbol} )}
@@ -612,45 +611,12 @@ const GovernanceCard: FC = (props) => {

- { - e.stopPropagation(); - e.preventDefault(); - setTagsModal(false); - }} - footer={false} - closeIcon={} - className={`${poppins.variable} ${poppins.className} ant-modal-content>.ant-modal-header]:bg-section-dark-overlay h-[120px] max-w-full shrink-0 max-sm:w-[100%]`} - title={ - <> - - - - } - > -
- {tags && tags.length > 0 && ( - <> - {tags?.map((tag, index) => ( -
- {tag} -
- ))} - - )} -
-
+ ); }; diff --git a/src/components/Home/AboutNetwork.tsx b/src/components/Home/AboutNetwork.tsx index 92ad01bea9..911a678b82 100644 --- a/src/components/Home/AboutNetwork.tsx +++ b/src/components/Home/AboutNetwork.tsx @@ -4,7 +4,7 @@ import { DesktopOutlined, FileTextOutlined, HomeFilled, PlayCircleFilled, TwitterOutlined, YoutubeFilled } from '@ant-design/icons'; import { Space } from 'antd'; -import React from 'react'; +import React, { useState } from 'react'; import { CubeIcon, DiscordIcon, GithubIcon, RedditIcon, TelegramIcon } from 'src/ui-components/CustomIcons'; import styled from 'styled-components'; @@ -122,6 +122,7 @@ const gov2Link = ({ className, bgImage, icon, link, text, subText }: { className ); const AboutNetwork = ({ className, networkSocialsData, showGov2Links }: { className?: string; networkSocialsData: NetworkSocials | null; showGov2Links?: boolean }) => { + const [showGallery, setShowGallery] = useState(false); return (
@@ -129,38 +130,60 @@ const AboutNetwork = ({ className, networkSocialsData, showGov2Links }: { classN
{networkSocialsData && socialLinks(networkSocialsData)}
-

Join our Community to discuss, contribute and get regular updates from us!

+

+ Join our Community to discuss, contribute and get regular updates from us! + {showGallery && ( + setShowGallery(false)} + > + Minimize Gallery + + )} + {!showGallery && ( + setShowGallery(true)} + > + View Gallery + + )} +

{networkSocialsData && socialLinks(networkSocialsData)}
- {showGov2Links && ( -
- {gov2Link({ - bgImage: '/assets/gavin-keynote.png', - className: 'mr-12 lg:mr-9', - icon: , - link: 'https://www.youtube.com/watch?v=FhC10CCw9Qg', - subText: '1:40 hours', - text: "Gavin's keynote @Decoded 2023" - })} + {showGallery && ( +
+ {showGov2Links && ( +
+ {gov2Link({ + bgImage: '/assets/gavin-keynote.png', + className: 'mr-12 lg:mr-9', + icon: , + link: 'https://www.youtube.com/watch?v=FhC10CCw9Qg', + subText: '1:40 hours', + text: "Gavin's keynote @Decoded 2023" + })} - {gov2Link({ - bgImage: '/assets/gov2-info-bg-2.png', - className: 'mr-12 lg:mr-9', - icon: , - link: 'https://medium.com/polkadot-network/gov2-polkadots-next-generation-of-decentralised-governance-4d9ef657d11b', - subText: '17 min read', - text: "Gavin's blog on Medium" - })} + {gov2Link({ + bgImage: '/assets/gov2-info-bg-2.png', + className: 'mr-12 lg:mr-9', + icon: , + link: 'https://medium.com/polkadot-network/gov2-polkadots-next-generation-of-decentralised-governance-4d9ef657d11b', + subText: '17 min read', + text: "Gavin's blog on Medium" + })} - {gov2Link({ - bgImage: '/assets/gov2-info-bg-3.png', - className: 'mr-12 lg:mr-0', - icon: , - link: 'https://docs.polkassembly.io', - subText: 'Wiki', - text: 'Polkassembly user guide' - })} + {gov2Link({ + bgImage: '/assets/gov2-info-bg-3.png', + className: 'mr-12 lg:mr-0', + icon: , + link: 'https://docs.polkassembly.io', + subText: 'Wiki', + text: 'Polkassembly user guide' + })} +
+ )}
)}
diff --git a/src/components/Home/News.tsx b/src/components/Home/News.tsx index 99358dd8e8..e2c08678b3 100644 --- a/src/components/Home/News.tsx +++ b/src/components/Home/News.tsx @@ -3,8 +3,9 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { useTheme } from 'next-themes'; -import React, { FC } from 'react'; +import React, { FC, useEffect, useState } from 'react'; import { TwitterTimelineEmbed } from 'react-twitter-embed'; +import Loader from '~src/ui-components/Loader'; interface INewsProps { twitter: string; @@ -13,15 +14,32 @@ interface INewsProps { const News: FC = (props) => { const { twitter } = props; const { resolvedTheme: theme } = useTheme(); + const [isLoading, setIsLoading] = useState(false); + const [prevTheme, setPrevTheme] = useState(theme); + let profile = 'polkadot'; if (twitter) { profile = twitter.split('/')[3]; } + + useEffect(() => { + setPrevTheme(theme); + }, [theme]); + + useEffect(() => { + if (prevTheme !== theme) { + setIsLoading(true); + } + }, [prevTheme, theme]); + return (

News

+ {isLoading && } setIsLoading(false)} sourceType='profile' screenName={profile} options={{ height: 450 }} diff --git a/src/components/Listing/OffChain/OffChainCard.tsx b/src/components/Listing/OffChain/OffChainCard.tsx index e6eee5d0d7..3b96b6e698 100644 --- a/src/components/Listing/OffChain/OffChainCard.tsx +++ b/src/components/Listing/OffChain/OffChainCard.tsx @@ -3,17 +3,16 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ClockCircleOutlined, DislikeOutlined, LikeOutlined } from '@ant-design/icons'; -import { Divider, Modal, Tooltip } from 'antd'; -import { poppins } from 'pages/_app'; +import { Divider, Tooltip } from 'antd'; import React, { FC, useState } from 'react'; import getRelativeCreatedAt from 'src/util/getRelativeCreatedAt'; -import { CloseIcon, CommentsIcon, WarningMessageIcon } from '~src/ui-components/CustomIcons'; -import TagsIcon from '~assets/icons/tags-icon.svg'; +import { CommentsIcon, WarningMessageIcon } from '~src/ui-components/CustomIcons'; import OnchainCreationLabel from '~src/ui-components/OnchainCreationLabel'; import { getFormattedLike } from '~src/util/getFormattedLike'; import TopicTag from '~src/ui-components/TopicTag'; import { useUserDetailsSelector } from '~src/redux/selectors'; import { useTheme } from 'next-themes'; +import TagsModal from '~src/ui-components/TagsModal'; export interface IDiscussionProps { created_at: Date; @@ -148,40 +147,11 @@ const DiscussionCard: FC = (props) => {
- { - e.stopPropagation(); - e.preventDefault(); - setTagsModal(false); - }} - closeIcon={} - footer={false} - className={`${poppins.variable} ${poppins.className} h-[120px] max-w-full shrink-0 max-sm:w-[100%] dark:[&>.ant-modal-content]:bg-section-dark-overlay`} - > -
-

- - Tags -

-
-
-
- {tags && tags.length > 0 && ( - <> - {tags?.map((tag, index) => ( -
- {tag} -
- ))} - - )} -
- +
= (props) => {
- { - e.stopPropagation(); - e.preventDefault(); - setTagsModal(false); - }} - closeIcon={} - footer={false} - className={`${poppins.variable} ${poppins.className} h-[120px] max-w-full shrink-0 max-sm:w-[100%] dark:[&>.ant-modal-content]:bg-section-dark-overlay`} - > -
- -

Tags

-
-
-
- {tags && tags.length > 0 && ( - <> - {tags?.map((tag, index) => ( -
- {tag} -
- ))} - - )} -
- +
); diff --git a/src/components/Listing/Tracks/AboutTrackCard.tsx b/src/components/Listing/Tracks/AboutTrackCard.tsx index ae15cb1038..341410e7a0 100644 --- a/src/components/Listing/Tracks/AboutTrackCard.tsx +++ b/src/components/Listing/Tracks/AboutTrackCard.tsx @@ -18,6 +18,10 @@ import { getTrackFunctions } from '../../Post/GovernanceSideBar/Referenda/util'; import blockToTime from '~src/util/blockToTime'; import dynamic from 'next/dynamic'; import { useNetworkSelector } from '~src/redux/selectors'; +import DiscussionIconGrey from '~assets/icons/Discussion-Unselected.svg'; +import DiscussionIconWhite from '~assets/icons/Discussion-Unselected-white.svg'; +import { useTheme } from 'next-themes'; +import styled from 'styled-components'; const Curves = dynamic(() => import('./Curves'), { loading: () => , @@ -76,6 +80,7 @@ function addTrackGroup(arr: any) { export const getTrackData = (network: string, trackName?: string, trackNumber?: number) => { const defaultTrackMetaData = getDefaultTrackMetaData(); + if (!network) return defaultTrackMetaData; let trackMetaData: TrackProps | undefined = undefined; if (trackName) { @@ -132,6 +137,7 @@ export const blocksToRelevantTime = (network: string, blocks: number): string => const AboutTrackCard: FC = (props) => { const { network } = useNetworkSelector(); + const { resolvedTheme: theme } = useTheme(); const { className, trackName } = props; const [trackMetaData, setTrackMetaData] = useState(getDefaultTrackMetaData()); useEffect(() => { @@ -142,6 +148,7 @@ const AboutTrackCard: FC = (props) => { labels: [] }); const [curvesLoading, setCurvesLoading] = useState(true); + const [showDetails, setShowDetails] = useState(false); //get the track number of the track const track_number = trackMetaData?.trackId; const { api, apiReady } = useApiContext(); @@ -223,102 +230,138 @@ const AboutTrackCard: FC = (props) => { }, [api, apiReady, network, track_number]); return ( -
-
+
+
+ {theme === 'dark' ? : }

About {trackName.split(/(?=[A-Z])/).join(' ')}

-

#{trackMetaData.trackId}

+

(#{trackMetaData.trackId})

{!['moonbeam', 'moonbase', 'moonriver'].includes(network) && }
+
+
+

+ {trackMetaData?.description} + {showDetails && ( + setShowDetails(false)} + > + Hide Track details + + )} + {!showDetails && ( + setShowDetails(true)} + > + Show Track details + + )} +

+
-

{trackMetaData?.description}

+ {showDetails && ( +
+
+
+
+ Max Deciding + {trackMetaData.maxDeciding} +
+
-
-
-
-
- Max Deciding - {trackMetaData.maxDeciding} -
-
+
+
+ Confirm Period + + {blocksToRelevantTime(network, Number(trackMetaData.confirmPeriod))} + +
+
-
-
- Confirm Period - - {blocksToRelevantTime(network, Number(trackMetaData.confirmPeriod))} - -
-
+
+
+ Min. Enactment Period + + {blocksToRelevantTime(network, Number(trackMetaData.minEnactmentPeriod))} + +
+
-
-
- Min. Enactment Period - - {blocksToRelevantTime(network, Number(trackMetaData.minEnactmentPeriod))} - -
-
- -
-
- Decision Period - - {blocksToRelevantTime(network, Number(trackMetaData.decisionPeriod))} - -
-
+
+
+ Decision Period + + {blocksToRelevantTime(network, Number(trackMetaData.decisionPeriod))} + +
+
-
-
- Decision Deposit - - {trackMetaData.decisionDeposit && - formatUSDWithUnits( - formatBnBalance( - `${trackMetaData.decisionDeposit}`.startsWith('0x') ? new BN(`${trackMetaData.decisionDeposit}`.slice(2), 'hex') : trackMetaData.decisionDeposit, - { numberAfterComma: 2, withThousandDelimitor: false, withUnit: true }, - network - ), - 1 - )} - -
-
+
+
+ Decision Deposit + + {trackMetaData.decisionDeposit && + formatUSDWithUnits( + formatBnBalance( + `${trackMetaData.decisionDeposit}`.startsWith('0x') ? new BN(`${trackMetaData.decisionDeposit}`.slice(2), 'hex') : trackMetaData.decisionDeposit, + { numberAfterComma: 2, withThousandDelimitor: false, withUnit: true }, + network + ), + 1 + )} + +
+
-
-
- Prepare Period - - {blocksToRelevantTime(network, Number(trackMetaData.preparePeriod))} - -
+
+
+ Prepare Period + + {blocksToRelevantTime(network, Number(trackMetaData.preparePeriod))} + +
+
+
+
+ +
-
-
- -
-
+ )} - + -
- {!['moonbeam', 'moonbase', 'moonriver'].includes(network) && } -
-
+
+ {!['moonbeam', 'moonbase', 'moonriver'].includes(network) && } +
+
+
); }; -export default AboutTrackCard; +export default styled(AboutTrackCard)` + @media (max-width: 766px) and (min-width: 319px) { + .text-container { + padding-top: 16px !important; + margin-bottom: 8px !important; + } + } + @media (max-width: 374px) and (min-width: 319px) { + .text-container { + display: block !important; + } + } +`; diff --git a/src/components/Listing/Tracks/DelegateModal.tsx b/src/components/Listing/Tracks/DelegateModal.tsx index ec8999d8fa..c35c567943 100644 --- a/src/components/Listing/Tracks/DelegateModal.tsx +++ b/src/components/Listing/Tracks/DelegateModal.tsx @@ -366,7 +366,7 @@ const DelegateModal = ({ className, defaultTarget, open, setOpen, trackNum, isMu @@ -318,14 +320,18 @@ const SocialVerification = ({ className, socials, onCancel, startLoading, closeM ); }; export default styled(SocialVerification)` -.ant-timeline .ant-timeline-item-tail{ - border-inline-start: 2px solid rgba(5, 5, 5, 0) !important; - background-image: linear-gradient(rgba(144,160,183) 33%, rgba(255,255,255,0) 0%) !important; - background-position: right !important; - background-size: 1.5px 7px !important; - background-repeat: repeat-y !important ; - cursor: pointer !important; -} -.ant-timeline .ant-timeline-item-content { - inset-block-start: -12px; + .ant-timeline .ant-timeline-item-tail { + border-inline-start: 2px solid rgba(5, 5, 5, 0) !important; + background-image: linear-gradient(rgba(144, 160, 183) 33%, rgba(255, 255, 255, 0) 0%) !important; + background-position: right !important; + background-size: 1.5px 7px !important; + background-repeat: repeat-y !important ; + cursor: pointer !important; + } + .ant-timeline .ant-timeline-item-content { + inset-block-start: -12px; + } + .ant-timeline .ant-timeline-item-head { + background-color: transparent !important; + } `; diff --git a/src/components/OnchainIdentity/TotalAmountBreakdown.tsx b/src/components/OnchainIdentity/TotalAmountBreakdown.tsx index 499865e3ec..dd2e2a5557 100644 --- a/src/components/OnchainIdentity/TotalAmountBreakdown.tsx +++ b/src/components/OnchainIdentity/TotalAmountBreakdown.tsx @@ -15,6 +15,10 @@ import { AmountBreakdownModalIcon } from '~src/ui-components/CustomIcons'; import styled from 'styled-components'; import { useNetworkSelector, useUserDetailsSelector } from '~src/redux/selectors'; import { trackEvent } from 'analytics'; +import { useApiContext } from '~src/context'; +import executeTx from '~src/util/executeTx'; +import { ILoading, NotificationStatus } from '~src/types'; +import queueNotification from '~src/ui-components/QueueNotification'; interface Props { className?: string; @@ -24,6 +28,8 @@ interface Props { loading: boolean; isIdentityAlreadySet: boolean; alreadyVerifiedfields: IVerifiedFields; + address: string; + setStartLoading: (pre: ILoading) => void; } const getLearnMoreRedirection = (network: string) => { switch (network) { @@ -34,9 +40,10 @@ const getLearnMoreRedirection = (network: string) => { } }; -const TotalAmountBreakdown = ({ className, txFee, changeStep, perSocialBondFee, loading, isIdentityAlreadySet, alreadyVerifiedfields }: Props) => { +const TotalAmountBreakdown = ({ className, txFee, changeStep, perSocialBondFee, loading, isIdentityAlreadySet, alreadyVerifiedfields, address, setStartLoading }: Props) => { const { registerarFee, minDeposite } = txFee; const { network } = useNetworkSelector(); + const { api, apiReady } = useApiContext(); const unit = `${chainProperties[network]?.tokenSymbol}`; const [amountBreakup, setAmountBreakup] = useState(false); const { id: userId } = useUserDetailsSelector(); @@ -69,15 +76,42 @@ const TotalAmountBreakdown = ({ className, txFee, changeStep, perSocialBondFee, ); }, [network, userId]); - const handleRequestJudgement = () => { + const handleRequestJudgement = async () => { // GAEvent for request judgement button clicked trackEvent('request_judgement_cta_clicked', 'initiated_judgement_request', { userId: currentUser?.id || '', userName: currentUser?.username || '' }); if (isIdentityAlreadySet && !!alreadyVerifiedfields.email && !!alreadyVerifiedfields.twitter) { - handleLocalStorageSave({ setIdentity: true }); - changeStep(ESetIdentitySteps.SOCIAL_VERIFICATION); + if (!api || !apiReady) return; + setStartLoading({ isLoading: true, message: 'Awaiting Confirmation' }); + const requestedJudgementTx = api.tx?.identity?.requestJudgement(3, txFee.registerarFee.toString()); + + const onSuccess = async () => { + handleLocalStorageSave({ setIdentity: true }); + changeStep(ESetIdentitySteps.SOCIAL_VERIFICATION); + setStartLoading({ isLoading: false, message: 'Success!' }); + }; + const onFailed = (error: string) => { + queueNotification({ + header: 'failed!', + message: error || 'Transaction failed!', + status: NotificationStatus.ERROR + }); + setStartLoading({ isLoading: false, message: error || 'Failed' }); + }; + + await executeTx({ + address, + api, + apiReady, + errorMessageFallback: 'failed.', + network, + onFailed, + onSuccess, + setStatus: (message: string) => setStartLoading({ isLoading: true, message }), + tx: requestedJudgementTx + }); } else { setShowAlert(true); } @@ -88,16 +122,20 @@ const TotalAmountBreakdown = ({ className, txFee, changeStep, perSocialBondFee, No identity request found for judgment.} /> )} {isIdentityAlreadySet && showAlert && (!alreadyVerifiedfields.email || !alreadyVerifiedfields.twitter) && ( + To request judgement from Polkassembly please provide both twitter and email credentials for verification before requesting judgement. + + } /> )} diff --git a/src/components/OnchainIdentity/index.tsx b/src/components/OnchainIdentity/index.tsx index a01ea235af..ca291b1e18 100644 --- a/src/components/OnchainIdentity/index.tsx +++ b/src/components/OnchainIdentity/index.tsx @@ -376,7 +376,7 @@ const OnChainIdentity = ({ open, setOpen, openAddressLinkedModal: addressModal, handleLocalStorageSetUnverified(); setLoading({ ...loading, isLoading: false }); }} - className='h-[38px] w-[145px] rounded-[4px] border-pink_primary text-sm font-medium tracking-[0.05em] text-pink_primary' + className='h-[38px] w-[145px] rounded-[4px] border-pink_primary text-sm font-medium tracking-[0.05em] text-pink_primary dark:bg-transparent' > Yes, Exit @@ -412,7 +412,7 @@ const OnChainIdentity = ({ open, setOpen, openAddressLinkedModal: addressModal, )} {step !== ESetIdentitySteps.SOCIAL_VERIFICATION ? 'On-chain identity' : 'Socials Verification'} {isIdentityUnverified && step === ESetIdentitySteps.SOCIAL_VERIFICATION && !loading?.isLoading && ( - + In Progress @@ -433,6 +433,8 @@ const OnChainIdentity = ({ open, setOpen, openAddressLinkedModal: addressModal, perSocialBondFee={perSocialBondFee} changeStep={setStep} alreadyVerifiedfields={alreadyVerifiedfields} + address={address} + setStartLoading={setLoading} /> )} {step === ESetIdentitySteps.SET_IDENTITY_FORM && ( diff --git a/src/components/OpenGovTreasuryProposal/CreatePreimage.tsx b/src/components/OpenGovTreasuryProposal/CreatePreimage.tsx index cdd29c34cc..f1dfae7c1c 100644 --- a/src/components/OpenGovTreasuryProposal/CreatePreimage.tsx +++ b/src/components/OpenGovTreasuryProposal/CreatePreimage.tsx @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import React, { useCallback, useEffect, useState } from 'react'; import { Alert, Button, Form, FormInstance, Input, Radio, Spin } from 'antd'; -import { EEnactment, IEnactment, IPreimage, ISteps } from '.'; +import { EBeneficiaryAddressesAction, EBeneficiaryAddressesActionType, EEnactment, IEnactment, INIT_BENEFICIARIES, IPreimage, ISteps } from '.'; import HelperTooltip from '~src/ui-components/HelperTooltip'; import BN from 'bn.js'; import dynamic from 'next/dynamic'; @@ -15,12 +15,13 @@ import Web3 from 'web3'; import getEncodedAddress from '~src/util/getEncodedAddress'; import styled from 'styled-components'; import DownArrow from '~assets/icons/down-icon.svg'; +import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons'; import { BN_HUNDRED, BN_MAX_INTEGER, BN_ONE, BN_THOUSAND, formatBalance, isHex } from '@polkadot/util'; import { isWeb3Injected } from '@polkadot/extension-dapp'; import { Injected, InjectedWindow } from '@polkadot/extension-inject/types'; import { APPNAME } from '~src/global/appName'; import queueNotification from '~src/ui-components/QueueNotification'; -import { NotificationStatus } from '~src/types'; +import { IBeneficiary, NotificationStatus } from '~src/types'; import { SubmittableExtrinsic } from '@polkadot/api/types'; import { blake2AsHex } from '@polkadot/util-crypto'; import { HexString } from '@polkadot/util/types'; @@ -58,8 +59,8 @@ interface Props { setPreimageHash: (pre: string) => void; setSteps: (pre: ISteps) => void; proposerAddress: string; - beneficiaryAddress: string; - setBeneficiaryAddress: (pre: string) => void; + beneficiaryAddresses: IBeneficiary[]; + dispatchBeneficiaryAddresses: React.Dispatch; fundingAmount: BN; setFundingAmount: (pre: BN) => void; selectedTrack: string; @@ -95,8 +96,8 @@ const CreatePreimage = ({ selectedTrack, setSelectedTrack, proposerAddress, - beneficiaryAddress, - setBeneficiaryAddress, + beneficiaryAddresses, + dispatchBeneficiaryAddresses, enactment, setEnactment, setPreimage, @@ -117,7 +118,6 @@ const CreatePreimage = ({ const [inputAmountValue, setInputAmountValue] = useState('0'); const [txFee, setTxFee] = useState(ZERO_BN); const [showAlert, setShowAlert] = useState(false); - const [isAutoSelectTrack, setIsAutoSelectTrack] = useState(true); const [currentTokenPrice, setCurrentTokenPrice] = useState({ isLoading: true, value: '' @@ -155,34 +155,40 @@ const CreatePreimage = ({ maxSpendArr.sort((a, b) => a.maxSpend - b.maxSpend); - const getPreimageTxFee = (isPreimageVal?: boolean, selectedTrackVal?: string, fundingAmountVal?: BN) => { + const getPreimageTxFee = (isPreimageVal?: boolean, selectedTrackVal?: string, fundingAmountVal?: BN, latestBenefeciaries?: IBeneficiary[]) => { const txSelectedTrack = selectedTrackVal || selectedTrack; - const txBeneficiary = form.getFieldValue('address') || beneficiaryAddress; const txFundingAmount = fundingAmountVal || fundingAmount; + latestBenefeciaries = latestBenefeciaries || beneficiaryAddresses; - if (!api || !apiReady || !txBeneficiary || !txSelectedTrack) return; - setShowAlert(false); + //validate beneficiaryAddresses if ( - isPreimageVal || - isPreimage || - !proposerAddress || - !txBeneficiary || - !getEncodedAddress(txBeneficiary, network) || - !txFundingAmount || - txFundingAmount.lte(ZERO_BN) || - txFundingAmount.eq(ZERO_BN) - ) + !latestBenefeciaries.length || + latestBenefeciaries.find( + (beneficiary) => !beneficiary.address || isNaN(Number(beneficiary.amount)) || Number(beneficiary.amount) <= 0 || !getEncodedAddress(beneficiary.address, network) + ) + ) { return; + } + + if (!api || !apiReady || !latestBenefeciaries.length || !txSelectedTrack) return; + setShowAlert(false); + if (isPreimageVal || isPreimage || !proposerAddress || !txFundingAmount || txFundingAmount.lte(ZERO_BN) || txFundingAmount.eq(ZERO_BN)) return; + + const txArr: any[] = []; + + latestBenefeciaries.forEach((beneficiary) => { + if (beneficiary.address && beneficiary.amount && getEncodedAddress(beneficiary.address, network) && Number(beneficiary.amount) > 0) { + const [balance] = inputToBn(`${beneficiary.amount}`, network, false); + txArr.push(api?.tx?.treasury?.spend(balance.toString(), beneficiary.address)); + } + }); - setLoading(true); - const tx = api.tx.treasury.spend(txFundingAmount.toString(), txBeneficiary); (async () => { await form.validateFields(); - const info = await tx.paymentInfo(proposerAddress); + const info = await (txArr.length > 1 ? api.tx.utility.batchAll(txArr).paymentInfo(proposerAddress) : txArr[0].paymentInfo(proposerAddress)); const gasFee: BN = new BN(info.partialFee); setGasFee(gasFee); setTxFee(gasFee.add(baseDeposit)); - setLoading(false); setShowAlert(true); })(); }; @@ -191,10 +197,11 @@ const CreatePreimage = ({ setSteps({ percent: 20, step: 1 }); setAdvancedDetails({ ...advancedDetails, atBlockNo: currentBlock?.add(BN_THOUSAND) || BN_ONE }); - const bnBalance = new BN(isNaN(Number(createPreimageForm?.fundingAmount)) ? 0 : createPreimageForm?.fundingAmount); const [balance, isValid] = inputToBn(`${isNaN(Number(createPreimageForm?.fundingAmount)) ? 0 : createPreimageForm?.fundingAmount}`, network, false); + if (isValid) { if (createPreimageForm.isPreimage) { + const bnBalance = new BN(isNaN(Number(createPreimageForm?.fundingAmount)) ? 0 : createPreimageForm?.fundingAmount); setFundingAmount(bnBalance); } else { setFundingAmount(balance); @@ -205,17 +212,30 @@ const CreatePreimage = ({ setInputAmountValue(createPreimageForm?.fundingAmount); setPreimageHash(createPreimageForm?.preimageHash || ''); setPreimageLength(createPreimageForm?.preimageLength || null); - setBeneficiaryAddress(createPreimageForm?.beneficiaryAddress || ''); + + dispatchBeneficiaryAddresses({ + payload: { + address: '', + amount: '', + index: 0, + newState: createPreimageForm?.beneficiaryAddresses || INIT_BENEFICIARIES + }, + type: EBeneficiaryAddressesActionType.REPLACE_STATE + }); + setEnactment(createPreimageForm?.enactment || { key: EEnactment.After_No_Of_Blocks, value: BN_HUNDRED }); - setBeneficiaryAddress(createPreimageForm.beneficiaryAddress || ''); setSelectedTrack(createPreimageForm?.selectedTrack || ''); form.setFieldValue('preimage_hash', createPreimageForm?.preimageHash || ''); form.setFieldValue('preimage_length', createPreimageForm?.preimageLength || 0); form.setFieldValue('funding_amount', createPreimageForm?.fundingAmount); - form.setFieldValue('address', createPreimageForm.beneficiaryAddress || ''); form.setFieldValue('at_block', currentBlock?.add(BN_THOUSAND) || BN_ONE); + ((createPreimageForm?.beneficiaryAddresses || INIT_BENEFICIARIES) as IBeneficiary[]).forEach((beneficiary, index) => { + form.setFieldValue(`address-${index}`, beneficiary.address || ''); + form.setFieldValue(`balance-${index}`, beneficiary.amount || ZERO_BN); + }); + if ( createPreimageForm.preimageHash && createPreimageForm.preimageLength && @@ -227,11 +247,9 @@ const CreatePreimage = ({ } if (createPreimageForm.beneficiaryAddress && createPreimageForm?.fundingAmount && createPreimageForm?.selectedTrack) { setSteps({ percent: 100, step: 1 }); + const bnBalance = new BN(isNaN(Number(createPreimageForm?.fundingAmount)) ? 0 : createPreimageForm?.fundingAmount); getPreimageTxFee(createPreimageForm.isPreimage, createPreimageForm?.selectedTrack, createPreimageForm.isPreimage ? bnBalance : balance); } - if (createPreimageForm?.selectedTrack) { - setIsAutoSelectTrack(false); - } if (createPreimageForm?.enactment) { setOpenAdvanced(true); @@ -240,14 +258,19 @@ const CreatePreimage = ({ }; const handleSelectTrack = (fundingAmount: BN, isPreimage: boolean) => { + let selectedTrack = ''; + for (const i in maxSpendArr) { const [maxSpend] = inputToBn(String(maxSpendArr[i].maxSpend), network, false); if (maxSpend.gte(fundingAmount)) { + selectedTrack = maxSpendArr[i].track; setSelectedTrack(maxSpendArr[i].track); onChangeLocalStorageSet({ selectedTrack: maxSpendArr[i].track }, Boolean(isPreimage)); break; } } + + return selectedTrack; }; useEffect(() => { @@ -303,7 +326,6 @@ const CreatePreimage = ({ form.setFieldValue('at_block', currentBlock?.add(BN_THOUSAND) || BN_ONE); if (data.preimageCreated) setPreimageCreated(data.preimageCreated); if (data.preimageLinked) setPreimageLinked(data.preimageLinked); - setIsAutoSelectTrack(true); setOpenAdvanced(false); } }; @@ -371,7 +393,16 @@ const CreatePreimage = ({ } api.setSigner(injected.signer); - const proposal = api?.tx?.treasury?.spend(fundingAmount.toString(), beneficiaryAddress); + const txArr: any[] = []; + + beneficiaryAddresses.forEach((beneficiary) => { + const [balance] = inputToBn(`${beneficiary.amount}`, network, false); + if (beneficiary.address && !isNaN(Number(beneficiary.amount)) && getEncodedAddress(beneficiary.address, network) && Number(beneficiary.amount) > 0) { + txArr.push(api?.tx?.treasury?.spend(balance.toString(), beneficiary.address)); + } + }); + + const proposal = txArr.length > 1 ? api.tx.utility.batchAll(txArr) : txArr[0]; const preimage: any = getState(api, proposal); setLoading(true); const onSuccess = () => { @@ -379,7 +410,11 @@ const CreatePreimage = ({ setPreimageHash(preimage.preimageHash); setPreimageLength(preimage.preimageLength); setPreimageCreated(true); - onChangeLocalStorageSet({ preimageCreated: true, preimageHash: preimage.preimageHash, preimageLength: preimage.preimageLength }, Boolean(isPreimage), true); + onChangeLocalStorageSet( + { beneficiaryAddresses: INIT_BENEFICIARIES, preimageCreated: true, preimageHash: preimage.preimageHash, preimageLength: preimage.preimageLength }, + Boolean(isPreimage), + true + ); setLoading(false); setSteps({ percent: 100, step: 2 }); }; @@ -404,6 +439,24 @@ const CreatePreimage = ({ userId: currentUser?.id || '', userName: currentUser?.username || '' }); + + //validate beneficiaryAddresses and fundingAmount for each beneficiary + let areBeneficiaryAddressesValid = true; + for (const beneficiary in beneficiaryAddresses) { + if ( + !beneficiaryAddresses[beneficiary].address || + isNaN(Number(beneficiaryAddresses[beneficiary].amount)) || + !getEncodedAddress(beneficiaryAddresses[beneficiary].address, network) || + Number(beneficiaryAddresses[beneficiary].amount) <= 0 + ) { + areBeneficiaryAddressesValid = false; + setValidBeneficiaryAddress(false); + return; + } + } + + if (!areBeneficiaryAddressesValid) return; + if (!isPreimage) { if (txFee.gte(availableBalance)) return; } @@ -415,7 +468,7 @@ const CreatePreimage = ({ } else { if (!isPreimage) { await getPreimage(); - } else if (preimageLength !== 0 && beneficiaryAddress?.length > 0 && fundingAmount.gt(ZERO_BN)) { + } else if (preimageLength !== 0 && beneficiaryAddresses[0]?.address?.length > 0 && fundingAmount.gt(ZERO_BN)) { setSteps({ percent: 100, step: 2 }); } setEnactment({ ...enactment, value: enactment.key === EEnactment.At_Block_No ? advancedDetails?.atBlockNo : advancedDetails?.afterNoOfBlocks }); @@ -469,9 +522,23 @@ const CreatePreimage = ({ }); if (preImageArguments && proposal.section === 'treasury' && proposal?.method === 'spend') { const balance = new BN(preImageArguments[0].value || '0') || ZERO_BN; - setBeneficiaryAddress(preImageArguments[1].value || ''); + + const newBeneficiaryAddress = { + address: preImageArguments[1].value, + amount: balance.toString() + }; + + dispatchBeneficiaryAddresses({ + payload: { + address: newBeneficiaryAddress.address, + amount: newBeneficiaryAddress.amount, + index: 0 + }, + type: EBeneficiaryAddressesActionType.REPLACE_ALL_WITH_ONE + }); + setFundingAmount(balance); - onChangeLocalStorageSet({ beneficiaryAddress: preImageArguments[1].value || '', fundingAmount: balance.toString() }, Boolean(isPreimage)); + onChangeLocalStorageSet({ beneficiaryAddresses: [newBeneficiaryAddress] || '', fundingAmount: balance.toString() }, Boolean(isPreimage)); setSteps({ percent: 100, step: 1 }); handleSelectTrack(balance, isPreimage); } else { @@ -497,6 +564,7 @@ const CreatePreimage = ({ }); } }; + const existPreimageData = async (preimageHash: string, isPreimage: boolean) => { setPreimageLength(0); form.setFieldValue('preimage_length', 0); @@ -512,13 +580,28 @@ const CreatePreimage = ({ } else { console.log('fetching data from subsquid'); form.setFieldValue('preimage_length', data?.length); - setBeneficiaryAddress(data?.proposedCall?.args?.beneficiary || ''); + const balance = new BN(data?.proposedCall?.args?.amount || '0') || ZERO_BN; + + const newBeneficiaryAddress = { + address: data?.proposedCall?.args?.beneficiary, + amount: balance.toString() + }; + + dispatchBeneficiaryAddresses({ + payload: { + address: newBeneficiaryAddress.address, + amount: newBeneficiaryAddress.amount, + index: 0 + }, + type: EBeneficiaryAddressesActionType.REPLACE_ALL_WITH_ONE + }); + setFundingAmount(balance); setPreimageLength(data.length); form.setFieldValue('preimage_length', data.length); onChangeLocalStorageSet( - { beneficiaryAddress: data?.proposedCall?.args?.beneficiary || '', fundingAmount: balance.toString(), preimageLength: data?.length || '' }, + { beneficiaryAddresses: [newBeneficiaryAddress] || [], fundingAmount: balance.toString(), preimageLength: data?.length || '' }, Boolean(isPreimage) ); //select track @@ -545,6 +628,8 @@ const CreatePreimage = ({ // eslint-disable-next-line react-hooks/exhaustive-deps const debounceExistPreimageFn = useCallback(_.debounce(existPreimageData, 2000), []); + // eslint-disable-next-line react-hooks/exhaustive-deps + const debounceGetPreimageTxFee = useCallback(_.debounce(getPreimageTxFee, 500), []); const handlePreimageHash = (preimageHash: string, isPreimage: boolean) => { if (!preimageHash || preimageHash.length === 0) return; @@ -578,11 +663,19 @@ const CreatePreimage = ({ setPreimageLinked(false); }; - const handleBeneficiaryAddresschange = (address: string) => { - setBeneficiaryAddress(address); + const handleBeneficiaryAddresschange = (address: string, index: number) => { + dispatchBeneficiaryAddresses({ + payload: { + address, + amount: '', + index + }, + type: EBeneficiaryAddressesActionType.UPDATE_ADDRESS + }); + setPreimageCreated(false); setPreimageLinked(false); - !isPreimage && onChangeLocalStorageSet({ beneficiaryAddress: beneficiaryAddress }, Boolean(isPreimage)); + !isPreimage && onChangeLocalStorageSet({ beneficiaryAddresses: beneficiaryAddresses }, Boolean(isPreimage)); setSteps({ percent: fundingAmount.gt(ZERO_BN) && address?.length > 0 ? 100 : 60, step: 1 }); if (address.length > 0) { (getEncodedAddress(address, network) || Web3.utils.isAddress(address)) && address !== getEncodedAddress(address, network) && setAddressAlert(true); @@ -591,6 +684,7 @@ const CreatePreimage = ({ setAddressAlert(false); }, 5000); }; + const handleOnAvailableBalanceChange = (balanceStr: string) => { let balance = ZERO_BN; @@ -603,12 +697,79 @@ const CreatePreimage = ({ }; const handleFundingAmountChange = (fundingAmount: BN) => { - setFundingAmount(fundingAmount); setPreimageCreated(false); setPreimageLinked(false); - setSteps({ percent: beneficiaryAddress?.length > 0 && fundingAmount.gt(ZERO_BN) ? 100 : 60, step: 1 }); - if (!isAutoSelectTrack || !fundingAmount || fundingAmount.eq(ZERO_BN)) return; - handleSelectTrack(fundingAmount, Boolean(isPreimage)); + setSteps({ percent: beneficiaryAddresses[0]?.address?.length > 0 && fundingAmount.gt(ZERO_BN) ? 100 : 60, step: 1 }); + }; + + const handleInputValueChange = (input: string, index: number) => { + if (isNaN(Number(input))) return; + + dispatchBeneficiaryAddresses({ + payload: { + address: '', + amount: input, + index + }, + type: EBeneficiaryAddressesActionType.UPDATE_AMOUNT + }); + + let totalAmt = 0; + + const latestBenefeciaries = beneficiaryAddresses.map((beneficiary, i) => { + if (index === i) { + totalAmt += Number(input); + return { ...beneficiary, amount: input }; + } else { + totalAmt += Number(beneficiary.amount); + } + return beneficiary; + }); + + totalAmt = Number(totalAmt.toFixed(6)); + + setInputAmountValue(totalAmt.toString()); + form.setFieldValue('funding_amount', totalAmt.toString()); + onChangeLocalStorageSet({ beneficiaryAddresses: latestBenefeciaries, fundingAmount: totalAmt.toString() }, Boolean(isPreimage)); + + const [fundingAmt] = inputToBn(totalAmt.toString(), network, false); + setFundingAmount(fundingAmt); + + const selectedTrack = handleSelectTrack(fundingAmt, Boolean(isPreimage)); + debounceGetPreimageTxFee(Boolean(isPreimage), selectedTrack, fundingAmt, latestBenefeciaries); + }; + + const addBeneficiary = () => { + dispatchBeneficiaryAddresses({ + payload: { + address: '', + amount: '', + index: beneficiaryAddresses.length + }, + type: EBeneficiaryAddressesActionType.ADD + }); + }; + + const removeAllBeneficiaries = () => { + dispatchBeneficiaryAddresses({ + payload: { + address: '', + amount: '', + index: 0 + }, + type: EBeneficiaryAddressesActionType.REMOVE_ALL + }); + + form.resetFields(); + + setInputAmountValue('0'); + form.setFieldValue('funding_amount', '0'); + handleSelectTrack(ZERO_BN, Boolean(isPreimage)); + }; + + const fundingAmtToBN = () => { + const [fundingAmt] = inputToBn(inputAmountValue || '0', network, false); + return fundingAmt; }; return ( @@ -648,7 +809,6 @@ const CreatePreimage = ({ disabled={loading} onFinish={handleSubmit} initialValues={{ - address: beneficiaryAddress, after_blocks: String(advancedDetails.afterNoOfBlocks?.toString()), at_block: String(advancedDetails.atBlockNo?.toString()), preimage_hash: preimageHash, @@ -700,9 +860,9 @@ const CreatePreimage = ({ {txFee.gte(availableBalance) && !txFee.eq(ZERO_BN) && ( Insufficient available balance.} /> )}
@@ -727,31 +887,77 @@ const CreatePreimage = ({ identiconSize={30} />
- handleBeneficiaryAddresschange(address)} - helpText='The amount requested in the proposal will be received in this address.' - size='large' - identiconSize={30} - inputClassName={'font-normal text-sm h-[40px]'} - skipFormatCheck={true} - checkValidAddress={setValidBeneficiaryAddress} - onBlur={getPreimageTxFee} - /> - {beneficiaryAddress - ? !(getEncodedAddress(beneficiaryAddress, network) || Web3.utils.isAddress(beneficiaryAddress)) && ( - Invalid Address - ) - : null} + + {beneficiaryAddresses.map((beneficiary, index) => { + return ( +
+
+ 1 ? index + 1 : ''}`}`} + placeholder='Add beneficiary address' + className='text-sm font-normal text-lightBlue dark:text-blue-dark-medium' + onChange={(address) => handleBeneficiaryAddresschange(address, index)} + helpText='The amount requested in the proposal will be received in this address.' + size='large' + identiconSize={30} + inputClassName={'font-normal text-sm h-[40px]'} + skipFormatCheck={true} + checkValidAddress={setValidBeneficiaryAddress} + /> + + {beneficiary.address + ? !(getEncodedAddress(beneficiary.address, network) || Web3.utils.isAddress(beneficiary.address)) && ( + Invalid Address + ) + : null} +
+
+ handleInputValueChange(input, index)} + onChange={handleFundingAmountChange} + theme={theme} + /> +
+
+ ); + })} + +
+ + + +
{addressAlert && ( The substrate address has been changed to {network} network address.} /> )}
@@ -770,16 +976,12 @@ const CreatePreimage = ({
{ - setInputAmountValue(input); - onChangeLocalStorageSet({ fundingAmount: input }, Boolean(isPreimage)); - }} formItemName='funding_amount' - onChange={handleFundingAmountChange} theme={theme} + balance={fundingAmtToBN()} + disabled={true} />
@@ -796,7 +998,6 @@ const CreatePreimage = ({ tracksArr={trackArr} onTrackChange={(track) => { setSelectedTrack(track); - setIsAutoSelectTrack(false); onChangeLocalStorageSet({ selectedTrack: track }, isPreimage); getPreimageTxFee(); setSteps({ percent: 100, step: 1 }); @@ -925,10 +1126,18 @@ const CreatePreimage = ({ {showAlert && !isPreimage && !txFee.eq(ZERO_BN) && ( + Gas Fees of {formatedBalance(String(gasFee.toString()), unit)} {unit} will be applied to create preimage. + + } + message={ + + {formatedBalance(String(baseDeposit.toString()), unit)} {unit} Base deposit is required to create a preimage. + + } /> )}
@@ -942,12 +1151,28 @@ const CreatePreimage = ({ htmlType='submit' className={`h-[40px] w-[165px] rounded-[4px] bg-pink_primary text-center text-sm font-medium tracking-[0.05em] text-white ${ (isPreimage !== null && !isPreimage - ? !(beneficiaryAddress && validBeneficiaryAddress && fundingAmount && selectedTrack && !txFee.gte(availableBalance) && !txFee.eq(ZERO_BN) && !loading) + ? !( + !beneficiaryAddresses.find((beneficiary) => !beneficiary.address || isNaN(Number(beneficiary.amount)) || Number(beneficiary.amount) <= 0) && + validBeneficiaryAddress && + fundingAmount && + selectedTrack && + !txFee.gte(availableBalance) && + !txFee.eq(ZERO_BN) && + !loading + ) : preimageHash?.length === 0 || invalidPreimageHash()) && 'opacity-50' }`} disabled={ isPreimage !== null && !isPreimage - ? !(beneficiaryAddress && validBeneficiaryAddress && fundingAmount && selectedTrack && !txFee.gte(availableBalance) && !txFee.eq(ZERO_BN) && !loading) + ? !( + !beneficiaryAddresses.find((beneficiary) => !beneficiary.address || isNaN(Number(beneficiary.amount)) || Number(beneficiary.amount) <= 0) && + validBeneficiaryAddress && + fundingAmount && + selectedTrack && + !txFee.gte(availableBalance) && + !txFee.eq(ZERO_BN) && + !loading + ) : preimageHash?.length === 0 || invalidPreimageHash() } > @@ -959,6 +1184,7 @@ const CreatePreimage = ({ ); }; + export default styled(CreatePreimage)` .down-icon { filter: brightness(0) saturate(100%) invert(13%) sepia(94%) saturate(7151%) hue-rotate(321deg) brightness(90%) contrast(101%); diff --git a/src/components/OpenGovTreasuryProposal/CreateProposal.tsx b/src/components/OpenGovTreasuryProposal/CreateProposal.tsx index 1f85462612..0c51d74692 100644 --- a/src/components/OpenGovTreasuryProposal/CreateProposal.tsx +++ b/src/components/OpenGovTreasuryProposal/CreateProposal.tsx @@ -14,7 +14,7 @@ import { formatedBalance } from '~src/util/formatedBalance'; import copyToClipboard from '~src/util/copyToClipboard'; import { LoadingOutlined } from '@ant-design/icons'; import queueNotification from '~src/ui-components/QueueNotification'; -import { NotificationStatus } from '~src/types'; +import { IBeneficiary, NotificationStatus } from '~src/types'; import { Injected, InjectedWindow } from '@polkadot/extension-inject/types'; import { isWeb3Injected } from '@polkadot/extension-dapp'; import { APPNAME } from '~src/global/appName'; @@ -25,6 +25,7 @@ import { poppins } from 'pages/_app'; import executeTx from '~src/util/executeTx'; import { useNetworkSelector, useUserDetailsSelector } from '~src/redux/selectors'; import { CopyIcon } from '~src/ui-components/CustomIcons'; +import Beneficiary from '~src/ui-components/BeneficiariesListing/Beneficiary'; import { trackEvent } from 'analytics'; const ZERO_BN = new BN(0); @@ -38,7 +39,7 @@ interface Props { preimageHash: string; preimageLength: number | null; enactment: IEnactment; - beneficiaryAddress: string; + beneficiaryAddresses: IBeneficiary[]; setOpenModal: (pre: boolean) => void; setOpenSuccess: (pre: boolean) => void; title: string; @@ -63,7 +64,7 @@ const CreateProposal = ({ preimageHash, preimageLength, enactment, - beneficiaryAddress, + beneficiaryAddresses, setOpenModal, setOpenSuccess, title, @@ -133,7 +134,7 @@ const CreateProposal = ({ setLoading(false); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [proposerAddress, beneficiaryAddress, fundingAmount, api, apiReady, network, selectedTrack, preimageHash, preimageLength, enactment.value, enactment.key]); + }, [proposerAddress, beneficiaryAddresses, fundingAmount, api, apiReady, network, selectedTrack, preimageHash, preimageLength, enactment.value, enactment.key]); const handleSaveTreasuryProposal = async (postId: number) => { const { data, error: apiError } = await nextApiClientFetch('api/v1/auth/actions/createOpengovTreasuryProposal', { @@ -146,15 +147,7 @@ const CreateProposal = ({ userId }); - if (data && !isNaN(Number(data?.post_id)) && data.post_id !== undefined) { - setPostId(data?.post_id); - setOpenSuccess(true); - console.log(postId, 'postId'); - localStorage.removeItem('treasuryProposalProposerAddress'); - localStorage.removeItem('treasuryProposalProposerWallet'); - localStorage.removeItem('treasuryProposalData'); - setOpenModal(false); - } else if (apiError || !data?.post_id) { + if (apiError || !data?.post_id) { queueNotification({ header: 'Error', message: apiError, @@ -162,6 +155,7 @@ const CreateProposal = ({ }); console.error(apiError); } + setLoading(false); }; @@ -220,8 +214,15 @@ const CreateProposal = ({ ); const onSuccess = async () => { - await handleSaveTreasuryProposal(post_id); + handleSaveTreasuryProposal(post_id); + setPostId(post_id); + console.log('Saved referenda ID: ', post_id); + localStorage.removeItem('treasuryProposalProposerAddress'); + localStorage.removeItem('treasuryProposalProposerWallet'); + localStorage.removeItem('treasuryProposalData'); setLoading(false); + setOpenSuccess(true); + setOpenModal(false); }; const onFailed = async () => { @@ -256,14 +257,14 @@ const CreateProposal = ({ {submitionDeposite.gte(availableBalance) && !txFee.eq(ZERO_BN) && ( Insufficient available balance.} /> )} Preimage {isPreimage ? 'linked' : 'created'} successfully} + className={`mt-4 rounded-[4px] text-sm text-bodyBlue dark:border-[#026630] dark:bg-[#063E20] dark:text-blue-dark-high ${poppins.variable} ${poppins.className}`} type='success' showIcon /> @@ -281,13 +282,15 @@ const CreateProposal = ({ Beneficiary Address: -
+
+ {beneficiaryAddresses.map((beneficiary, index) => ( + + ))} +
Track: @@ -344,11 +347,11 @@ const CreateProposal = ({
{showAlert && ( + An amount of{' '} {formatedBalance(String(txFee.add(submitionDeposite).toString()), unit)} {unit} @@ -358,19 +361,19 @@ const CreateProposal = ({ } description={
- - Deposit amount + + Deposit amount {formatedBalance(String(submitionDeposite.toString()), unit)} {unit} - + Gas fees {formatedBalance(String(txFee.toString()), unit)} {unit} - + Total {formatedBalance(String(txFee.add(submitionDeposite).toString()), unit)} {unit} diff --git a/src/components/OpenGovTreasuryProposal/SelectTracks.tsx b/src/components/OpenGovTreasuryProposal/SelectTracks.tsx index 5c307489ec..88e61a8f86 100644 --- a/src/components/OpenGovTreasuryProposal/SelectTracks.tsx +++ b/src/components/OpenGovTreasuryProposal/SelectTracks.tsx @@ -19,7 +19,7 @@ const SelectTracks = ({ tracksArr, className, onTrackChange, selectedTrack }: Pr = ({ open, dismissModal }) => { {error && } {extensionNotAvailable && ( Please install polkadot.js extension} type='error' + className='dark:border-errorAlertBorderDark dark:bg-errorAlertBgDark' /> )}
diff --git a/src/components/Settings/Account/Proxy.tsx b/src/components/Settings/Account/Proxy.tsx index f48dbb1c94..5c4818fec0 100644 --- a/src/components/Settings/Account/Proxy.tsx +++ b/src/components/Settings/Account/Proxy.tsx @@ -186,11 +186,12 @@ const Proxy: FC = ({ dismissModal, open }) => { +

At least one proxy account should be in your polkadot js extension.

Please reload this page after adding accounts.

- +
} + className='dark:border-warningAlertBorderDark dark:bg-warningAlertBgDark' /> ) : ( <> diff --git a/src/components/Settings/Profile/index.tsx b/src/components/Settings/Profile/index.tsx index 364410540a..b12f97c37a 100644 --- a/src/components/Settings/Profile/index.tsx +++ b/src/components/Settings/Profile/index.tsx @@ -238,7 +238,8 @@ const Profile = () => {
{err}} + className='dark:border-errorAlertBorderDark dark:bg-errorAlertBgDark' />
)} diff --git a/src/components/Signup/MetamaskSignup.tsx b/src/components/Signup/MetamaskSignup.tsx index 7b53af1b1c..a2b3415bc6 100644 --- a/src/components/Signup/MetamaskSignup.tsx +++ b/src/components/Signup/MetamaskSignup.tsx @@ -266,10 +266,11 @@ const MetamaskSignup: FC = ({ onWalletUpdate, chosenWallet, isModal, setS {accountsNotFound && (
You need at least one account in Polkadot-js extension to login.} + description={Please reload this page after adding accounts.} type='info' showIcon + className='dark:border-infoAlertBorderDark dark:bg-infoAlertBgDark' />
)} @@ -296,13 +297,14 @@ const MetamaskSignup: FC = ({ onWalletUpdate, chosenWallet, isModal, setS

-
+
diff --git a/src/components/Signup/Web2Signup.tsx b/src/components/Signup/Web2Signup.tsx index afb46a1e47..dcfb0de632 100644 --- a/src/components/Signup/Web2Signup.tsx +++ b/src/components/Signup/Web2Signup.tsx @@ -39,7 +39,7 @@ const WalletButtons = dynamic(() => import('~src/components/Login/WalletButtons' const Container = styled.article` .changeColor .ant-alert-message { - color: ${(props) => (props.theme === 'dark' ? 'white' : '#243a57')} !important; + color: ${(props) => (props.theme === 'dark' ? '#1677ff' : '#243a57')} !important; } `; interface Props { @@ -196,18 +196,22 @@ const Web2Signup: FC = ({ className, walletError, onWalletSelect, isModal {defaultWallets.length === 0 && isDelegation && ( Wallet extension not detected.} + description={ + + No web 3 account integration could be found. To be able to use this feature, visit this page on a computer with polkadot-js extension. + + } type='info' showIcon - className='changeColor px-8 text-[#243A57] dark:text-blue-dark-high' + className='changeColor px-8 text-[#243A57] dark:border-infoAlertBorderDark dark:bg-infoAlertBgDark dark:text-white' /> )} {walletError && ( {walletError}} type='error' - className='px-8' + className='px-8 dark:border-errorAlertBorderDark dark:bg-errorAlertBgDark' /> )} = ({ className, walletError, onWalletSelect, isModal showPolkasafe={canUsePolkasafe(network)} onPolkasafeSelect={setWithPolkasafe} isSigningUp={true} + isLoginFlow={true} />
{error && ( diff --git a/src/components/Signup/Web3Signup.tsx b/src/components/Signup/Web3Signup.tsx index e6a8e7abad..d654b4f718 100644 --- a/src/components/Signup/Web3Signup.tsx +++ b/src/components/Signup/Web3Signup.tsx @@ -399,10 +399,11 @@ const Web3Signup: FC = ({ {accountsNotFound && (
You need at least one account in Polkadot-js extension to login.} + description={Please reload this page after adding accounts.} type='info' showIcon + className='dark:border-infoAlertBorderDark dark:bg-infoAlertBgDark' />
)} diff --git a/src/components/Tipping/index.tsx b/src/components/Tipping/index.tsx index 8f81e858ae..5a000f3039 100644 --- a/src/components/Tipping/index.tsx +++ b/src/components/Tipping/index.tsx @@ -322,10 +322,10 @@ const Tipping = ({ className, open, setOpen, username, openAddressChangeModal, s > {!tipAmount.eq(ZERO_BN) && availableBalance.lte(tipAmount.add(existentialDeposit)) ? ( Insufficient Balance for Tipping} /> ) : null}
diff --git a/src/components/UserProfile/BasicInformation.tsx b/src/components/UserProfile/BasicInformation.tsx index daec5e462b..5c9384a879 100644 --- a/src/components/UserProfile/BasicInformation.tsx +++ b/src/components/UserProfile/BasicInformation.tsx @@ -245,8 +245,8 @@ const BasicInformation: FC = (props) => {
{errorCheck && ( {errorCheck}} type='info' showIcon /> diff --git a/src/components/UserProfile/Socials.tsx b/src/components/UserProfile/Socials.tsx index 7d6adb85db..4c1c308515 100644 --- a/src/components/UserProfile/Socials.tsx +++ b/src/components/UserProfile/Socials.tsx @@ -80,8 +80,8 @@ const Socials: FC = (props) => { })} {errorCheck && ( {errorCheck}} type='info' showIcon /> diff --git a/src/components/WalletButton/index.tsx b/src/components/WalletButton/index.tsx index 147e729c71..11d25df8be 100644 --- a/src/components/WalletButton/index.tsx +++ b/src/components/WalletButton/index.tsx @@ -2,9 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Button } from 'antd'; -import { InjectedWindow } from '@polkadot/extension-inject/types'; -import { useEffect, useState } from 'react'; +import { Button, Tooltip } from 'antd'; import styled from 'styled-components'; interface Props { @@ -15,32 +13,57 @@ interface Props { className?: string; text?: string; isOptionalLogin?: boolean; + isAvailable?: boolean; + isLoginFlow?: boolean; } -const WalletButton = ({ disabled, onClick, icon, className, text, name, isOptionalLogin }: Props) => { - const [availableWallets, setAvailableWallets] = useState({}); - - const getWallet = () => { - const injectedWindow = window as Window & InjectedWindow; - setAvailableWallets(injectedWindow.injectedWeb3); - }; - - useEffect(() => { - getWallet(); - }, []); - +const WalletButton = ({ isLoginFlow, disabled, onClick, icon, className, text, name, isOptionalLogin, isAvailable }: Props) => { return ( - + <> + {!isOptionalLogin && isLoginFlow && ( + + + + )} + {isOptionalLogin && isLoginFlow && ( + + )} + {!isLoginFlow && ( + + )} + ); }; @@ -52,4 +75,9 @@ export default styled(WalletButton)` overflow-x: hidden; } } + @media (max-width: 544px) and (min-width: 319px) { + .not-installed-container { + display: none; + } + } `; diff --git a/src/global/networkConstants.ts b/src/global/networkConstants.ts index d1b76852a6..74dc58356e 100644 --- a/src/global/networkConstants.ts +++ b/src/global/networkConstants.ts @@ -536,7 +536,7 @@ export const chainProperties: types.ChainPropType = { category: 'polkadot', chainId: 0, logo: equilibriumLogo, - rpcEndpoint: 'wss://node.pol.equilibrium.io/', + rpcEndpoint: 'wss://equilibrium-rpc.dwellir.com', ss58Format: 68, tokenDecimals: 9, tokenSymbol: tokenSymbol.TOKEN, @@ -861,7 +861,7 @@ export const chainProperties: types.ChainPropType = { logo: parallelLogo, rpcEndpoint: 'wss://rpc.parallel.fi', ss58Format: 172, - subsquidUrl: '', + subsquidUrl: 'https://squid.subsquid.io/parallel-polkassembly/graphql', tokenDecimals: 12, tokenSymbol: tokenSymbol.PARA, treasuryProposalBondPercent: '5%', diff --git a/src/queries.ts b/src/queries.ts index c031f6f128..511220be0b 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -1588,20 +1588,42 @@ query ConvictionVotesListingForAddressByTypeAndIndex($orderBy: [ConvictionVoteOr totalCount } convictionVotes(orderBy: $orderBy, where: {type_eq: $type_eq, decision_eq: $decision_eq, proposal: {index_eq: $index_eq}, removedAtBlock_isNull: true, voter_eq: $voter_eq}, limit: $limit, offset: $offset) { - decision - voter - balance { - ... on StandardVoteBalance { - value - } - ... on SplitVoteBalance { - aye - nay - abstain - } - } - lockPeriod - createdAt + id + decision + voter + balance { + ... on StandardVoteBalance { + value + } + ... on SplitVoteBalance { + aye + nay + abstain + } + } + createdAt + lockPeriod + selfVotingPower + totalVotingPower + delegatedVotingPower + delegatedVotes(limit: 5, orderBy: votingPower_DESC, where: { + removedAtBlock_isNull: true + }) { + decision + lockPeriod + voter + votingPower + balance { + ... on StandardVoteBalance { + value + } + ... on SplitVoteBalance { + aye + nay + abstain + } + } + } } } `; @@ -1939,3 +1961,21 @@ export const GET_POSTS_LISTING_FOR_POLYMESH = `query PolymeshPrposalsQuery($type totalCount } }`; + +export const GET_AYE_NAY_TOTAL_COUNT = `query getAyeNayTotalCount($type_eq: ProposalType, $proposalIndex_eq: Int= 291) { + aye: flattenedConvictionVotesConnection(orderBy: id_ASC, where: { + decision_eq: yes, removedAtBlock_isNull: true,proposalIndex_eq: $proposalIndex_eq, proposal:{type_eq:$type_eq} } + ) { + totalCount + } + nay: flattenedConvictionVotesConnection(orderBy: id_ASC, where: { + decision_eq: no, removedAtBlock_isNull: true, proposalIndex_eq:$proposalIndex_eq, proposal:{type_eq:$type_eq} } + ) { + totalCount + } + abstain: flattenedConvictionVotesConnection(orderBy: id_ASC, where: { + decision_eq: abstain, removedAtBlock_isNull: true, proposalIndex_eq:$proposalIndex_eq, proposal:{type_eq:$type_eq} } + ) { + totalCount + } +}`; diff --git a/src/redux/userDetails/index.ts b/src/redux/userDetails/index.ts index ccb6b2c3fe..d60d5ea688 100644 --- a/src/redux/userDetails/index.ts +++ b/src/redux/userDetails/index.ts @@ -54,6 +54,7 @@ export const userDetailsStore = createSlice({ localStorage.removeItem('loginAddress'); localStorage.removeItem('identityWallet'); localStorage.removeItem('identityAddress'); + localStorage.removeItem('identityNudgeStatus'); localStorage.removeItem('identityForm'); state.addresses = []; state.allowed_roles = []; diff --git a/src/types.ts b/src/types.ts index e6ef1ebe62..c4509da7af 100644 --- a/src/types.ts +++ b/src/types.ts @@ -348,6 +348,7 @@ export interface Post { subscribers?: number[]; summary?: string; createdOnPolkassembly?: boolean; + inductee_address?: string; } export interface IPostTag { @@ -524,3 +525,9 @@ export interface IBeneficiary { address: string; amount: string; } + +export interface IVotesCount { + ayes: number; + nays: number; + abstain?: number; +} diff --git a/src/ui-components/AccountSelectionForm.tsx b/src/ui-components/AccountSelectionForm.tsx index 8f31c4cad1..8c73344904 100644 --- a/src/ui-components/AccountSelectionForm.tsx +++ b/src/ui-components/AccountSelectionForm.tsx @@ -59,7 +59,7 @@ const AccountSelectionForm = ({ } }, [address]); return ( -
+

{title}

{!withoutInfo && ( diff --git a/src/ui-components/AddTags.tsx b/src/ui-components/AddTags.tsx index b5fe231144..42a638a170 100644 --- a/src/ui-components/AddTags.tsx +++ b/src/ui-components/AddTags.tsx @@ -2,8 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { InputRef, MenuProps, Tag, Input } from 'antd'; -import { Dropdown } from '~src/ui-components/Dropdown'; +import { InputRef, MenuProps, Tag, Input, Dropdown } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; import { IPostTag } from '~src/types'; import { PlusOutlined } from '@ant-design/icons'; @@ -12,6 +11,7 @@ import { poppins } from 'pages/_app'; import handleFilterResults from '~src/util/handleFilterResults'; import { useTheme } from 'next-themes'; import { NoTagFoundIcon } from './CustomIcons'; +import styled from 'styled-components'; interface Props { tags: string[]; @@ -140,71 +140,80 @@ const AddTags = ({ tags, setTags, className, disabled, onChange }: Props) => { }`} > ul]:w-[126px] ${ + theme == 'dark' + ? '[&>ul]:bg-section-dark-garyBackground [&>ul>li]:text-white [&>ul>.ant-dropdown-menu-item-selected]:bg-section-dark-garyBackground [&>ul>.ant-dropdown-menu-item-selected]:text-pink_primary hover:[&>ul>li]:bg-section-dark-garyBackground hover:[&>ul>li]:text-pink_secondary' + : '' + } z-[2000] `} menu={{ items }} placement='topLeft' + trigger={['click']} > -
- {inputVisible && !disabled - ? tags.length < 5 && ( - input]:dark:bg-section-dark-overlay [&>input]:dark:text-blue-dark-high`} - suffix={ - - - - } - /> - ) - : tags.length < 5 && - !disabled && ( - - - Add new tag - - )} -
- {tags.map((tag, index) => ( - { - e.preventDefault(); - handleClose(tag); - }} - > - {tag} - - ))} +
+
+ {inputVisible && !disabled + ? tags.length < 5 && ( + input]:dark:bg-section-dark-overlay [&>input]:dark:text-blue-dark-high`} + suffix={ + + + + } + /> + ) + : tags.length < 5 && + !disabled && ( + + + Add new tag + + )}
+ {tags.map((tag, index) => ( + { + e.preventDefault(); + handleClose(tag); + }} + > + {tag} + + ))}
- {!disabled &&
{5 - tags.length} Tags left
} + {!disabled &&
{5 - tags.length} Tags left
}
{charLimitReached &&

Character limit reached

}
); }; -export default AddTags; +export default styled(AddTags)` + .overlay-class { + width: 126px !important; + min-width: 0px !important; + } +`; diff --git a/src/ui-components/Address.tsx b/src/ui-components/Address.tsx index 1a09541483..0af1288213 100644 --- a/src/ui-components/Address.tsx +++ b/src/ui-components/Address.tsx @@ -63,6 +63,8 @@ interface Props { isVoterAddress?: boolean; disableTooltip?: boolean; showKiltAddress?: boolean; + destroyTooltipOnHide?: boolean; + inPostHeading?: boolean; } const shortenUsername = (username: string, usernameMaxLength?: number) => { @@ -94,7 +96,9 @@ const Address = (props: Props) => { ethIdenticonSize, isVoterAddress, disableTooltip = false, - showKiltAddress = false + showKiltAddress = false, + destroyTooltipOnHide = false, + inPostHeading } = props; const { network } = useNetworkSelector(); const apiContext = useContext(ApiContext); @@ -263,6 +267,7 @@ const Address = (props: Props) => { arrow color='#fff' overlayClassName={className} + destroyTooltipOnHide={destroyTooltipOnHide} title={ { ))} @@ -311,13 +316,13 @@ const Address = (props: Props) => { /> ))} -
+
handleClick(e)} title={mainDisplay || encodedAddr} className={`flex gap-x-1 ${ - usernameClassName ? usernameClassName : 'text-sm font-medium text-bodyBlue dark:text-blue-dark-high' - } hover:text-bodyBlue dark:text-blue-dark-high`} + usernameClassName ? usernameClassName : 'font-semibold text-bodyBlue dark:text-blue-dark-high' + } hover:text-bodyBlue dark:text-blue-dark-high ${inPostHeading ? 'text-xs' : 'text-sm'}`} > {!!addressPrefix && ( diff --git a/src/ui-components/AddressConnectModal.tsx b/src/ui-components/AddressConnectModal.tsx index 29b0ad682f..4ae7466c8a 100644 --- a/src/ui-components/AddressConnectModal.tsx +++ b/src/ui-components/AddressConnectModal.tsx @@ -446,7 +446,7 @@ const AddressConnectModal = ({
{canUsePolkasafe(network) && !showMultisig && usingMultisig && (
- OR + OR
For using {walletAlertTitle}:} description={ -
    +
    • Give access to Polkassembly on your selected wallet.
    • Add an address to the selected wallet.
    } showIcon - className='mt-4' + className='mt-4 dark:border-infoAlertBorderDark dark:bg-infoAlertBgDark' type='info' /> )} {Object.keys(availableWallets || {}).length === 0 && !loading && ( {accountAlertTitle}
} + message={
{accountAlertTitle}
} description={ -
+
{linkAddressNeeded ? 'No web 3 account integration could be found. To be able to use this feature, visit this page on a computer with polkadot-js extension.' : 'Please login with a web3 wallet to access this feature.'} @@ -505,7 +505,7 @@ const AddressConnectModal = ({ } type='info' showIcon - className='changeColor text-md mt-6 rounded-[4px] text-bodyBlue dark:text-blue-dark-high' + className='changeColor text-md mt-6 rounded-[4px] text-bodyBlue dark:border-infoAlertBorderDark dark:bg-infoAlertBgDark' /> )} Please select a wallet.} /> ) : null}
{isProposalCreation && availableBalance.lte(submissionDeposite.add(baseDeposit)) && ( + Please maintain minimum balance for these transactions: +
  • -
    Preimage Creation
    - +
    Preimage Creation
    + {formatedBalance(String(baseDeposit.toString()), unit)} {unit}
  • -
    Proposal Submission
    - +
    Proposal Submission
    + {formatedBalance(String(submissionDeposite.toString()), unit)} {unit}
  • diff --git a/src/ui-components/BalanceInput.tsx b/src/ui-components/BalanceInput.tsx index c004e176b0..83b7662996 100644 --- a/src/ui-components/BalanceInput.tsx +++ b/src/ui-components/BalanceInput.tsx @@ -37,6 +37,7 @@ interface Props { onBlur?: () => void; theme?: string; isBalanceUpdated?: boolean; + disabled?: boolean; } const BalanceInput = ({ @@ -57,7 +58,8 @@ const BalanceInput = ({ onBlur, // eslint-disable-next-line @typescript-eslint/no-unused-vars theme, - isBalanceUpdated + isBalanceUpdated, + disabled }: Props) => { const { network } = useNetworkSelector(); const unit = `${chainProperties[network].tokenSymbol}`; @@ -150,6 +152,7 @@ const BalanceInput = ({ placeholder={placeholder} value={formatedBalance(String(balance || ZERO_BN), unit)} size={size || 'middle'} + disabled={disabled} />
    diff --git a/src/ui-components/BeneficiariesListing/Beneficiary.tsx b/src/ui-components/BeneficiariesListing/Beneficiary.tsx index 866d824036..1616552339 100644 --- a/src/ui-components/BeneficiariesListing/Beneficiary.tsx +++ b/src/ui-components/BeneficiariesListing/Beneficiary.tsx @@ -12,9 +12,11 @@ import { formatedBalance } from '~src/util/formatedBalance'; interface Props { className?: string; beneficiary: IBeneficiary; + inPostHeading?: boolean; + disableBalanceFormatting?: boolean; } -const Beneficiary = ({ className, beneficiary }: Props) => { +const Beneficiary = ({ className, beneficiary, disableBalanceFormatting, inPostHeading }: Props) => { const { network } = useNetworkSelector(); return ( @@ -23,9 +25,11 @@ const Beneficiary = ({ className, beneficiary }: Props) => { displayInline iconSize={10} address={beneficiary.address} + inPostHeading={inPostHeading} /> - ({formatedBalance(beneficiary.amount.toString(), chainProperties[network]?.tokenSymbol, 2)} {chainProperties[network]?.tokenSymbol}) + ({disableBalanceFormatting ? beneficiary.amount.toString() : formatedBalance(beneficiary.amount.toString(), chainProperties[network]?.tokenSymbol, 2)}  + {chainProperties[network]?.tokenSymbol})
    ); diff --git a/src/ui-components/BeneficiariesListing/index.tsx b/src/ui-components/BeneficiariesListing/index.tsx index 1713d0a8b1..8788b03967 100644 --- a/src/ui-components/BeneficiariesListing/index.tsx +++ b/src/ui-components/BeneficiariesListing/index.tsx @@ -6,19 +6,27 @@ import React from 'react'; import { IBeneficiary } from '~src/types'; import Beneficiary from './Beneficiary'; import { Popover } from 'antd'; +import { BeneficiaryIcon, BeneficiaryGreyIcon } from '../CustomIcons'; +import { useTheme } from 'next-themes'; interface Props { className?: string; beneficiaries?: IBeneficiary[]; + inPostHeading?: boolean; } -const BeneficiariesListing = ({ className, beneficiaries }: Props) => { +const BeneficiariesListing = ({ className, beneficiaries, inPostHeading }: Props) => { + const { resolvedTheme: theme } = useTheme(); if (!beneficiaries || beneficiaries.length === 0) return null; return (
    - Beneficiary: - + {theme === 'dark' ? : } + Beneficiary: + {beneficiaries.length > 1 && ( & diff --git a/src/ui-components/CommentHistoryModal.tsx b/src/ui-components/CommentHistoryModal.tsx index 41a51551e9..36bc9852d9 100644 --- a/src/ui-components/CommentHistoryModal.tsx +++ b/src/ui-components/CommentHistoryModal.tsx @@ -111,7 +111,7 @@ const CommentHistoryModal = ({ className, open, setOpen, history, defaultAddress className='flex items-center justify-center text-lg text-white min-[320px]:mr-2' theme={theme} > - {getSentimentIcon(item.sentiment as ESentiment, theme || '')} + <>{getSentimentIcon(item.sentiment as ESentiment, theme || '')}
    diff --git a/src/ui-components/CreationLabel.tsx b/src/ui-components/CreationLabel.tsx index 62a12d4ff5..9c239e5e84 100644 --- a/src/ui-components/CreationLabel.tsx +++ b/src/ui-components/CreationLabel.tsx @@ -22,9 +22,10 @@ import { ESentiment, EVoteDecisionType, IBeneficiary } from '~src/types'; import { DislikeFilled, LikeFilled } from '@ant-design/icons'; import AbstainGray from '~assets/icons/abstainGray.svg'; import SplitYellow from '~assets/icons/split-yellow-icon.svg'; -import { parseBalance } from '~src/components/Post/GovernanceSideBar/Modal/VoteData/utils/parseBalaceToReadable'; -import { useNetworkSelector } from '~src/redux/selectors'; import { useTheme } from 'next-themes'; +import { usePostDataContext } from '~src/context'; +import { getVotingTypeFromProposalType } from '~src/global/proposalType'; +import VoteList from '~src/components/Post/GovernanceSideBar/Modal/VoteData/VoteList'; import BeneficiariesListing from './BeneficiariesListing'; const Styled = styled.div` @@ -57,6 +58,7 @@ const Styled = styled.div` font-size: 10px; padding: 0px 6px; } + .dark-pink { color: #e5007a; text-decoration: underline; @@ -67,6 +69,7 @@ interface ICreationLabelProps { children?: ReactNode; created_at?: Date; defaultAddress?: string | null; + voterAddress?: string | null; text?: string; topic?: string; username?: string; @@ -80,6 +83,7 @@ interface ICreationLabelProps { isRow?: boolean; voteData?: any; beneficiaries?: IBeneficiary[]; + inPostHeading?: boolean; } const CreationLabel: FC = (props) => { @@ -99,13 +103,17 @@ const CreationLabel: FC = (props) => { truncateUsername, vote, votesArr = [], - isRow + isRow, + voterAddress, + inPostHeading } = props; const relativeCreatedAt = getRelativeCreatedAt(created_at); const [showVotesModal, setShowVotesModal] = useState(false); - const { network } = useNetworkSelector(); + const handleContentClick = (e: React.MouseEvent) => { + e.stopPropagation(); + }; const { resolvedTheme: theme } = useTheme(); - + const { postData } = usePostDataContext(); const getSentimentLabel = (sentiment: ESentiment) => { return
    {getSentimentTitle(sentiment)}
    ; }; @@ -117,110 +125,12 @@ const CreationLabel: FC = (props) => { } ]; - const AbstainDetailsComponent = ({ network, vote, power }: any) => { - return ( - <> -
    - {parseBalance((vote?.balance?.abstain || 0).toString(), 2, true, network)} -
    -
    -
    -
    {power}
    - - ); - }; - - const AyeNyeDetailsComponent = ({ network, vote, power }: any) => { - return ( - <> -
    - {parseBalance((vote?.balance?.value || 0).toString(), 2, true, network)} -
    -
    {`${ - vote.lockPeriod === 0 ? '0.1' : vote.lockPeriod - }x`}
    -
    {power}
    - - ); - }; - const SplitDetailsComponent = ({ network, vote, power }: any) => { - return ( - <> -
    - {parseBalance((vote?.decision === 'abstain' ? vote?.balance?.abstain || 0 : vote?.balance?.value || 0).toString(), 2, true, network)} -
    - {vote?.decision === 'abstain' && ( -
    -
    - )} -
    {power}
    - - ); - }; - - const renderVoteContent = (vote: any, network: any, idx: number) => { - const lockPeriod = vote.lockPeriod === 0 || vote?.decision === 'abstain' ? 0.1 : vote.lockPeriod; - const value = vote?.decision === 'abstain' ? BigInt(vote?.balance?.abstain || 0) : BigInt(vote?.balance?.value || 0); - const powerValue = lockPeriod === 0.1 ? value / BigInt(10) : value * BigInt(lockPeriod); - const power = parseBalance(powerValue.toString(), 2, true, network); - - return ( -
    - {vote.decision == 'yes' ? ( -
    -
    - Aye - -
    -
    - ) : vote.decision == 'no' ? ( -
    -
    - Nay - -
    -
    - ) : vote.decision == 'abstain' && !(vote.balance as any).abstain ? ( -
    -
    - Split - -
    -
    - ) : vote.decision == 'abstain' && (vote.balance as any).abstain ? ( -
    -
    - Abstain - -
    -
    - ) : null} -
    - ); - }; - return (
    -
    +
    + {inPostHeading && Proposer:} = (props) => { /> {text}  {topic && ( -
    +
    in{' '} = (props) => { className={`md:inline-block ${!isRow ? 'hidden' : 'inline-block'} border-lightBlue dark:border-icon-dark-inactive max-sm:hidden`} type='vertical' /> - + )} {cid ? ( @@ -264,19 +177,23 @@ const CreationLabel: FC = (props) => { ) : null}
    -
    - {(topic || text || created_at) && ( - <> -   - - +
    + {!inPostHeading && ( +
    + {(topic || text || created_at) && ( + <> +   + + + )} +
    )} {created_at && ( - - + + {relativeCreatedAt} )} @@ -290,7 +207,7 @@ const CreationLabel: FC = (props) => { /> {vote === EVoteDecisionType.AYE ? (

    - Voted {vote} + Voted {vote}

    ) : vote === EVoteDecisionType.NAY ? (
    @@ -302,7 +219,7 @@ const CreationLabel: FC = (props) => {
    ) : vote === EVoteDecisionType.ABSTAIN ? (
    - Voted {vote} + Voted {vote}
    ) : null}
    @@ -342,7 +259,7 @@ const CreationLabel: FC = (props) => { open={showVotesModal} onCancel={() => setShowVotesModal(false)} footer={false} - className={`${poppins.variable} ${poppins.className} max-h-[675px] rounded-[6px] max-md:w-full dark:[&>.ant-modal-content]:bg-section-dark-overlay`} + className={`${poppins.variable} ${poppins.className} max-h-[675px] w-[595px] rounded-[6px] max-md:w-full dark:[&>.ant-modal-content]:bg-section-dark-overlay`} closeIcon={} wrapClassName={`${className} dark:bg-modalOverlayDark`} title={ @@ -351,30 +268,13 @@ const CreationLabel: FC = (props) => {
    } > -
    -
    -

    Vote

    -

    Amount

    -

    Conviction

    -

    Voting Power

    -
    -
    - {votesArr.length > 0 && - votesArr.slice(0, 1).map((vote: any, idx: any) => { - return renderVoteContent(vote, network, idx); - })} -
    - {votesArr.length > 1 && ( -
    -
    -

    Vote History

    -
    - )} - {votesArr.length > 1 && - votesArr.slice(1).map((vote: any, idx: any) => { - return renderVoteContent(vote, network, idx); - })} -
    +
    +
    @@ -459,4 +359,16 @@ export default styled(CreationLabel)` left: 178px !important; } } + + @media (max-width: 468px) and (min-width: 319px) { + .topic-container { + margin-top: 8px; + } + } + + @media (max-width: 768px) and (min-width: 319px) { + .details-container { + margin-top: -4px !important; + } + } `; diff --git a/src/ui-components/CustomIcons.tsx b/src/ui-components/CustomIcons.tsx index ec1e7ef45e..32b5bd6f2e 100644 --- a/src/ui-components/CustomIcons.tsx +++ b/src/ui-components/CustomIcons.tsx @@ -134,6 +134,8 @@ import DeleteIconSVG from '~assets/icons/deleteIcon.svg'; import WhiteDeleteIconSVG from '~assets/icons/deleteWhiteIcon.svg'; import PolkaverseSVG from '~assets/icons/SubsocialIcon.svg'; import VerifiedSVG from '~assets/icons/verified-tick.svg'; +import BeneficiarySVG from '~assets/icons/Beneficiary.svg'; +import BeneficiaryGreySVG from '~assets/icons/BeneficiaryGrey.svg'; export const PolkasafeWhiteIcon = (props: Partial) => ( ) => ( {...props} /> ); + +export const BeneficiaryIcon = (props: Partial) => ( + +); + +export const BeneficiaryGreyIcon = (props: Partial) => ( + +); diff --git a/src/ui-components/Dropdown/index.tsx b/src/ui-components/Dropdown/index.tsx index cfef0bc9ea..f3d51b4c76 100644 --- a/src/ui-components/Dropdown/index.tsx +++ b/src/ui-components/Dropdown/index.tsx @@ -8,7 +8,7 @@ export const Dropdown = (props: any) => { return ( ul>li]:text-pink_primary ${ props.theme == 'dark' ? '[&>ul]:bg-section-dark-garyBackground [&>ul>li]:text-white [&>ul>.ant-dropdown-menu-item-selected]:bg-section-dark-garyBackground [&>ul>.ant-dropdown-menu-item-selected]:text-pink_primary hover:[&>ul>li]:bg-section-dark-garyBackground hover:[&>ul>li]:text-pink_secondary' : '' diff --git a/src/ui-components/ErrorAlert.tsx b/src/ui-components/ErrorAlert.tsx index d7679539a9..ac68582e0e 100644 --- a/src/ui-components/ErrorAlert.tsx +++ b/src/ui-components/ErrorAlert.tsx @@ -15,7 +15,7 @@ const ErrorAlert = ({ className, errorMsg }: Props) => { ); }; diff --git a/src/ui-components/FilteredError.tsx b/src/ui-components/FilteredError.tsx index 1e7f24e224..7ea7a9246d 100644 --- a/src/ui-components/FilteredError.tsx +++ b/src/ui-components/FilteredError.tsx @@ -13,8 +13,8 @@ const FilteredError = ({ className, text }: ErrorProps) => { return (
    {text}} type='error' />
    diff --git a/src/ui-components/Loader.tsx b/src/ui-components/Loader.tsx index 82769d94ce..d8a2b4b18d 100644 --- a/src/ui-components/Loader.tsx +++ b/src/ui-components/Loader.tsx @@ -12,8 +12,9 @@ interface Props { timeout?: number; timeoutText?: string; size?: 'default' | 'small' | 'large'; + iconClassName?: string; } -const Loader = ({ className, timeout, text, timeoutText = 'Process timeout', size = 'default' }: Props) => { +const Loader = ({ className, timeout, text, timeoutText = 'Process timeout', size = 'default', iconClassName }: Props) => { const [displayLoader, setDisplayLoader] = useState(true); useEffect(() => { @@ -35,13 +36,13 @@ const Loader = ({ className, timeout, text, timeoutText = 'Process timeout', siz } + indicator={} /> ) : ( {timeoutText}} /> )}
    diff --git a/src/ui-components/LoginSuccessModal.tsx b/src/ui-components/LoginSuccessModal.tsx index 445176f746..a552b70b1c 100644 --- a/src/ui-components/LoginSuccessModal.tsx +++ b/src/ui-components/LoginSuccessModal.tsx @@ -18,11 +18,12 @@ import nextApiClientFetch from '~src/util/nextApiClientFetch'; import { IAddProfileResponse } from '~src/auth/types'; import { handleTokenChange } from '~src/services/auth.service'; import { useDispatch } from 'react-redux'; - +import styled from 'styled-components'; interface Props { // setLoading: (pre: boolean) => void; setLoginOpen?: (pre: boolean) => void; setSignupOpen?: (pre: boolean) => void; + theme?: string; } const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => { @@ -36,6 +37,8 @@ const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => { const currentUser = useUserDetailsSelector(); const dispatch = useDispatch(); const [loading, setLoading] = useState(false); + const [firstPassword, setFirstPassword] = useState(''); + const { password } = validation; const validateUsername = (optionalUsername: string) => { let errorUsername = 0; @@ -80,6 +83,7 @@ const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => { custom_username: true, email: email || '', image: currentUser.picture || '', + password: firstPassword || '', social_links: JSON.stringify([]), title: '', user_id: Number(currentUser.id), @@ -172,15 +176,15 @@ const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => {
    {!isError ? ( You can update your username from the settings page.} type='info' showIcon /> ) : ( Username already exists. Please try again } type='error' showIcon /> @@ -242,17 +246,48 @@ const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => { />
    +
    + + + { + setFirstPassword(e.target.value); + }} + disabled={loading} + placeholder='Password' + className='rounded-md px-4 py-2 dark:border-[#3B444F] dark:bg-transparent dark:text-blue-dark-high dark:focus:border-[#91054F] dark:[&>input]:bg-transparent dark:[&>input]:text-white' + id='first_password' + /> + +
    {!emailError ? ( You can set your email later from the settings page.} type='info' showIcon /> ) : ( Email already exists. Please use a different email or link your address with the existing account.} type='error' showIcon /> @@ -263,7 +298,7 @@ const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => { className='-mt-6 mb-5 dark:bg-separatorDark' />
    - {!email && ( + {!email && !firstPassword && ( )} - {email && ( + {(email || firstPassword) && ( @@ -290,4 +326,8 @@ const LoginSuccessModal = ({ setLoginOpen, setSignupOpen }: Props) => { ); }; -export default LoginSuccessModal; +export default styled(LoginSuccessModal)` + #first_password { + color: ${(props) => (props.theme == 'dark' ? 'white' : '')} !important; + } +`; diff --git a/src/ui-components/MultisigAccountSelectionForm.tsx b/src/ui-components/MultisigAccountSelectionForm.tsx index de1cfc1b45..29d49e32f6 100644 --- a/src/ui-components/MultisigAccountSelectionForm.tsx +++ b/src/ui-components/MultisigAccountSelectionForm.tsx @@ -182,12 +182,12 @@ export default MultisigAccountSelectionForm; const MultisigNotFound = () => ( +
    No multisig account found.
    } description={ -
    + ), link diff --git a/src/ui-components/OnchainCreationLabel.tsx b/src/ui-components/OnchainCreationLabel.tsx index 750bea6df1..81ead3be81 100644 --- a/src/ui-components/OnchainCreationLabel.tsx +++ b/src/ui-components/OnchainCreationLabel.tsx @@ -90,7 +90,7 @@ const OnchainCreationLabel = ({ address, username, truncateUsername, className } setOpen(e); }} > - + social.type === 'Twitter')?.link || '' }, - { isVerified: false, key: 'Telegram', value: socials?.find((social) => social.type === 'Telegram')?.link || '' } + { isVerified: !!identity?.twitter && isGood, key: 'Twitter', value: identity?.twitter || socials?.find((social) => social.type === 'Twitter')?.link || '' }, + { isVerified: false, key: 'Telegram', value: socials?.find((social) => social.type === 'Telegram')?.link || '' }, + { isVerified: !!identity?.email && isGood, key: 'Email', value: identity?.email || socials?.find((social) => social.type === 'Email')?.link || '' }, + { isVerified: !!identity?.riot && isGood, key: 'Riot', value: identity?.riot || socials?.find((social) => social.type === 'Riot')?.link || '' } ]; const color: 'brown' | 'green' | 'grey' = isGood ? 'green' : isBad ? 'brown' : 'grey'; const success = () => { @@ -114,7 +116,7 @@ const QuickView = ({ />
    - {username?.length > 20 ? `${username?.slice(0, 20)}...` : username} + {username?.length > 15 ? `${username?.slice(0, 15)}...` : username}
    {isGood ? : }
    diff --git a/src/ui-components/ReferendaLoginPrompts.tsx b/src/ui-components/ReferendaLoginPrompts.tsx index dea705158c..b62fe366a0 100644 --- a/src/ui-components/ReferendaLoginPrompts.tsx +++ b/src/ui-components/ReferendaLoginPrompts.tsx @@ -91,6 +91,5 @@ export default styled(ReferendaLoginPrompts)` .text .ant-modal-content { color: var(--bodyBlue) !important; border-radius: 4px !important; - background: ${(props) => (props.theme == 'dark' ? '#0D0D0D' : 'white')} !important; } `; diff --git a/src/ui-components/SearchBar.tsx b/src/ui-components/SearchBar.tsx index 5b828ad858..a1d435a1c3 100644 --- a/src/ui-components/SearchBar.tsx +++ b/src/ui-components/SearchBar.tsx @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { SearchOutlined } from '@ant-design/icons'; -import React, { FC, useState } from 'react'; +import React, { FC, useEffect, useState } from 'react'; import styled from 'styled-components'; import ClientOnly, { Search } from './ClientOnly'; import NewSearch from 'src/components/Search'; @@ -26,6 +26,18 @@ const SearchBar: FC = (props) => { const [isSuperSearch, setIsSuperSearch] = useState(false); const { resolvedTheme: theme } = useTheme(); + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key === 'k') { + setOpen((prev) => !prev); + } + }; + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [open]); + return allowedNetwork.includes(network?.toUpperCase()) ? (
    {isSmallScreen ? ( diff --git a/src/ui-components/SetIdentityNudge.tsx b/src/ui-components/SetIdentityNudge.tsx new file mode 100644 index 0000000000..4dad83298b --- /dev/null +++ b/src/ui-components/SetIdentityNudge.tsx @@ -0,0 +1,57 @@ +// Copyright 2019-2025 @polkassembly/polkassembly authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. +import React, { useState, useEffect } from 'react'; +import { CloseIcon } from '~src/ui-components/CustomIcons'; +import ShieldUserIcon from '~assets/icons/shield-user-icon.svg'; + +interface Props { + isIdentitySet: boolean; + handleSetIdentityClick: () => void; +} + +const SetIdentityNudge = ({ isIdentitySet, handleSetIdentityClick }: Props) => { + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + const nudgeStatus = localStorage.getItem('identityNudgeStatus'); + + if (nudgeStatus !== 'viewed') { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, []); + + function handleNudgeClose() { + localStorage.setItem('identityNudgeStatus', 'viewed'); + setIsOpen(false); + } + + if (!isOpen) return null; + + return ( +
    +
    +
    +
    + {isIdentitySet ? 'Identity has not been verified yet' : 'Identity has not been set yet'} + + {isIdentitySet ? 'Verify on-chain identity' : 'Set on-chain identity'} + +
    + + + +
    +
    + ); +}; + +export default SetIdentityNudge; diff --git a/src/ui-components/TagsModal.tsx b/src/ui-components/TagsModal.tsx new file mode 100644 index 0000000000..37ef47c2ba --- /dev/null +++ b/src/ui-components/TagsModal.tsx @@ -0,0 +1,81 @@ +// Copyright 2019-2025 @polkassembly/polkassembly authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. +import { Divider, Modal } from 'antd'; +import React, { FC } from 'react'; +import { CloseIcon } from './CustomIcons'; +import { poppins } from 'pages/_app'; +import { useTheme } from 'next-themes'; +import TagsIcon from '~assets/icons/tags-icon.svg'; +import TagsWhiteIcon from '~assets/icons/tags-white-icon.svg'; +import { useRouter } from 'next/router'; +import { onTagClickFilter } from '~src/util/onTagClickFilter'; + +interface ITagsModalProps { + className?: string; + tags?: any; + track_name?: any; + proposalType?: any; + openTagsModal?: boolean; + setOpenTagsModal?: (pre: boolean) => void; +} +const TagsModal: FC = (props) => { + const { tags, track_name, proposalType, openTagsModal, setOpenTagsModal } = props; + const router = useRouter(); + const { resolvedTheme: theme } = useTheme(); + const handleTagClick = (pathname: string, filterBy: string) => { + if (pathname) + router.replace({ + pathname: `/${pathname}`, + query: { + filterBy: encodeURIComponent(JSON.stringify([filterBy])) + } + }); + }; + return ( +
    + { + e.stopPropagation(); + e.preventDefault(); + setOpenTagsModal?.(false); + }} + footer={false} + closeIcon={} + className={`${poppins.variable} ${poppins.className} ant-modal-content>.ant-modal-header]:bg-section-dark-overlay h-[120px] max-w-full shrink-0 max-sm:w-[100%]`} + title={ + <> + + + + } + > +
    + {tags && tags.length > 0 && ( + <> + {tags?.map((tag: any, index: number) => ( +
    handleTagClick(onTagClickFilter(proposalType, track_name || ''), tag)} + > + {tag} +
    + ))} + + )} +
    +
    +
    + ); +}; + +export default TagsModal; diff --git a/src/ui-components/TextEditor.tsx b/src/ui-components/TextEditor.tsx index 381425d853..ddaed59e66 100644 --- a/src/ui-components/TextEditor.tsx +++ b/src/ui-components/TextEditor.tsx @@ -36,7 +36,7 @@ interface ITextEditorProps { const gifSVGData = ` +fill-rule="evenodd" stroke="none">