mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-04-24 18:07:57 +00:00
170 lines
4.6 KiB
TypeScript
170 lines
4.6 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/react-signer authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import type { QueueTx, QueueTxMessageSetStatus, QueueTxResult } from '@pezkuwi/react-components/Status/types';
|
|
import type { BareProps as Props } from '@pezkuwi/react-components/types';
|
|
import type { DefinitionRpcExt } from '@pezkuwi/types/types';
|
|
|
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
|
import { Modal, styled } from '@pezkuwi/react-components';
|
|
import { useApi, useQueue } from '@pezkuwi/react-hooks';
|
|
import { assert, isFunction, loggerFormat } from '@pezkuwi/util';
|
|
|
|
import { useTranslation } from './translate.js';
|
|
import TxSigned from './TxSigned.js';
|
|
import TxUnsigned from './TxUnsigned.js';
|
|
|
|
export * from './signers/index.js';
|
|
|
|
interface ItemState {
|
|
currentItem: QueueTx | null;
|
|
isRpc: boolean;
|
|
isVisible: boolean;
|
|
queueSize: number;
|
|
requestAddress: string | null;
|
|
}
|
|
|
|
const NOOP = () => undefined;
|
|
|
|
const AVAIL_STATUS = ['queued', 'qr', 'signing'];
|
|
|
|
async function submitRpc (api: ApiPromise, { method, section }: DefinitionRpcExt, values: unknown[]): Promise<QueueTxResult> {
|
|
try {
|
|
const rpc = api.rpc as unknown as Record<string, Record<string, (...params: unknown[]) => Promise<unknown>>>;
|
|
|
|
assert(isFunction(rpc[section]?.[method]), `api.rpc.${section}.${method} does not exist`);
|
|
|
|
const result = await rpc[section][method](...values);
|
|
|
|
console.log('submitRpc: result ::', loggerFormat(result));
|
|
|
|
return {
|
|
result,
|
|
status: 'sent'
|
|
};
|
|
} catch (error) {
|
|
console.error(error);
|
|
|
|
return {
|
|
error: error as Error,
|
|
status: 'error'
|
|
};
|
|
}
|
|
}
|
|
|
|
async function sendRpc (api: ApiPromise, queueSetTxStatus: QueueTxMessageSetStatus, { id, rpc, values = [] }: QueueTx): Promise<void> {
|
|
if (rpc) {
|
|
queueSetTxStatus(id, 'sending');
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
const { error, result, status } = await submitRpc(api, rpc, values);
|
|
|
|
queueSetTxStatus(id, status, result, error);
|
|
}
|
|
}
|
|
|
|
function extractCurrent (txqueue: QueueTx[]): ItemState {
|
|
const available = txqueue.filter(({ status }) => AVAIL_STATUS.includes(status));
|
|
const currentItem = available[0] || null;
|
|
let isRpc = false;
|
|
let isVisible = false;
|
|
|
|
if (currentItem) {
|
|
if (currentItem.status === 'queued' && !(currentItem.extrinsic || currentItem.payload)) {
|
|
isRpc = true;
|
|
} else if (currentItem.status !== 'signing') {
|
|
isVisible = true;
|
|
}
|
|
}
|
|
|
|
return {
|
|
currentItem,
|
|
isRpc,
|
|
isVisible,
|
|
queueSize: available.length,
|
|
requestAddress: (currentItem?.accountId) || null
|
|
};
|
|
}
|
|
|
|
function Signer ({ children, className = '' }: Props): React.ReactElement<Props> {
|
|
const { api } = useApi();
|
|
const { t } = useTranslation();
|
|
const { queueSetTxStatus, txqueue } = useQueue();
|
|
const [isQueueSubmit, setIsQueueSubmit] = useState(false);
|
|
|
|
const { currentItem, isRpc, isVisible, queueSize, requestAddress } = useMemo(
|
|
() => extractCurrent(txqueue),
|
|
[txqueue]
|
|
);
|
|
|
|
useEffect((): void => {
|
|
(queueSize === 1) && setIsQueueSubmit(false);
|
|
}, [queueSize]);
|
|
|
|
useEffect((): void => {
|
|
isRpc && currentItem &&
|
|
sendRpc(api, queueSetTxStatus, currentItem).catch(console.error);
|
|
}, [api, isRpc, currentItem, queueSetTxStatus]);
|
|
|
|
const _onCancel = useCallback(
|
|
(): void => {
|
|
if (currentItem) {
|
|
const { id, signerCb = NOOP, txFailedCb = NOOP } = currentItem;
|
|
|
|
queueSetTxStatus(id, 'cancelled');
|
|
signerCb(id, null);
|
|
txFailedCb(null);
|
|
}
|
|
},
|
|
[currentItem, queueSetTxStatus]
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{children}
|
|
{currentItem && isVisible && (
|
|
<StyledModal
|
|
className={className}
|
|
header={<>{t('Authorize transaction')}{(queueSize === 1) ? undefined : <> 1/{queueSize}</>}</>}
|
|
key={currentItem.id}
|
|
onClose={_onCancel}
|
|
size='large'
|
|
>
|
|
{currentItem.isUnsigned
|
|
? <TxUnsigned currentItem={currentItem} />
|
|
: (
|
|
<TxSigned
|
|
currentItem={currentItem}
|
|
isQueueSubmit={isQueueSubmit}
|
|
queueSize={queueSize}
|
|
requestAddress={requestAddress}
|
|
setIsQueueSubmit={setIsQueueSubmit}
|
|
/>
|
|
)
|
|
}
|
|
</StyledModal>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
const StyledModal = styled(Modal)`
|
|
.signToggle {
|
|
bottom: 1.5rem;
|
|
left: 1.5rem;
|
|
position: absolute;
|
|
|
|
.ui--Toggle {
|
|
display: inline-block;
|
|
|
|
&+.ui--Toggle {
|
|
margin-left: 1rem;
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
export default React.memo(Signer);
|