Small explorer query fixes (#1654)

This commit is contained in:
Jaco Greeff 2019-09-24 16:46:40 +02:00 committed by GitHub
parent f19eeaf6f9
commit 5aac644a4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 190 additions and 54 deletions

View File

@ -4,9 +4,9 @@
import { I18nProps } from '@polkadot/react-components/types';
import React from 'react';
import React, { useState } from 'react';
import styled from 'styled-components';
import { Button, FilterOverlay, Input, TxComponent } from '@polkadot/react-components';
import { Button, FilterOverlay, Input } from '@polkadot/react-components';
import { isHex } from '@polkadot/util';
import translate from './translate';
@ -17,68 +17,47 @@ interface Props extends I18nProps {
interface State {
value: string;
isNumber: boolean;
isValid: boolean;
}
class Query extends TxComponent<Props, State> {
public constructor (props: Props) {
super(props);
function stateFromValue (value: string): State {
const isValidHex = isHex(value, 256);
const isNumber = !isValidHex && /^\d+$/.test(value);
const { value } = this.props;
return {
value,
isValid: isValidHex || isNumber
};
}
this.state = this.stateFromValue(value || '');
}
public render (): React.ReactNode {
const { className, t } = this.props;
const { value, isValid } = this.state;
return (
<FilterOverlay className={className}>
<Input
className='explorer--query'
defaultValue={this.props.value}
isError={!isValid && value.length !== 0}
placeholder={t('block hash or number to query')}
onChange={this.setHash}
onEnter={this.submit}
withLabel={false}
>
<Button
icon='play'
onClick={this.onQuery}
ref={this.button}
/>
</Input>
</FilterOverlay>
);
}
private setHash = (value: string): void => {
this.setState(
this.stateFromValue(value)
);
}
private onQuery = (): void => {
const { isValid, value } = this.state;
function Query ({ className, t, value: propsValue }: Props): React.ReactElement<Props> {
const [{ value, isValid }, setState] = useState(stateFromValue(propsValue || ''));
const _setHash = (value: string): void => setState(stateFromValue(value));
const _onQuery = (): void => {
if (isValid && value.length !== 0) {
window.location.hash = `/explorer/query/${value}`;
}
}
};
private stateFromValue (value: string): State {
const isValidHex = isHex(value, 256);
const isNumber = !isValidHex && /^\d+$/.test(value);
return {
value,
isNumber,
isValid: isValidHex || isNumber
};
}
return (
<FilterOverlay className={className}>
<Input
className='explorer--query'
defaultValue={propsValue}
isError={!isValid && value.length !== 0}
placeholder={t('block hash or number to query')}
onChange={_setHash}
onEnter={_onQuery}
withLabel={false}
>
<Button
icon='play'
onClick={_onQuery}
/>
</Input>
</FilterOverlay>
);
}
export default translate(styled(Query)`

View File

@ -0,0 +1,157 @@
// Copyright 2017-2019 @polkadot/react-components authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { SignerPayloadJSON } from '@polkadot/types/types';
import { BareProps } from '../types';
import { ActionStatus, PartialQueueTxExtrinsic, PartialQueueTxRpc, QueueStatus, QueueTx, QueueTxExtrinsic, QueueTxRpc, QueueTxStatus, SignerCallback } from './types';
import React, { useState } from 'react';
import jsonrpc from '@polkadot/jsonrpc';
import { createType } from '@polkadot/types';
import { QueueProvider } from './Context';
import { SubmittableResult } from '@polkadot/api';
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
export interface Props extends BareProps {
children: React.ReactNode;
}
let nextId = 0;
const REMOVE_TIMEOUT = 7500;
const SUBMIT_RPC = jsonrpc.author.methods.submitAndWatchExtrinsic;
const STATUS_COMPLETE: QueueTxStatus[] = [
// status from subscription
'finalized', 'usurped', 'dropped', 'invalid',
// normal completion
'cancelled', 'error', 'sent'
];
// FIXME We really need to memo these things - this is not really optimal
export default function Queue ({ children }: Props): React.ReactElement<Props> {
const [stqueue, setStQueue] = useState<QueueStatus[]>([]);
const [txqueue, setTxQueue] = useState<QueueTx[]>([]);
const _clearStatus = (id: number): () => void =>
(): void => setTxQueue([...txqueue.map((item): QueueTx =>
item.id === id
? { ...item, status: 'completed' }
: item
)]);
const _queueActions = (status: ActionStatus[]): QueueStatus[] =>
status.map((item: ActionStatus) => {
const id = ++nextId;
const removeItem = (): void => setStQueue([...stqueue.filter((item): boolean => item.id !== id)]);
setTimeout(removeItem, REMOVE_TIMEOUT);
return {
...item,
id,
isCompleted: false,
removeItem
};
});
const queueAction = (status: ActionStatus | ActionStatus[]): number => {
const todos = _queueActions(Array.isArray(status) ? status : [status]);
setStQueue([...stqueue, ...todos]);
return todos[0].id;
};
const queueAdd = (value: QueueTxExtrinsic | QueueTxRpc | QueueTx): number => {
const id = ++nextId;
setTxQueue([...txqueue, {
...value,
id,
removeItem: _clearStatus(id),
rpc: (value as QueueTxRpc).rpc || SUBMIT_RPC,
status: 'queued'
}]);
return id;
};
const _addResultEvents = ({ events = [] }: Partial<SubmittableResult> = {}): void => {
queueAction(
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'
}))
);
};
const queueSetTxStatus = (id: number, status: QueueTxStatus, result?: SubmittableResult, error?: Error): void => {
setTxQueue([...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
)]);
_addResultEvents(result);
if (STATUS_COMPLETE.includes(status)) {
setTimeout(_clearStatus(id), REMOVE_TIMEOUT);
}
};
const queueExtrinsic = ({ accountId, extrinsic, txFailedCb, txSuccessCb, txStartCb, txUpdateCb, isUnsigned }: PartialQueueTxExtrinsic): number =>
queueAdd({
accountId,
extrinsic,
isUnsigned,
txFailedCb,
txSuccessCb,
txStartCb,
txUpdateCb
});
const queuePayload = (payload: SignerPayloadJSON, signerCb: SignerCallback): number =>
queueAdd({
accountId: payload.address,
// this is not great, but the Extrinsic we don't need a submittable
extrinsic: createType('Extrinsic',
{ method: createType('Call', payload.method) },
{ version: payload.version }
) as unknown as SubmittableExtrinsic,
payload,
signerCb
});
const queueRpc = ({ accountId, rpc, values }: PartialQueueTxRpc): number =>
queueAdd({
accountId,
rpc,
values
});
return (
<QueueProvider value={{
queueAction,
queueExtrinsic,
queuePayload,
queueRpc,
queueSetTxStatus,
stqueue,
txqueue
}}>
{children}
</QueueProvider>
);
}