Convert status queue (#1658)
* Convert status queue * Use queue refs (closures)
This commit is contained in:
parent
d803899ccb
commit
ba9765ba1c
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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[];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user