React.memo-ize more func. components (#2389)

* React.memo-ize more func. components

* Rework status display determination

* isApiReady &&

* ApiSigner private class interface

* readonly #

* Remove isPrimary on council vote
This commit is contained in:
Jaco Greeff 2020-03-15 12:11:59 +01:00 committed by GitHub
parent 6530e49c3e
commit e97868348b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 111 additions and 84 deletions

View File

@ -10,8 +10,10 @@ type Props = {};
const defaultTo = `/${routing.default}`;
export default function NotFound (): React.ReactElement<Props> {
function NotFound (): React.ReactElement<Props> {
return (
<Redirect to={defaultTo} />
);
}
export default React.memo(NotFound);

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/camelcase */
// Copyright 2017-2020 @polkadot/apps authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
@ -24,54 +23,58 @@ interface Props {
let prevEventHash: string;
export default function Status ({ optionsAll, queueAction, stqueue, txqueue }: Props): React.ReactElement<Props> {
function filterEvents (allAccounts: string[], t: (key: string, opts?: object) => string, optionsAll?: KeyringOptions, events?: EventRecord[]): ActionStatus[] | null {
const eventHash = xxhashAsHex(stringToU8a(JSON.stringify(events)));
if (!optionsAll || !events || eventHash === prevEventHash) {
return null;
}
prevEventHash = eventHash;
return events
.map(({ event: { data, method, section } }): ActionStatus | null => {
if (section === 'balances' && method === 'Transfer') {
const account = data[1].toString();
if (allAccounts.includes(account)) {
return {
account,
action: `${section}.${method}`,
status: 'event',
message: t('transfer received')
};
}
} else if (section === 'democracy') {
const index = data[0].toString();
return {
action: `${section}.${method}`,
status: 'event',
message: t('update on #{{index}}', {
replace: {
index
}
})
};
}
return null;
})
.filter((item): item is ActionStatus => !!item);
}
function Status ({ optionsAll, queueAction, stqueue, txqueue }: Props): React.ReactElement<Props> {
const { api, isApiReady } = useApi();
const { allAccounts } = useAccounts();
const { t } = useTranslation();
const events = useCall<EventRecord[]>(isApiReady ? api.query.system?.events : undefined, []);
const events = useCall<EventRecord[]>(isApiReady && api.query.system?.events, []);
useEffect((): void => {
const eventHash = xxhashAsHex(stringToU8a(JSON.stringify(events)));
const filtered = filterEvents(allAccounts, t, optionsAll, events);
if (!optionsAll || eventHash === prevEventHash) {
return;
}
prevEventHash = eventHash;
const statusses = events && events
.map(({ event: { data, method, section } }): ActionStatus | null => {
if (section === 'balances' && method === 'Transfer') {
const account = data[1].toString();
if (allAccounts.includes(account)) {
return {
account,
action: `${section}.${method}`,
status: 'event',
message: t('transfer received')
};
}
} else if (section === 'democracy') {
const index = data[0].toString();
return {
action: `${section}.${method}`,
status: 'event',
message: t('update on #{{index}}', {
replace: {
index
}
})
};
}
return null;
})
.filter((item): boolean => !!item) as ActionStatus[];
statusses && statusses.length && queueAction(statusses);
}, [events]);
filtered && queueAction(filtered);
}, [allAccounts, events, optionsAll]);
return (
<StatusDisplay
@ -80,3 +83,5 @@ export default function Status ({ optionsAll, queueAction, stqueue, txqueue }: P
/>
);
}
export default React.memo(Status);

View File

@ -73,12 +73,12 @@ function checkVisible (name: string, { api, isApiReady, isApiConnected }: ApiPro
return notFound.length === 0;
}
export default function Item ({ route, isCollapsed, onClick }: Props): React.ReactElement<Props> | null {
function Item ({ route, isCollapsed, onClick }: Props): React.ReactElement<Props> | null {
const { Modal, useCounter = DUMMY_COUNTER, display, i18n, icon, name } = route;
const { t } = useTranslation();
const { allAccounts, hasAccounts } = useAccounts();
const apiProps = useApi();
const sudoKey = useCall<AccountId>(apiProps.isApiReady ? apiProps.api.query.sudo?.key : undefined, []);
const sudoKey = useCall<AccountId>(apiProps.isApiReady && apiProps.api.query.sudo?.key, []);
const [hasSudo, setHasSudo] = useState(false);
const [isVisible, setIsVisible] = useState(false);
const count = useCounter();
@ -149,3 +149,5 @@ export default function Item ({ route, isCollapsed, onClick }: Props): React.Rea
</Menu.Item>
);
}
export default React.memo(Item);

View File

@ -5,12 +5,12 @@
import React, { useEffect, useState } from 'react';
import { useApi, useCall } from '@polkadot/react-hooks';
export default function WarmUp (): React.ReactElement {
function WarmUp (): React.ReactElement {
const { api, isApiReady } = useApi();
const fees = useCall<any>(isApiReady && api.derive.balances?.fees, []);
const indexes = useCall<any>(isApiReady && api.derive.accounts?.indexes, []);
const registrars = useCall<any>(isApiReady && api.query.identity?.registrars, []);
const staking = null; // useCall<any>(isApiReady ? api.derive.staking?.overview : undefined, []);
const fees = useCall<unknown>(isApiReady && api.derive.balances?.fees, []);
const indexes = useCall<unknown>(isApiReady && api.derive.accounts?.indexes, []);
const registrars = useCall<unknown>(isApiReady && api.query.identity?.registrars, []);
const staking = null; // useCall<unknown>(isApiReady && api.derive.staking?.overview, []);
const [hasValues, setHasValues] = useState(false);
useEffect((): void => {
@ -21,3 +21,5 @@ export default function WarmUp (): React.ReactElement {
<div className={`apps--api-warm ${hasValues}`} />
);
}
export default React.memo(WarmUp);

View File

@ -13,7 +13,7 @@ interface Props {
onClose: () => void;
}
export default function NetworkModal ({ className, onClose }: Props): React.ReactElement<Props> {
function NetworkModal ({ className, onClose }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
return (
@ -30,3 +30,5 @@ export default function NetworkModal ({ className, onClose }: Props): React.Reac
</Modal>
);
}
export default React.memo(NetworkModal);

View File

@ -71,7 +71,6 @@ export default function Voting ({ hash, idNumber, isDisabled, members, prime, pr
<Button
icon='check'
isDisabled={isDisabled}
isPrimary
label={t('Vote')}
onClick={toggleVoting}
/>

View File

@ -9,7 +9,7 @@ import { useApi, useCall } from '@polkadot/react-hooks';
export default function useCounter (): number {
const { api, isApiReady } = useApi();
const motions = useCall<DerivedCollectiveProposals>(isApiReady ? api.derive.council?.proposals : undefined, []);
const motions = useCall<DerivedCollectiveProposals>(isApiReady && api.derive.council?.proposals, []);
const [counter, setCounter] = useState(0);
useEffect((): void => {

View File

@ -7,8 +7,8 @@ import { useApi, useCall } from '@polkadot/react-hooks';
export default function useCounter (): number {
const { api, isApiReady } = useApi();
const proposals = useCall<any[]>(isApiReady ? api.derive.democracy?.proposals : undefined, []);
const referenda = useCall<any[]>(isApiReady ? api.derive.democracy?.referendums : undefined, []);
const proposals = useCall<any[]>(isApiReady && api.derive.democracy?.proposals, []);
const referenda = useCall<any[]>(isApiReady && api.derive.democracy?.referendums, []);
const [counter, setCounter] = useState(0);
useEffect((): void => {

View File

@ -9,7 +9,7 @@ import { useApi, useCall } from '@polkadot/react-hooks';
export default function useCounter (): number {
const { api, isApiReady } = useApi();
const proposals = useCall<Hash[]>(isApiReady ? api.query.technicalCommittee?.proposals : undefined, []);
const proposals = useCall<Hash[]>(isApiReady && api.query.technicalCommittee?.proposals, []);
const [counter, setCounter] = useState(0);
useEffect((): void => {

View File

@ -9,7 +9,7 @@ import { useApi, useCall } from '@polkadot/react-hooks';
export default function useCounter (): number {
const { api, isApiReady } = useApi();
const proposals = useCall<DerivedTreasuryProposals>(isApiReady ? api.derive.treasury?.proposals : undefined, []);
const proposals = useCall<DerivedTreasuryProposals>(isApiReady && api.derive.treasury?.proposals, []);
const [counter, setCounter] = useState(0);
useEffect((): void => {

View File

@ -4,7 +4,7 @@
import { QueueStatus, QueueTx, QueueTxStatus } from './types';
import React from 'react';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { registry } from '@polkadot/react-api';
@ -149,38 +149,53 @@ function renderItem ({ id, extrinsic, error, removeItem, rpc, status }: QueueTx)
);
}
function Status ({ className, stqueue = [], txqueue = [] }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const allst: QueueStatus[] = stqueue.filter(({ isCompleted }): boolean => !isCompleted);
const alltx: QueueTx[] = txqueue.filter(({ status }): boolean =>
!['completed', 'incomplete'].includes(status)
);
const completedTx = alltx.filter(({ status }): boolean => STATUS_COMPLETE.includes(status));
function filterSt (stqueue?: QueueStatus[]): QueueStatus[] {
return (stqueue || []).filter(({ isCompleted }): boolean => !isCompleted);
}
if (!allst.length && !alltx.length) {
function filterTx (txqueue?: QueueTx[]): [QueueTx[], QueueTx[]] {
const allTx = (txqueue || []).filter(({ status }): boolean => !['completed', 'incomplete'].includes(status));
return [allTx, allTx.filter(({ status }): boolean => STATUS_COMPLETE.includes(status))];
}
function Status ({ className, stqueue, txqueue }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const allSt = useMemo(
(): QueueStatus[] => filterSt(stqueue),
[stqueue]
);
const [allTx, completedTx] = useMemo(
(): [QueueTx[], QueueTx[]] => filterTx(txqueue),
[txqueue]
);
const _onDismiss = useCallback(
(): void => {
allSt.map((s): void => s.removeItem());
completedTx.map((t): void => t.removeItem());
},
[allSt, completedTx]
);
if (!allSt.length && !allTx.length) {
return null;
}
const _onDismiss = (): void => {
allst.map((s): void => s.removeItem());
completedTx.map((t): void => t.removeItem());
};
return (
<div className={`ui--Status ${className}`}>
{(allst.length + completedTx.length) > 1 && (
{(allSt.length + completedTx.length) > 1 && (
<div className='dismiss'>
<Button
isFluid
isNegative
isPrimary
onClick={_onDismiss}
label={t('Dismiss all notifications')}
icon='cancel'
/>
</div>
)}
{alltx.map(renderItem)}
{allst.map(renderStatus)}
{allTx.map(renderItem)}
{allSt.map(renderStatus)}
</div>
);
}

View File

@ -17,7 +17,7 @@ interface Props extends BareProps {
export default function BestNumber ({ children, className, label, style, withPound }: Props): React.ReactElement<Props> {
const { api, isApiReady } = useApi();
const bestNumber = useCall<BlockNumber>(isApiReady ? api.derive.chain.bestNumber as any : undefined, []);
const bestNumber = useCall<BlockNumber>(isApiReady && api.derive.chain.bestNumber, []);
return (
<div

View File

@ -12,18 +12,18 @@ import { registry } from '@polkadot/react-api';
import { ClassOf } from '@polkadot/types';
export default class ApiSigner implements Signer {
private _queuePayload: QueueTxPayloadAdd;
readonly #queuePayload: QueueTxPayloadAdd;
private _queueSetTxStatus: QueueTxMessageSetStatus;
readonly #queueSetTxStatus: QueueTxMessageSetStatus;
constructor (queuePayload: QueueTxPayloadAdd, queueSetTxStatus: QueueTxMessageSetStatus) {
this._queuePayload = queuePayload;
this._queueSetTxStatus = queueSetTxStatus;
this.#queuePayload = queuePayload;
this.#queueSetTxStatus = queueSetTxStatus;
}
public async signPayload (payload: SignerPayloadJSON): Promise<SignerResult> {
return new Promise((resolve, reject): void => {
this._queuePayload(payload, (id: number, result: SignerResult | null): void => {
this.#queuePayload(payload, (id: number, result: SignerResult | null): void => {
if (result) {
resolve(result);
} else {
@ -35,9 +35,9 @@ export default class ApiSigner implements Signer {
public update (id: number, result: Hash | SubmittableResult): void {
if (result instanceof ClassOf(registry, 'Hash')) {
this._queueSetTxStatus(id, 'sent', result.toHex());
this.#queueSetTxStatus(id, 'sent', result.toHex());
} else {
this._queueSetTxStatus(id, result.status.type.toLowerCase() as QueueTxStatus, status);
this.#queueSetTxStatus(id, result.status.type.toLowerCase() as QueueTxStatus, status);
}
}
}