mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-07-31 14:45:43 +00:00
147 lines
4.7 KiB
TypeScript
147 lines
4.7 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/react-components authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { Abi, ContractPromise } from '@pezkuwi/api-contract';
|
|
import type { AbiMessage, ContractCallOutcome } from '@pezkuwi/api-contract/types';
|
|
import type { Option } from '@pezkuwi/types';
|
|
import type { ContractInfo } from '@pezkuwi/types/interfaces';
|
|
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
|
|
import { Expander, styled } from '@pezkuwi/react-components';
|
|
import { useApi, useCall } from '@pezkuwi/react-hooks';
|
|
import { formatNumber } from '@pezkuwi/util';
|
|
|
|
import { useTranslation } from '../translate.js';
|
|
import Message from './Message.js';
|
|
|
|
export interface Props {
|
|
className?: string;
|
|
contract?: ContractPromise;
|
|
contractAbi: Abi;
|
|
isLabelled?: boolean;
|
|
isWatching?: boolean;
|
|
trigger?: number;
|
|
onSelect?: (messageIndex: number, resultCb: (messageIndex: number, result?: ContractCallOutcome) => void) => void;
|
|
onSelectConstructor?: (constructorIndex: number) => void;
|
|
withConstructors?: boolean;
|
|
withMessages?: boolean;
|
|
withWasm?: boolean;
|
|
}
|
|
|
|
const READ_ADDR = '0x'.padEnd(66, '0');
|
|
|
|
function sortMessages (messages: AbiMessage[]): [AbiMessage, number][] {
|
|
return messages
|
|
.map((m, index): [AbiMessage, number] => [m, index])
|
|
.sort((a, b) => a[0].identifier.localeCompare(b[0].identifier))
|
|
.sort((a, b) => a[0].isMutating === b[0].isMutating
|
|
? 0
|
|
: a[0].isMutating
|
|
? -1
|
|
: 1
|
|
);
|
|
}
|
|
|
|
function Messages ({ className = '', contract, contractAbi: { constructors, info: { source }, messages }, isLabelled, isWatching, onSelect, onSelectConstructor, trigger, withConstructors, withMessages, withWasm }: Props): React.ReactElement<Props> {
|
|
const { t } = useTranslation();
|
|
const { api } = useApi();
|
|
const optInfo = useCall<Option<ContractInfo>>(contract && api.query.contracts.contractInfoOf, [contract?.address]);
|
|
const [isUpdating, setIsUpdating] = useState(false);
|
|
const [lastResults, setLastResults] = useState<(ContractCallOutcome | undefined)[]>([]);
|
|
|
|
const _onExpander = useCallback(
|
|
(isOpen: boolean): void => {
|
|
isWatching && setIsUpdating(isOpen);
|
|
},
|
|
[isWatching]
|
|
);
|
|
|
|
const _onRefresh = useCallback(
|
|
(): void => {
|
|
optInfo && contract &&
|
|
Promise
|
|
.all(
|
|
messages.map((m) =>
|
|
m.isMutating || m.args.length !== 0
|
|
? Promise.resolve(undefined)
|
|
: contract
|
|
.query[m.method](READ_ADDR, { gasLimit: -1, value: 0 })
|
|
.catch((e: Error) => console.error(`contract.query.${m.method}:: ${e.message}`))
|
|
.then(() => undefined)
|
|
)
|
|
)
|
|
.then(setLastResults)
|
|
.catch(console.error);
|
|
},
|
|
[contract, messages, optInfo]
|
|
);
|
|
|
|
useEffect((): void => {
|
|
(isUpdating || trigger) && optInfo && contract && _onRefresh();
|
|
}, [_onRefresh, contract, isUpdating, optInfo, trigger]);
|
|
|
|
const _setMessageResult = useCallback(
|
|
(_messageIndex: number, _result?: ContractCallOutcome): void => {
|
|
// ignore
|
|
},
|
|
[]
|
|
);
|
|
|
|
const _onSelect = useCallback(
|
|
(index: number) => onSelect && onSelect(index, _setMessageResult),
|
|
[_setMessageResult, onSelect]
|
|
);
|
|
|
|
return (
|
|
<StyledDiv className={`${className} ui--Messages ${isLabelled ? 'isLabelled' : ''}`}>
|
|
{withConstructors && (
|
|
<Expander summary={t('Constructors ({{count}})', { replace: { count: constructors.length } })}>
|
|
{sortMessages(constructors).map(([message, index]) => (
|
|
<Message
|
|
index={index}
|
|
key={index}
|
|
message={message}
|
|
onSelect={onSelectConstructor}
|
|
/>
|
|
))}
|
|
</Expander>
|
|
)}
|
|
{withMessages && (
|
|
<Expander
|
|
onClick={_onExpander}
|
|
summary={t('Messages ({{count}})', { replace: { count: messages.length } })}
|
|
>
|
|
{sortMessages(messages).map(([message, index]) => (
|
|
<Message
|
|
index={index}
|
|
key={index}
|
|
lastResult={lastResults[index]}
|
|
message={message}
|
|
onSelect={_onSelect}
|
|
/>
|
|
))}
|
|
</Expander>
|
|
)}
|
|
{withWasm && source.wasm.length !== 0 && (
|
|
<div>{t('{{size}} WASM bytes', { replace: { size: formatNumber(source.wasm.length) } })}</div>
|
|
)}
|
|
</StyledDiv>
|
|
);
|
|
}
|
|
|
|
const StyledDiv = styled.div`
|
|
padding-bottom: 0.75rem !important;
|
|
|
|
&.isLabelled {
|
|
background: var(--bg-input);
|
|
box-sizing: border-box;
|
|
border: 1px solid var(--border-input);
|
|
border-radius: .28571429rem;
|
|
padding: 1rem 1rem 0.5rem;
|
|
width: 100%;
|
|
}
|
|
`;
|
|
|
|
export default React.memo(Messages);
|