Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add support us modal #1620

Merged
merged 12 commits into from
Nov 2, 2024
6 changes: 5 additions & 1 deletion packages/extension-polkagate/src/components/TwoButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface Props {
onPrimaryClick: React.MouseEventHandler<HTMLButtonElement>;
secondaryBtnText?: string;
onSecondaryClick: React.MouseEventHandler<HTMLButtonElement>;
primaryBtnStartIcon?: React.JSX.Element;
secondaryBtnStartIcon?: React.JSX.Element;
mt?: string;
ml?: string;
disabled?: boolean;
Expand All @@ -25,7 +27,7 @@ interface Props {
}
// TODO: can replace ButtonWithCancel later

export default function TwoButtons ({ disabled = false, isBusy = false, ml = '6%', mt, onPrimaryClick, onSecondaryClick, primaryBtnText, secondaryBtnText, variant = 'outlined', width = '88%' }: Props): React.ReactElement<Props> {
export default function TwoButtons ({ disabled = false, isBusy = false, ml = '6%', mt, onPrimaryClick, onSecondaryClick, primaryBtnStartIcon, primaryBtnText, secondaryBtnStartIcon, secondaryBtnText, variant = 'outlined', width = '88%' }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const theme = useTheme();

Expand All @@ -35,6 +37,7 @@ export default function TwoButtons ({ disabled = false, isBusy = false, ml = '6%
<Button
disabled={isBusy}
onClick={onSecondaryClick}
startIcon={secondaryBtnStartIcon}
sx={{
borderColor: 'secondary.main',
color: variant === 'text' ? 'secondary.main' : 'text.primary',
Expand All @@ -59,6 +62,7 @@ export default function TwoButtons ({ disabled = false, isBusy = false, ml = '6%
: <Button
disabled={disabled}
onClick={onPrimaryClick}
startIcon={primaryBtnStartIcon}
sx={{
borderColor: 'secondary.main',
borderRadius: '5px',
Expand Down
126 changes: 126 additions & 0 deletions packages/extension-polkagate/src/fullscreen/governance/SupportUs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

/* eslint-disable react/jsx-max-props-per-line */

import { faHandshakeAngle } from '@fortawesome/free-solid-svg-icons';
import ThumbUpIcon from '@mui/icons-material/ThumbUp';
import WatchLaterIcon from '@mui/icons-material/WatchLater';
import { Box, Grid, Typography } from '@mui/material';
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';

import { BN, BN_ZERO } from '@polkadot/util';

import { AccountsAssetsContext, TwoButtons } from '../../components';
import { getStorage, setStorage } from '../../components/Loading';
import { useMyVote, useTranslation } from '../../hooks';
import { tieAccount } from '../../messaging';
import { POLKADOT_GENESIS_HASH } from '../../util/constants';
import { openOrFocusTab } from '../accountDetails/components/CommonTasks';
import SimpleModalTitle from '../partials/SimpleModalTitle';
import { DraggableModal } from './components/DraggableModal';

const PROPOSAL_NO = 1264;
const TRACK_ID = 33;
const SHOW_INTERVAL = 10 * 1000; // ms
const STORAGE_LABEL = `polkaGateVoteReminderLastShown_${PROPOSAL_NO}`;

export default function SupportUs (): React.ReactElement {
const { t } = useTranslation();
const { accountsAssets } = useContext(AccountsAssetsContext);

const [open, setOpen] = useState<boolean>(true);
const [maxPowerAddress, setAddress] = useState<string>();
const [timeToShow, setTimeToShow] = useState<boolean>();
const vote = useMyVote(maxPowerAddress, PROPOSAL_NO, TRACK_ID);
const notVoted = useMemo(() => vote === null || (vote && !('standard' in vote || 'splitAbstain' in vote || ('delegating' in vote && vote?.delegating?.voted))), [vote]);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
getStorage(STORAGE_LABEL).then((maybeDate) => {
(!maybeDate || Date.now() - (maybeDate as unknown as number) > SHOW_INTERVAL) && setTimeToShow(true);
}).catch(console.error);
}, [accountsAssets]);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
useEffect(() => {
if (!accountsAssets) {
return;
}

const balances = accountsAssets.balances;
let addressWithMaxVotingPower: string | undefined;
let max = BN_ZERO;

Object.keys(balances).forEach((address) => {
const maybeAsset = balances[address]?.[POLKADOT_GENESIS_HASH];

if (!maybeAsset) {
return;
}

const votingBalance = maybeAsset[0].votingBalance ? new BN(maybeAsset[0].votingBalance) : BN_ZERO;

if (votingBalance.gt(max)) {
max = votingBalance;
addressWithMaxVotingPower = address;
}
});

addressWithMaxVotingPower && tieAccount(addressWithMaxVotingPower, POLKADOT_GENESIS_HASH).finally(() => {
setAddress(addressWithMaxVotingPower);
}).catch(console.error);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
}, [accountsAssets]);

const handleClose = useCallback(() => {
setOpen(false);
}, []);

const handleOnVote = useCallback(() => {
maxPowerAddress && openOrFocusTab(`/governance/${maxPowerAddress}/referenda/${PROPOSAL_NO}`);
}, [maxPowerAddress]);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

const handleMaybeLater = useCallback(() => {
setStorage(STORAGE_LABEL, Date.now()).catch(console.error);
setOpen(false);
}, []);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

return (
<>
{maxPowerAddress && timeToShow && notVoted &&
<DraggableModal onClose={handleClose} open={open}>
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
<>
<SimpleModalTitle
icon={faHandshakeAngle}
onClose={handleClose}
title={t('Support PolkaGate!')}
/>
<Grid item sx={{ bgcolor: 'background.paper', border: 1, borderColor: 'divider', borderRadius: '5px', mt: 5, p: '10px' }}>
<Typography fontSize='14px' lineHeight='25px' textAlign='left'>
{t('We’re seeking retroactive funding to sustain and expand PolkaGate’s impact! Your vote can empower us to continue delivering valuable improvements and innovations. Voting won’t spend your tokens—they’ll just be temporarily locked based on your chosen conviction level.')}
</Typography>
</Grid>
<Box
alt='Description of the image'
component='img'
src='/images/supportUs.webp'
sx={{
width: '60%',
height: 'auto',
borderRadius: 1
}}
/>
<TwoButtons
ml='0'
onPrimaryClick={handleOnVote}
onSecondaryClick={handleMaybeLater}
primaryBtnStartIcon={<ThumbUpIcon />}
primaryBtnText={t('Vote to Support Us')}
secondaryBtnStartIcon={<WatchLaterIcon />}
secondaryBtnText={t('Maybe Later')}
width='88%'
/>
</>
</DraggableModal>
}
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0
// @ts-nocheck

//@ts-nocheck

Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
import type { ApiPromise } from '@polkadot/api';
import type { AccountId32 } from '@polkadot/types/interfaces/runtime';
import type { PalletConvictionVotingVoteVoting } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import type { Track } from '../../utils/types';

import { ApiPromise } from '@polkadot/api';
import { BN } from '@polkadot/util';

import { Track } from '../../utils/types';
import { toCamelCase } from '../../utils/util';

export const CONVICTION = {
Expand Down Expand Up @@ -71,8 +71,8 @@ interface Voting {
delegating: any; // needs to be fixed
}

export async function getAddressVote(address: string, api: ApiPromise, referendumIndex: number, trackId: number): Promise<Vote | null> {
const voting = await api.query.convictionVoting.votingFor(address, trackId) as unknown as PalletConvictionVotingVoteVoting;
export async function getAddressVote (address: string, api: ApiPromise, referendumIndex: number, trackId: number): Promise<Vote | null> {
const voting = await api.query['convictionVoting']['votingFor'](address, trackId) as unknown as PalletConvictionVotingVoteVoting;
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

if (voting.isEmpty) {
return null;
Expand All @@ -96,7 +96,7 @@ export async function getAddressVote(address: string, api: ApiPromise, referendu
if (voting.isDelegating) {
// Then, look into the votes of the delegating target address.
const { conviction, target } = voting.asDelegating;
const proxyVoting = await api.query.convictionVoting.votingFor(target, trackId) as unknown as PalletConvictionVotingVoteVoting;
const proxyVoting = await api.query['convictionVoting']['votingFor'](target, trackId) as unknown as PalletConvictionVotingVoteVoting;
const targetVote = proxyVoting.isCasting ? proxyVoting.asCasting.votes.find(([index]) => index.toNumber() === referendumIndex)?.[1] : undefined;

if (!targetVote?.isStandard && !targetVote?.isSplitAbstain) {
Expand Down Expand Up @@ -139,8 +139,8 @@ export async function getAddressVote(address: string, api: ApiPromise, referendu
return null;
}

export async function getAllVotes(address: string, api: ApiPromise, tracks: Track[]): Promise<number[] | null> {
const queries = tracks.map((t) => api.query.convictionVoting.votingFor(address, t[0]));
export async function getAllVotes (address: string, api: ApiPromise, tracks: Track[]): Promise<number[] | null> {
const queries = tracks.map((t) => api.query['convictionVoting']['votingFor'](address, t[0]));
const voting = await Promise.all(queries);
const castedRefIndexes = voting?.map((v) => {
const jsonV = v.toJSON() as unknown as Voting;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AccountContext, ActionContext } from '../../components';
import { useAccountsOrder, useAlerts, useFullscreen, useProfileAccounts, useTranslation } from '../../hooks';
import { AddNewAccountButton } from '../../partials';
import FullScreenHeader from '../governance/FullScreenHeader';
import SupportUs from '../governance/SupportUs';
import HeaderComponents from './components/HeaderComponents';
import DraggableAccountsList from './partials/DraggableAccountList';
import HomeMenu from './partials/HomeMenu';
Expand Down Expand Up @@ -89,6 +90,7 @@ function HomePageFullScreen (): React.ReactElement {
</Grid>
</Grid>
</Grid>
<SupportUs />
</Grid>
);
}
Expand Down
16 changes: 9 additions & 7 deletions packages/extension-polkagate/src/hooks/useMyVote.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0
// @ts-nocheck

import type React from 'react';
import type { Vote } from '../fullscreen/governance/post/myVote/util';

import { useCallback, useEffect, useState } from 'react';

import { getAddressVote, Vote } from '../fullscreen/governance/post/myVote/util';
import { useApi, useFormatted } from '.';
import { getAddressVote } from '../fullscreen/governance/post/myVote/util';
import { useInfo } from '.';

export default function useMyVote(
export default function useMyVote (
address: string | undefined,
refIndex: number | string | undefined,
trackId: number | string | undefined,
refresh?: boolean,
setRefresh?: React.Dispatch<React.SetStateAction<boolean>>
): Vote | null | undefined {
const api = useApi(address);
const formatted = useFormatted(address);
const { api, formatted } = useInfo(address);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

const [vote, setVote] = useState<Vote | null | undefined>();

const fetchVote = useCallback(async () => {
Expand All @@ -36,7 +38,7 @@ export default function useMyVote(
}, [fetchVote]);

useEffect(() => {
refresh && fetchVote();
refresh && fetchVote().catch(console.error);
}, [fetchVote, refresh]);

return vote;
Expand Down
Binary file added packages/extension/public/images/supportUs.webp
Binary file not shown.
6 changes: 5 additions & 1 deletion packages/extension/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1411,5 +1411,9 @@
"Not recoverable": "",
"No proxy": "",
"Switch Theme": "",
"Account Icon": ""
"Account Icon": "",
"Support PolkaGate!": "",
"We’re seeking retroactive funding to sustain and expand PolkaGate’s impact! Your vote can empower us to continue delivering valuable improvements and innovations. Voting won’t spend your tokens—they’ll just be temporarily locked based on your chosen conviction level.": "",
"Vote to Support Us": "",
"Maybe Later": ""
}
Loading