From 593b42c27552d89c172a2f3ec10f2eb7e5dd60af Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sun, 20 Dec 2020 12:55:10 +0100 Subject: [PATCH] Save target/overview toggles (#4249) --- .../page-staking/src/Overview/CurrentList.tsx | 16 ++--- packages/page-staking/src/Targets/index.tsx | 64 +++++++++---------- packages/react-hooks/src/index.ts | 1 + packages/react-hooks/src/useSavedFlags.ts | 51 +++++++++++++++ 4 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 packages/react-hooks/src/useSavedFlags.ts diff --git a/packages/page-staking/src/Overview/CurrentList.tsx b/packages/page-staking/src/Overview/CurrentList.tsx index c0a7bbcc60..c0354d5fd0 100644 --- a/packages/page-staking/src/Overview/CurrentList.tsx +++ b/packages/page-staking/src/Overview/CurrentList.tsx @@ -9,7 +9,7 @@ import type { SortedTargets, ValidatorInfo } from '../types'; import React, { useCallback, useContext, useMemo, useRef, useState } from 'react'; import { Table } from '@polkadot/react-components'; -import { useApi, useCall, useLoadingDelay } from '@polkadot/react-hooks'; +import { useApi, useCall, useLoadingDelay, useSavedFlags } from '@polkadot/react-hooks'; import { BlockAuthorsContext } from '@polkadot/react-query'; import Filtering from '../Filtering'; @@ -78,7 +78,7 @@ function CurrentList ({ favorites, hasQueries, isIntentions, stakingOverview, ta const recentlyOnline = useCall(!isIntentions && api.derive.imOnline?.receivedHeartbeats); const nominatedBy = useNominations(isIntentions); const [nameFilter, setNameFilter] = useState(''); - const [withIdentity, setWithIdentity] = useState(false); + const [toggles, setToggle] = useSavedFlags('staking:overview', { withIdentity: false }); // we have a very large list, so we use a loading delay const isLoading = useLoadingDelay(); @@ -134,10 +134,10 @@ function CurrentList ({ favorites, hasQueries, isIntentions, stakingOverview, ta points={eraPoints[address]} toggleFavorite={toggleFavorite} validatorInfo={infoMap?.[address]} - withIdentity={withIdentity} + withIdentity={toggles.withIdentity} /> )), - [byAuthor, eraPoints, hasQueries, infoMap, nameFilter, nominatedBy, recentlyOnline, toggleFavorite, withIdentity] + [byAuthor, eraPoints, hasQueries, infoMap, nameFilter, nominatedBy, recentlyOnline, toggleFavorite, toggles] ); return isIntentions @@ -155,8 +155,8 @@ function CurrentList ({ favorites, hasQueries, isIntentions, stakingOverview, ta } header={headerWaitingRef.current} @@ -179,8 +179,8 @@ function CurrentList ({ favorites, hasQueries, isIntentions, stakingOverview, ta } header={headerActiveRef.current} diff --git a/packages/page-staking/src/Targets/index.tsx b/packages/page-staking/src/Targets/index.tsx index 3e17fae5a2..6511e638ba 100644 --- a/packages/page-staking/src/Targets/index.tsx +++ b/packages/page-staking/src/Targets/index.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import styled from 'styled-components'; import { Button, Icon, Table, Toggle } from '@polkadot/react-components'; -import { useApi, useAvailableSlashes, useBlocksPerDays } from '@polkadot/react-hooks'; +import { useApi, useAvailableSlashes, useBlocksPerDays, useSavedFlags } from '@polkadot/react-hooks'; import { MAX_NOMINATIONS } from '../constants'; import ElectionBanner from '../ElectionBanner'; @@ -33,10 +33,7 @@ interface Props { toggleFavorite: (address: string) => void; } -interface Flags { - daysPayout: BN; - isBabe: boolean; - maxPaid: BN | undefined; +interface SavedFlags { withElected: boolean; withGroup: boolean; withIdentity: boolean; @@ -45,6 +42,12 @@ interface Flags { withoutOver: boolean; } +interface Flags extends SavedFlags { + daysPayout: BN; + isBabe: boolean; + maxPaid: BN | undefined; +} + interface SortState { sortBy: TargetSortBy; sortFromMax: boolean; @@ -180,12 +183,14 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak const allIdentity = useIdentities(validatorIds); const [selected, setSelected] = useState([]); const [{ isQueryFiltered, nameFilter }, setNameFilter] = useState({ isQueryFiltered: false, nameFilter: '' }); - const [withElected, setWithElected] = useState(false); - const [withGroup, setWithGroup] = useState(true); - const [withIdentity, setWithIdentity] = useState(false); - const [withPayout, setWithPayout] = useState(false); - const [withoutComm, setWithoutComm] = useState(true); - const [withoutOver, setWithoutOver] = useState(true); + const [toggles, setToggle] = useSavedFlags('staking:targets', { + withElected: false, + withGroup: true, + withIdentity: false, + withPayout: false, + withoutComm: true, + withoutOver: true + }); const [{ sortBy, sortFromMax }, setSortBy] = useState({ sortBy: 'rankOverall', sortFromMax: true }); const [sorted, setSorted] = useState(); @@ -198,18 +203,13 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak const flags = useMemo( () => ({ + ...toggles, daysPayout, isBabe: !!api.consts.babe, isQueryFiltered, - maxPaid: api.consts.staking?.maxNominatorRewardedPerValidator, - withElected, - withGroup, - withIdentity, - withPayout, - withoutComm, - withoutOver + maxPaid: api.consts.staking?.maxNominatorRewardedPerValidator }), - [api, daysPayout, isQueryFiltered, withElected, withGroup, withIdentity, withPayout, withoutComm, withoutOver] + [api, daysPayout, isQueryFiltered, toggles] ); const filtered = useMemo( @@ -282,14 +282,14 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak ('single from operator')} - onChange={setWithGroup} - value={withGroup} + onChange={setToggle.withGroup} + value={toggles.withGroup} /> ('no {{maxComm}}%+ comm', { replace: { maxComm: MAX_COMM_PERCENT } }) : t('no median+ comm') } - onChange={setWithoutComm} - value={withoutComm} + onChange={setToggle.withoutComm} + value={toggles.withoutComm} /> ('no {{maxCap}}%+ capacity', { replace: { maxCap: MAX_CAP_PERCENT } }) : t('no at capacity') } - onChange={setWithoutOver} - value={withoutOver} + onChange={setToggle.withoutOver} + value={toggles.withoutOver} /> {api.consts.babe && ( // FIXME have some sane era defaults for Aura ('recent payouts')} - onChange={setWithPayout} - value={withPayout} + onChange={setToggle.withPayout} + value={toggles.withPayout} /> )} ('only elected')} - onChange={setWithElected} - value={withElected} + onChange={setToggle.withElected} + value={toggles.withElected} /> - ), [api, nameFilter, _setNameFilter, t, withElected, withGroup, withIdentity, withPayout, withoutComm, withoutOver]); + ), [api, nameFilter, _setNameFilter, setToggle, t, toggles]); const displayList = isQueryFiltered ? validators diff --git a/packages/react-hooks/src/index.ts b/packages/react-hooks/src/index.ts index dd1bd07ee0..c00cdd7c8e 100644 --- a/packages/react-hooks/src/index.ts +++ b/packages/react-hooks/src/index.ts @@ -29,6 +29,7 @@ export { useOwnStashes, useOwnStashIds } from './useOwnStashes'; export { useOwnStashInfos } from './useOwnStashInfos'; export { usePassword } from './usePassword'; export { useRegistrars } from './useRegistrars'; +export { useSavedFlags } from './useSavedFlags'; export { useSudo } from './useSudo'; export { useToggle } from './useToggle'; export { useVotingStatus } from './useVotingStatus'; diff --git a/packages/react-hooks/src/useSavedFlags.ts b/packages/react-hooks/src/useSavedFlags.ts new file mode 100644 index 0000000000..e591f852d6 --- /dev/null +++ b/packages/react-hooks/src/useSavedFlags.ts @@ -0,0 +1,51 @@ +// Copyright 2017-2020 @polkadot/react-hooks authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useEffect, useState } from 'react'; +import store from 'store'; + +import { isBoolean } from '@polkadot/util'; + +type Flags = Record; + +type Setters = Record void>; + +type State = [T, Setters]; + +function getInitial (storageKey: string, initial: T): T { + const saved = store.get(`flags:${storageKey}`, {}) as T; + + return Object.keys(initial).reduce((result, key: keyof T): T => { + if (isBoolean(saved[key])) { + result[key] = saved[key]; + } + + return result; + }, { ...initial }); +} + +function getSetters (flags: T, setFlags: React.Dispatch>): Setters { + const setFlag = (key: keyof T) => + (value: boolean) => + setFlags((state) => ({ ...state, [key]: value })); + + return Object.keys(flags).reduce((setters, key: keyof T): Setters => { + setters[key] = setFlag(key); + + return setters; + }, {} as Setters); +} + +export function useSavedFlags (storageKey: string, initial: T): State { + const [flags, setFlags] = useState(getInitial(storageKey, initial)); + const [setters] = useState(getSetters(initial, setFlags)); + + useEffect( + (): void => { + store.set(`flags:${storageKey}`, flags); + }, + [flags, storageKey] + ); + + return [flags, setters]; +}