Skip to content

Commit

Permalink
feat: auto loading referenda list (#1599)
Browse files Browse the repository at this point in the history
* feat: auto loading referenda list

* chore: adjust loading text

* chore: increasing LATEST_REFERENDA_LIMIT_TO_LOAD_PER_REQUEST
  • Loading branch information
Nick-1979 authored Oct 24, 2024
1 parent ada7f21 commit c987138
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,11 @@ function InternetConnectivity (): React.ReactElement {

useEffect(() => {
checkInternetAccess().then((_isOnline) => {
console.log('internet check result:', _isOnline);

setIsOnline(_isOnline);
}).catch(console.error);

const intervalId = setInterval(() => {
checkInternetAccess().then((_isOnline) => {
console.log('internet check result:', _isOnline);

setIsOnline(_isOnline);
}).catch(console.error);
}, CHECK_INTERNET_CONNECTIVITY_PERIOD);
Expand Down
90 changes: 71 additions & 19 deletions packages/extension-polkagate/src/fullscreen/governance/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

// @ts-nocheck

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

import type { u32 } from '@polkadot/types-codec';
import type { LatestReferenda } from './utils/types';

import { Container, Grid, Typography, useTheme } from '@mui/material';
// @ts-ignore
import { CubeGrid } from 'better-react-spinkit';
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { useParams } from 'react-router';
import { useLocation } from 'react-router-dom';

import { ReferendaContext, Warning } from '../../components';
import { useApi, useChain, useChainName, useDecidingCount, useFullscreen, useTracks, useTranslation } from '../../hooks';
import { useDecidingCount, useFullscreen, useInfo, useTracks, useTranslation } from '../../hooks';
import { GOVERNANCE_CHAINS } from '../../util/constants';
import HorizontalWaiting from './components/HorizontalWaiting';
import { getAllVotes } from './post/myVote/util';
Expand All @@ -33,22 +33,22 @@ export type Fellowship = [string, number];

export default function Governance (): React.ReactElement {
useFullscreen();
const { t } = useTranslation();
const { state } = useLocation();
const theme = useTheme();
const { t } = useTranslation();
const { state } = useLocation() as unknown as {state: {selectedSubMenu?: string}};
const { address, topMenu } = useParams<{ address: string, topMenu: 'referenda' | 'fellowship' }>();
const api = useApi(address);
const chain = useChain(address);
const chainName = useChainName(address);

const { api, chain, chainName } = useInfo(address);
const decidingCounts = useDecidingCount(address);
const chainChangeRef = useRef('');
const refsContext = useContext(ReferendaContext);

const { fellowshipTracks, tracks } = useTracks(address);

const pageTrackRef = useRef({ listFinished: false, page: 1, subMenu: 'All', topMenu });

const [menuOpen, setMenuOpen] = useState(false);
const [selectedSubMenu, setSelectedSubMenu] = useState<string>(state?.selectedSubMenu as string || 'All');
const [selectedSubMenu, setSelectedSubMenu] = useState<string>(state?.selectedSubMenu || 'All');
const [referendumCount, setReferendumCount] = useState<{ referenda: number | undefined, fellowship: number | undefined }>({ fellowship: undefined, referenda: undefined });
const [referenda, setReferenda] = useState<LatestReferenda[] | null>();
const [filteredReferenda, setFilteredReferenda] = useState<LatestReferenda[] | null>();
Expand Down Expand Up @@ -77,7 +77,7 @@ export default function Governance (): React.ReactElement {
fetchJson();
}, []);

const referendaTrackId = tracks?.find((t) => String(t[1].name) === selectedSubMenu.toLowerCase().replace(' ', '_'))?.[0]?.toNumber() as number;
const referendaTrackId = tracks?.find((t) => String(t[1].name) === selectedSubMenu.toLowerCase().replace(' ', '_'))?.[0]?.toNumber()!;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const currentTrack = useMemo(() => {
if (!tracks && !fellowshipTracks) {
Expand Down Expand Up @@ -131,7 +131,7 @@ export default function Governance (): React.ReactElement {
return;
}

if (!api.consts.referenda || !api.query.referenda) {
if (!api.consts['referenda'] || !api.query['referenda']) {
console.log('OpenGov is not supported on this chain');
setNotSupportedChain(true);
// to reset refs on non supported chain, or when chain has changed
Expand All @@ -143,19 +143,23 @@ export default function Governance (): React.ReactElement {

setNotSupportedChain(false);

api.query.referenda.referendumCount().then((count) => {
referendumCount.referenda = count?.toNumber();
api.query['referenda']['referendumCount']().then((count) => {
referendumCount.referenda = (count as u32)?.toNumber();
setReferendumCount({ ...referendumCount });
}).catch(console.error);

api.query.fellowshipReferenda && api.query.fellowshipReferenda.referendumCount().then((count) => {
referendumCount.fellowship = count?.toNumber();
api.query['fellowshipReferenda']?.['referendumCount']().then((count) => {
referendumCount.fellowship = (count as u32)?.toNumber();
setReferendumCount({ ...referendumCount });
}).catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [api, chainName]);

const addFellowshipOriginsFromSb = useCallback(async (resPA: LatestReferenda[]): Promise<LatestReferenda[] | undefined> => {
if (!chainName) {
return;
}

const resSb = await getReferendumsListSb(chainName, topMenu, pageTrackRef.current.page * LATEST_REFERENDA_LIMIT_TO_LOAD_PER_REQUEST);

if (resSb) {
Expand Down Expand Up @@ -297,8 +301,9 @@ export default function Governance (): React.ReactElement {
return;
}

api.query.fellowshipCollective && api.query.fellowshipCollective.members.entries().then((keys) => {
api.query['fellowshipCollective']?.['members'].entries().then((keys) => {
const fellowships = keys.map(([{ args: [id] }, option]) => {
//@ts-ignore
return [id.toString(), option?.value?.rank?.toNumber()] as Fellowship;
});

Expand All @@ -312,6 +317,47 @@ export default function Governance (): React.ReactElement {
setGetMore(pageTrackRef.current.page);
}, [pageTrackRef]);

const observerInstance = useRef<IntersectionObserver>();
const target = document.getElementById('observerObj');

useEffect(() => {
const observerCallback = (entries: IntersectionObserverEntry[]): void => {
const [entry] = entries;

if (!entry.isIntersecting) {
return; // If the observer object is not in view, do nothing
}

if (isLoadingMore) {
return; // If already fetching, do nothing
}

if (pageTrackRef.current.listFinished) {
observerInstance.current?.disconnect();

return;
}

getMoreReferenda();
};

const options = {
root: document.getElementById('scrollArea'),
rootMargin: '0px',
threshold: 1.0 // Trigger when 100% of the target (observerObj) is visible
};

observerInstance.current = new IntersectionObserver(observerCallback, options);

if (target) {
observerInstance.current.observe(target); // Start observing the target
}

return () => {
observerInstance.current?.disconnect();
};
}, [chainName, getMoreReferenda, isLoadingMore, target]);

return (
<>
<FullScreenHeader page='governance' />
Expand All @@ -335,16 +381,19 @@ export default function Governance (): React.ReactElement {
decidingCounts={decidingCounts}
menuOpen={menuOpen}
setMenuOpen={setMenuOpen}
// @ts-ignore
setSelectedSubMenu={setSelectedSubMenu}
/>
<Container disableGutters sx={{ maxWidth: 'inherit' }}>
<Bread
address={address}
// @ts-ignore
setSelectedSubMenu={setSelectedSubMenu}
subMenu={selectedSubMenu}
// @ts-ignore
topMenu={topMenu}
/>
<Container disableGutters sx={{ maxHeight: parent.innerHeight - 170, maxWidth: 'inherit', opacity: menuOpen ? 0.3 : 1, overflowY: 'scroll', px: '10px' }}>
<Container disableGutters id='scrollArea' sx={{ maxHeight: parent.innerHeight - 170, maxWidth: 'inherit', opacity: menuOpen ? 0.3 : 1, overflowY: 'scroll', px: '10px' }}>
{selectedSubMenu === 'All'
? <AllReferendaStats
address={address}
Expand Down Expand Up @@ -389,12 +438,12 @@ export default function Governance (): React.ReactElement {
!isLoadingMore
? <Grid container item justifyContent='center' sx={{ '&:hover': { cursor: 'pointer' }, pb: '15px' }}>
{notSupportedChain
? <Typography color='secondary.contrastText' fontSize='18px' fontWeight={600} onClick={getMoreReferenda} pt='50px'>
? <Typography color='secondary.contrastText' fontSize='18px' fontWeight={600} pt='50px'>
{t('Open Governance is not supported on the {{chainName}}', { replace: { chainName } })}
</Typography>
: !!referenda?.length && referendumCount[topMenu] && referenda.length < (referendumCount[topMenu] || 0)
? <Typography color='secondary.contrastText' fontSize='18px' fontWeight={600} onClick={getMoreReferenda}>
{t('Loaded {{count}} out of {{referendumCount}} referenda. Click here to load more', { replace: { count: referenda?.length || 0, referendumCount: referendumCount[topMenu] } })}
<div id='observerObj' style={{ height: '1px' }} />
</Typography>
: <Typography color='text.disabled' fontSize='15px'>
{t('No more referenda to load.')}
Expand All @@ -404,6 +453,9 @@ export default function Governance (): React.ReactElement {
: isLoadingMore &&
<Grid container justifyContent='center'>
<HorizontalWaiting color={theme.palette.primary.main} />
<Typography color='secondary.contrastText' display='block' fontSize='13px' textAlign='center' width='100%'>
{t('Loaded {{count}} out of {{referendumCount}} referenda ...', { replace: { count: referenda?.length || 0, referendumCount: referendumCount[topMenu] } })}
</Typography>
</Grid>
}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export default function Chronology ({ address, currentTreasuryApprovalList, refe
</Typography>
{!expanded &&
<Typography fontSize={16} fontWeight={300} sx={{ color: 'text.disabled', ml: '10px' }}>
{`(${sortedHistory?.length ? treasuryLabel || pascalCaseToTitleCase(sortedHistory[0].status)?.trim() : 'Unknown'})`}
({ isTreasury && isExecuted ? treasuryLabel : sortedHistory?.length ? treasuryLabel || pascalCaseToTitleCase(sortedHistory[0].status)?.trim() : ''})
</Typography>
}
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0
// @ts-nocheck

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

import type { Count } from '../../../hooks/useDecidingCount';

import { AdminPanelSettings as AdminsIcon, BorderAll as All, Groups3 as Groups3Icon, List as ListIcon } from '@mui/icons-material/';
import { Container, Grid, Typography, useTheme } from '@mui/material';
import React, { useCallback } from 'react';
import { useHistory } from 'react-router-dom';

import { Count } from '../../../hooks/useDecidingCount';
import { MAX_WIDTH } from '../utils/consts';
import { findItemDecidingCount } from './ReferendaMenu';

Expand All @@ -21,15 +21,15 @@ interface Props {

}

export default function FellowshipMenu({ address, decidingCounts, setMenuOpen, setSelectedSubMenu }: Props): React.ReactElement<Props> {
export default function FellowshipMenu ({ address, decidingCounts, setMenuOpen, setSelectedSubMenu }: Props): React.ReactElement<Props> {
const history = useHistory();
const theme = useTheme();

const onMouseLeave = useCallback(() => {
setMenuOpen(false);
}, [setMenuOpen]);

function MenuItem({ borderWidth = '2px', clickable = true, fontWeight, icon, item, top = false, width = '18%' }: { item: string, icon?: React.ReactElement, top?: boolean, width?: string, borderWidth?: string, fontWeight?: number, clickable?: boolean }): React.ReactElement {
function MenuItem ({ borderWidth = '2px', clickable = true, fontWeight, icon, item, top = false, width = '18%' }: { item: string, icon?: React.ReactElement, top?: boolean, width?: string, borderWidth?: string, fontWeight?: number, clickable?: boolean }): React.ReactElement {
const decidingCount = findItemDecidingCount(item, decidingCounts);

const onSubMenuClick = useCallback(() => {
Expand All @@ -44,7 +44,9 @@ export default function FellowshipMenu({ address, decidingCounts, setMenuOpen, s
return (
<Grid alignItems='center' container item
sx={{
borderBottom: top && `${borderWidth} solid`,
'&:hover': clickable ? { fontWeight: 700, textDecoration: 'underline' } : undefined,
borderBottom: top ? `${borderWidth} solid` : undefined,
borderColor: 'primary.main',
color: clickable
? (theme.palette.mode === 'light'
? 'secondary.main'
Expand All @@ -53,7 +55,11 @@ export default function FellowshipMenu({ address, decidingCounts, setMenuOpen, s
? 'text.primary'
: 'action.focus'
),
cursor: clickable && 'pointer', fontSize: '18px', width, borderColor: 'primary.main', mr: '20px', py: '5px', '&:hover': clickable && { fontWeight: 700, textDecoration: 'underline' }
cursor: clickable ? 'pointer' : undefined,
fontSize: '18px',
mr: '20px',
py: '5px',
width
}}>
{icon}
<Typography onClick={onSubMenuClick} sx={{ display: 'inline-block', fontWeight: fontWeight || 'inherit' }}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0
// @ts-nocheck

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

import type { Count } from '../../../hooks/useDecidingCount';

import { AccountBalance as TreasuryIcon, AdminPanelSettings as AdminsIcon, BorderAll as All, Cancel, Hub as Root } from '@mui/icons-material/';
import { Container, Grid, Typography, useTheme } from '@mui/material';
import React, { useCallback } from 'react';
import { useHistory } from 'react-router-dom';

import { Count } from '../../../hooks/useDecidingCount';
import { MAX_WIDTH } from '../utils/consts';

interface Props {
Expand All @@ -30,7 +30,7 @@ export const findItemDecidingCount = (item: string, decidingCounts: Count[] | un
return filtered?.[1];
};

export default function ReferendaMenu({ address, decidingCounts, setMenuOpen, setSelectedSubMenu }: Props): React.ReactElement<Props> {
export default function ReferendaMenu ({ address, decidingCounts, setMenuOpen, setSelectedSubMenu }: Props): React.ReactElement<Props> {
const history = useHistory();
const theme = useTheme();
const onMouseLeave = useCallback(() => {
Expand All @@ -51,7 +51,9 @@ export default function ReferendaMenu({ address, decidingCounts, setMenuOpen, se
return (
<Grid alignItems='center' container item
sx={{
borderBottom: top && `${borderWidth} solid`,
'&:hover': clickable ? { fontWeight: 700, textDecoration: 'underline' } : undefined,
borderBottom: top ? `${borderWidth} solid` : undefined,
borderColor: 'primary.main',
color: clickable
? (theme.palette.mode === 'light'
? 'secondary.main'
Expand All @@ -60,7 +62,11 @@ export default function ReferendaMenu({ address, decidingCounts, setMenuOpen, se
? 'text.primary'
: 'action.focus'
),
cursor: clickable && 'pointer', fontSize: '18px', width, borderColor: 'primary.main', mr: '20px', py: '5px', '&:hover': clickable && { fontWeight: 700, textDecoration: 'underline' }
cursor: clickable ? 'pointer' : 'default',
fontSize: '18px',
mr: '20px',
py: '5px',
width
}}>
{icon}
<Typography onClick={onSubMenuClick} sx={{ display: 'inline-block', fontWeight: fontWeight || 'inherit' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { BN } from '@polkadot/util';

export const MAX_WIDTH = '1280px';
export const LATEST_REFERENDA_LIMIT_TO_LOAD_PER_REQUEST = 30;
export const LATEST_REFERENDA_LIMIT_TO_LOAD_PER_REQUEST = 50;
export const TRACK_LIMIT_TO_LOAD_PER_REQUEST = LATEST_REFERENDA_LIMIT_TO_LOAD_PER_REQUEST;
export const REFERENDA_LIMIT_SAVED_LOCAL = 2 * LATEST_REFERENDA_LIMIT_TO_LOAD_PER_REQUEST;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ export async function getAllVotesFromPA (chainName: string, refIndex: number, li
headers: { 'x-network': chainName.charAt(0).toLowerCase() + chainName.slice(1) }
};

// return fetch(`https://api.polkassembly.io/api/v1/votes?postId=${refIndex}&page=1&listingLimit=${listingLimit}&voteType=${isFellowship ? 'Fellowship' : 'ReferendumV2'}&sortBy=time`, requestOptions)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return fetch(`https://api.polkassembly.io/api/v1/votes?postId=${refIndex}&page=1&listingLimit=${listingLimit}&voteType=${isFellowship ? 'Fellowship' : 'ReferendumV2'}`, requestOptions)
.then((response) => response.json())
Expand Down Expand Up @@ -395,13 +394,13 @@ interface RefListSb {
}[];
}

export async function getReferendumsListSb (chainName: string, type: TopMenu, listingLimit = 30): Promise<RefListSb | null> {
export async function getReferendumsListSb (chainName: string, type: 'referenda' | 'fellowship', listingLimit = 30): Promise<RefListSb | null> {
console.log('Getting ref list from sb ...');

return new Promise((resolve) => {
try {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
postData('https://' + chainName + `.api.subscan.io/api/scan/${type.toLocaleLowerCase()}/referendums`,
postData('https://' + chainName + `.api.subscan.io/api/scan/${type.toLowerCase()}/referendums`,
{
// page:1,
row: listingLimit
Expand Down

0 comments on commit c987138

Please sign in to comment.