Convert status queue (#1658)

* Convert status queue

* Use queue refs (closures)
This commit is contained in:
Jaco Greeff 2019-09-25 20:56:59 +02:00 committed by GitHub
parent d803899ccb
commit ba9765ba1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 114 additions and 160 deletions

View File

@ -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<State> = {
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<Props, State> {
public state: State = defaultState as State;
export default function Queue ({ children }: Props): React.ReactElement<Props> {
const [_stqueue, _setStQueue] = useState<QueueStatus[]>([]);
const [_txqueue, _setTxQueue] = useState<QueueTx[]>([]);
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 (
<QueueProvider value={this.state}>
{this.props.children}
</QueueProvider>
);
}
private clearAction (id: number): () => void {
return (): void => {
this.setState(
(prevState: State): Pick<State, never> => ({
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<State, never> => ({
stqueue: prevState.stqueue.concat(...todos)
})
);
return todos[0].id;
}
private clearStatus (id: number): () => void {
return (): void => {
this.setState(
(prevState: State): Pick<State, never> => ({
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<State, never> => ({
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<SubmittableResult> = {}): 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<State, never> => ({
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<Props, State> {
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<Props, State> {
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 (
<QueueProvider value={{
queueAction,
queueExtrinsic,
queuePayload,
queueRpc,
queueSetTxStatus,
stqueue: _stqueue,
txqueue: _txqueue
}}>
{children}
</QueueProvider>
);
}

View File

@ -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[];