From ba9765ba1cbcaf979e7bb32e7afbc0f411e523b4 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 25 Sep 2019 20:56:59 +0200 Subject: [PATCH] Convert status queue (#1658) * Convert status queue * Use queue refs (closures) --- .../react-components/src/Status/Queue.tsx | 266 ++++++++---------- packages/react-components/src/Status/types.ts | 8 +- 2 files changed, 114 insertions(+), 160 deletions(-) diff --git a/packages/react-components/src/Status/Queue.tsx b/packages/react-components/src/Status/Queue.tsx index 678309cc8c..1ddc97c091 100644 --- a/packages/react-components/src/Status/Queue.tsx +++ b/packages/react-components/src/Status/Queue.tsx @@ -2,12 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { RpcMethod } from '@polkadot/jsonrpc/types'; import { SignerPayloadJSON } from '@polkadot/types/types'; import { BareProps } from '../types'; -import { ActionStatus, PartialQueueTxExtrinsic, PartialQueueTxRpc, QueueProps as State, QueueStatus, QueueTx, QueueTxExtrinsic, QueueTxRpc, QueueTxStatus, SignerCallback } from './types'; +import { ActionStatus, PartialQueueTxExtrinsic, PartialQueueTxRpc, QueueStatus, QueueTx, QueueTxExtrinsic, QueueTxRpc, QueueTxStatus, SignerCallback } from './types'; -import React from 'react'; +import React, { useRef, useState } from 'react'; import jsonrpc from '@polkadot/jsonrpc'; import { createType } from '@polkadot/types'; @@ -19,11 +18,6 @@ export interface Props extends BareProps { children: React.ReactNode; } -const defaultState: Partial = { - stqueue: [] as QueueStatus[], - txqueue: [] as QueueTx[] -}; - let nextId = 0; const REMOVE_TIMEOUT = 7500; @@ -35,150 +29,61 @@ const STATUS_COMPLETE: QueueTxStatus[] = [ 'cancelled', 'error', 'sent' ]; -export default class Queue extends React.Component { - public state: State = defaultState as State; +export default function Queue ({ children }: Props): React.ReactElement { + const [_stqueue, _setStQueue] = useState([]); + const [_txqueue, _setTxQueue] = useState([]); + const stRef = useRef(_stqueue); + const txRef = useRef(_txqueue); - public constructor (props: Props) { - super(props); + const setStQueue = (st: QueueStatus[]): void => { + stRef.current = st; + _setStQueue(st); + }; + const setTxQueue = (tx: QueueTx[]): void => { + txRef.current = tx; + _setTxQueue(tx); + }; - this.state = { - stqueue: [], - txqueue: [], - queueAction: this.queueAction, - queueExtrinsic: this.queueExtrinsic, - queuePayload: this.queuePayload, - queueRpc: this.queueRpc, - queueSetTxStatus: this.queueSetTxStatus - }; - } - - public render (): React.ReactNode { - return ( - - {this.props.children} - - ); - } - - private clearAction (id: number): () => void { - return (): void => { - this.setState( - (prevState: State): Pick => ({ - stqueue: prevState.stqueue.filter((item): boolean => item.id !== id) - }) - ); - }; - } - - public queueAction = (status: ActionStatus | ActionStatus[]): number => { - const todos = (Array.isArray(status) ? status : [status]).map((item: ActionStatus) => { - const id = ++nextId; - const removeItem = this.clearAction(id); - - setTimeout(removeItem, REMOVE_TIMEOUT); - - return { - ...item, - id, - isCompleted: false, - removeItem - }; - }); - - this.setState( - (prevState: State): Pick => ({ - stqueue: prevState.stqueue.concat(...todos) - }) - ); - - return todos[0].id; - } - - private clearStatus (id: number): () => void { - return (): void => { - this.setState( - (prevState: State): Pick => ({ - txqueue: prevState.txqueue.map((item): QueueTx => - item.id === id - ? { ...item, status: 'completed' } - : item - ) - }) - ); - }; - } - - private queueSetTxStatus = (id: number, status: QueueTxStatus, result?: SubmittableResult, error?: Error): void => { - this.setState( - (prevState: State): Pick => ({ - txqueue: prevState.txqueue.map((item): QueueTx => - item.id === id - ? { - ...item, - error: error === undefined - ? item.error - : error, - result: result === undefined - ? item.result - : result, - status: item.status === 'completed' - ? item.status - : status - } - : item - ) - }) - ); - - this.addResultEvents(result); - - if (STATUS_COMPLETE.includes(status)) { - setTimeout(this.clearStatus(id), REMOVE_TIMEOUT); - } - } - - private addResultEvents ({ events = [] }: Partial = {}): void { - events.filter((record): boolean => !!record.event).forEach(({ event: { method, section } }): void => { - // filter events handled globally, or those we are not interested in, these are - // handled by the global overview, so don't add them here - if (section === 'democracy') { - return; - } - - const status = section === 'system' && method === 'ExtrinsicFailed' - ? 'error' - : 'event'; - - this.queueAction({ - action: `${section}.${method}`, - status, - message: 'extrinsic event' - }); - }); - } - - private queueAdd = (value: QueueTxExtrinsic | QueueTxRpc | QueueTx): number => { + const addToTxQueue = (value: QueueTxExtrinsic | QueueTxRpc | QueueTx): void => { const id = ++nextId; - const rpc: RpcMethod = (value as QueueTxRpc).rpc || SUBMIT_RPC; - const removeItem = this.clearStatus(id); + const removeItem = (): void => + setTxQueue([...txRef.current.map((item): QueueTx => + item.id === id + ? { ...item, status: 'completed' } + : item + )]); - this.setState( - (prevState: State): Pick => ({ - txqueue: prevState.txqueue.concat([{ - ...value, + setTxQueue([...txRef.current, { + ...value, + id, + removeItem, + rpc: (value as QueueTxRpc).rpc || SUBMIT_RPC, + status: 'queued' + }]); + }; + + const queueAction = (status: ActionStatus | ActionStatus[]): void => { + const _status = Array.isArray(status) ? status : [status]; + + if (_status.length) { + setStQueue([...stRef.current, ...(_status.map((item): QueueStatus => { + const id = ++nextId; + const removeItem = (): void => + setStQueue([...stRef.current.filter((item): boolean => item.id !== id)]); + + setTimeout(removeItem, REMOVE_TIMEOUT); + + return { + ...item, id, - removeItem, - rpc, - status: 'queued' - }]) - }) - ); - - return id; - } - - public queueExtrinsic = ({ accountId, extrinsic, txFailedCb, txSuccessCb, txStartCb, txUpdateCb, isUnsigned }: PartialQueueTxExtrinsic): number => { - return this.queueAdd({ + isCompleted: false, + removeItem + }; + }))]); + } + }; + const queueExtrinsic = ({ accountId, extrinsic, txFailedCb, txSuccessCb, txStartCb, txUpdateCb, isUnsigned }: PartialQueueTxExtrinsic): void => + addToTxQueue({ accountId, extrinsic, isUnsigned, @@ -187,10 +92,8 @@ export default class Queue extends React.Component { txStartCb, txUpdateCb }); - } - - public queuePayload = (payload: SignerPayloadJSON, signerCb: SignerCallback): number => { - return this.queueAdd({ + const queuePayload = (payload: SignerPayloadJSON, signerCb: SignerCallback): void => + addToTxQueue({ accountId: payload.address, // this is not great, but the Extrinsic we don't need a submittable extrinsic: createType('Extrinsic', @@ -200,13 +103,64 @@ export default class Queue extends React.Component { payload, signerCb }); - } - - public queueRpc = ({ accountId, rpc, values }: PartialQueueTxRpc): number => { - return this.queueAdd({ + const queueRpc = ({ accountId, rpc, values }: PartialQueueTxRpc): void => + addToTxQueue({ accountId, rpc, values }); - } + const queueSetTxStatus = (id: number, status: QueueTxStatus, result?: SubmittableResult, error?: Error): void => { + setTxQueue([...txRef.current.map((item): QueueTx => + item.id === id + ? { + ...item, + error: error === undefined + ? item.error + : error, + result: result === undefined + ? item.result + : result, + status: item.status === 'completed' + ? item.status + : status + } + : item + )]); + + queueAction( + ((result && result.events) || []) + // filter events handled globally, or those we are not interested in, these are + // handled by the global overview, so don't add them here + .filter((record): boolean => !!record.event && record.event.section !== 'democracy') + .map(({ event: { method, section } }): ActionStatus => ({ + action: `${section}.${method}`, + status: section === 'system' && method === 'ExtrinsicFailed' + ? 'error' + : 'event', + message: 'extrinsic event' + })) + ); + + if (STATUS_COMPLETE.includes(status)) { + setTimeout((): void => { + const item = txRef.current.find((item): boolean => item.id === id); + + item && item.removeItem(); + }, REMOVE_TIMEOUT); + } + }; + + return ( + + {children} + + ); } diff --git a/packages/react-components/src/Status/types.ts b/packages/react-components/src/Status/types.ts index 96b61a7d47..28b23f9967 100644 --- a/packages/react-components/src/Status/types.ts +++ b/packages/react-components/src/Status/types.ts @@ -89,15 +89,15 @@ export interface PartialQueueTxRpc extends PartialAccountInfo { values: any[]; } -export type QueueTxRpcAdd = (value: PartialQueueTxRpc) => number; +export type QueueTxRpcAdd = (value: PartialQueueTxRpc) => void; -export type QueueTxExtrinsicAdd = (value: PartialQueueTxExtrinsic) => number; +export type QueueTxExtrinsicAdd = (value: PartialQueueTxExtrinsic) => void; -export type QueueTxPayloadAdd = (payload: SignerPayloadJSON, signerCb: SignerCallback) => number; +export type QueueTxPayloadAdd = (payload: SignerPayloadJSON, signerCb: SignerCallback) => void; export type QueueTxMessageSetStatus = (id: number, status: QueueTxStatus, result?: any, error?: Error) => void; -export type QueueAction$Add = (status: ActionStatus | ActionStatus[]) => number; +export type QueueAction$Add = (status: ActionStatus | ActionStatus[]) => void; export interface QueueProps { stqueue: QueueStatus[];