Skip to content

Commit

Permalink
refactor(solive-core): refactor log output
Browse files Browse the repository at this point in the history
  • Loading branch information
chongqiangchen committed May 27, 2023
1 parent cf6772e commit 65beaad
Show file tree
Hide file tree
Showing 18 changed files with 313 additions and 276 deletions.
5 changes: 5 additions & 0 deletions .changeset/thin-colts-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"solive-core": patch
---

refactor log output
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,20 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'no-unused-expressions': 'off',

// React 相关
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }],
'react/prop-types': 'off',
'react/jsx-props-no-spreading': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react/require-default-props': 'warn',
'react/no-unused-prop-types': 'warn',
'jsx-a11y/click-events-have-key-events': 'warn',
'react/jsx-no-bind': 'off',
'react/require-default-props': 'off',
'react/no-array-index-key': 'off',
'jsx-a11y/no-static-element-interactions': 'off',

// import 相关
'import/prefer-default-export': 'off',
Expand Down
61 changes: 34 additions & 27 deletions packages/core/src/components/Console/index.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,66 @@
import ReactJson from "react-json-view";
import { useEffect, useRef } from 'react';

import NavBar from "../NavBar";
import {useEditor} from "../../editor/contexts/editorContext";
import {generateConsoleMessageToShow} from "../../types/console";
import {useConsole} from "../../editor/contexts/consoleContext";
import {useEffect, useRef} from "react";
import ReactJson from 'react-json-view';

import NavBar from '../NavBar';
import { useEditor } from '../../editor/contexts/editorContext';
import { generateConsoleMessageToShow } from '../../types/console';
import { useConsole } from '../../editor/contexts/consoleContext';

const NAVS = [
{label: "Console", id: "console"},
{label: "", id: "empty"},
{ label: 'Console', id: 'console' },
{ label: '', id: 'empty' },
];

type TProps = {
onDeleteClick?: () => void;
}

const Console = (props: TProps) => {
function Console(props: TProps) {
const {
onDeleteClick,
} = props;
const { id } = useEditor();
const {consoles} = useConsole();
const consoleRef = useRef<HTMLDivElement>(null)
const { consoles } = useConsole();
const consoleRef = useRef<HTMLDivElement>(null);

const consoleMessages = consoles || [];

const handleDeleteClick = () => props.onDeleteClick && props.onDeleteClick();
const handleDeleteClick = () => onDeleteClick && onDeleteClick();

useEffect(() => {
consoleRef.current?.scrollTo(0, consoleRef.current.scrollHeight)
}, [consoleMessages])
consoleRef.current?.scrollTo(0, consoleRef.current.scrollHeight);
}, [consoleMessages]);

return (
<div key={id + "_console"} className="h-full flex flex-col flex-1 bg-primary-700 pt-2 rounded-b-[12px]">
<div key={`${id}_console`} className="h-full flex flex-col flex-1 bg-primary-700 pt-2 rounded-b-[12px]">
<NavBar globalId={id} navs={NAVS} onDeleteClick={handleDeleteClick} />
<div ref={consoleRef} className="flex-auto mb-4 text-primary-100 p-2 text-[12px] overflow-scroll">
{consoleMessages.map((item, index) => {
let data
let data;
try {
data = JSON.parse(item.message)
data = JSON.parse(item.message);
} catch (e) { /* empty */ }
if (data instanceof Object) {
return (
<div key={index} className={`flex ${item.type === 'error' ? 'text-red-500' : 'text-white'}`}>
<span>[{new Date(item.timestamp).toLocaleTimeString()}]:</span>
<ReactJson src={data} theme="ocean" collapsed style={{backgroundColor: 'transparent'}} />
</div>
)
} else
return (
<div key={index} className={`${item.type === 'error' ? 'text-red-500' : 'text-white'}`}>
{generateConsoleMessageToShow(item).toString()}
<span>
[
{new Date(item.timestamp).toLocaleTimeString()}
]:
</span>
<ReactJson src={data} theme="ocean" collapsed style={{ backgroundColor: 'transparent' }} />
</div>
)
);
} return (
<div key={index} className={`whitespace-pre-line ${item.type === 'error' ? 'text-red-500' : 'text-white'}`}>
{generateConsoleMessageToShow(item)}
</div>
);
})}
</div>
</div>
)
);
}

export default Console;
160 changes: 94 additions & 66 deletions packages/core/src/components/DeployAndCall/AbiForm.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,128 @@
import {useMemo, useRef, useState} from "react";
import {useForm} from "react-hook-form";
import {ContractInterface, ethers} from "ethers";
import { useMemo, useRef, useState } from 'react';

import RHFInput from "../HookForm/RHFInput";
import {FormProvider} from "../HookForm";
import RHFSelect from "../HookForm/RHFSelect";
import Button from "../Button";
import {useRelayNetwork} from "../../editor/contexts/relayNetworkContext";
import {useConsole} from "../../editor/contexts/consoleContext";
import { useForm } from 'react-hook-form';
import { ethers } from 'ethers';
import { ABIDescription, FunctionDescription } from 'solive-compiler-utils';

import AbiInput from "./AbiInput";
import RHFInput from '../HookForm/RHFInput';
import { FormProvider } from '../HookForm';
import RHFSelect from '../HookForm/RHFSelect';
import Button from '../Button';
import { useRelayNetwork } from '../../editor/contexts/relayNetworkContext';
import { useConsole } from '../../editor/contexts/consoleContext';

const AbiForm = (props: any) => {
const [loading, setLoading] = useState(false);
const {providerRef} = useRelayNetwork();
const {addConsole} = useConsole();
const abiOptions = (abi: ContractInterface) => {
if (Array.isArray(abi)) {
return abi.filter(v => {
return v.type !== 'constructor' && v.type === 'function'
}).map(v => {
return {
label: v.name,
value: v.name,
};
})
} else {
return [];
}
}
import AbiInput from './AbiInput';
import { transformAbiOutputs } from './utils/utils';

type TProps = {
signerAddress: string;
abi: ABIDescription[];
address: string;
name: string;
}

function AbiForm(props: TProps) {
const {
signerAddress,
abi,
address,
} = props;
const [loading, setLoading] = useState(false);
const { providerRef } = useRelayNetwork();
const { addConsole } = useConsole();
const methods = useForm({
defaultValues: {
callFunction: '',
ethAmount: ''
}
ethAmount: '',
},
});
const {watch} = methods;
const { watch } = methods;
const callFunction = watch('callFunction');
const ethAmount = watch('ethAmount');
const abiInputRef = useRef<{ getValues: () => any; watch: (v: string) => any; }>();
const abiInputRef = useRef<{ getValues:() => any; watch: (v: string) => any; }>();

const abiOptions = useMemo(() => {
if (Array.isArray(abi)) {
return abi
.filter((v) => v.type !== 'constructor' && v.type === 'function')
.map((v) => ({
label: v.name || '',
value: v.name || '',
}));
}
return [];
}, [abi]);

const selectedFunction = useMemo(() => {
if (Array.isArray(props.abi)) {
return props.abi.filter((v: { name: string; }) => v.name === callFunction)[0];
} else {
return false
if (Array.isArray(abi)) {
return abi.filter((v) => v.type === 'function' && v.name === callFunction)[0] as FunctionDescription;
}
return {} as FunctionDescription;
}, [callFunction]);

const handleSubmit = async () => {
const submitParams = abiInputRef.current?.getValues();

setLoading(true);

try{
const signer = await providerRef.current.getSigner(props.signerAddress)
const contract = new ethers.Contract(props.address, props.abi, signer);
let result
if(selectedFunction?.stateMutability === "payable") {
try {
const signer = await providerRef.current.getSigner(signerAddress);
const contract = new ethers.Contract(address, abi, signer);
let result;
if (selectedFunction?.stateMutability === 'payable') {
const overrides = {
value: ethers.utils.parseEther(ethAmount),
}
};
result = await contract[callFunction](...Object.values(submitParams || []), overrides);
}else{
} else {
result = await contract[callFunction](...Object.values(submitParams || []));
}
console.log(result)
addConsole([
{
type: "success",
message: JSON.stringify(result)
}
]);
}catch (e) {

if (selectedFunction.stateMutability === 'view') {
addConsole([
{
type: 'success',
message: `Result: ${transformAbiOutputs(selectedFunction.outputs || [], result)}`,
},
]);
} else {
addConsole([
{
type: 'success',
message: JSON.stringify(result),
},
]);
const txResult = await result.wait();
addConsole([
{
type: 'success',
message: JSON.stringify(txResult),
},
]);
}
} catch (e: any) {
addConsole([
{
type: "error",
message: e
}
type: 'error',
message: e.message,
},
]);
}

setLoading(false);
}
};

return <FormProvider methods={methods}>
<RHFSelect name="callFunction" label="ABI" options={abiOptions(props.abi)}/>
{selectedFunction?.stateMutability === "payable"
? <RHFInput name="ethAmount" label="eth amount"/>
: ''}
{selectedFunction?.inputs?.length > 0 && (
<AbiInput ref={abiInputRef} inputs={selectedFunction.inputs}/>
)}
{selectedFunction ? <Button type="primary" loading={loading} onClick={handleSubmit}>Submit</Button> : ''}
</FormProvider>
return (
<FormProvider methods={methods}>
<RHFSelect name="callFunction" label="ABI" options={abiOptions} />
{selectedFunction?.stateMutability === 'payable'
? <RHFInput name="ethAmount" label="eth amount" />
: ''}
{selectedFunction?.inputs && selectedFunction?.inputs.length > 0 && (
<AbiInput ref={abiInputRef} inputs={selectedFunction.inputs} />
)}
{selectedFunction && <Button type="primary" loading={loading} onClick={handleSubmit}>Submit</Button>}
</FormProvider>
);
}

export default AbiForm;
2 changes: 1 addition & 1 deletion packages/core/src/components/DeployAndCall/AbiInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {forwardRef, useImperativeHandle} from "react";
import RHFInput from "../HookForm/RHFInput";
import {FormProvider} from "../HookForm";

import {transformAbiParams} from "./utils";
import {transformAbiParams} from "./utils/utils";

type TProps = {
inputs: ABIParameter[];
Expand Down
43 changes: 27 additions & 16 deletions packages/core/src/components/DeployAndCall/Call.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
import {Disclosure} from '@headlessui/react'
import {ChevronUpIcon} from "@heroicons/react/24/outline";
import { Disclosure } from '@headlessui/react';
import { ChevronUpIcon } from '@heroicons/react/24/outline';

import {useDeployed} from "../../editor/contexts/deployedContext";
import { useDeployed } from '../../editor/contexts/deployedContext';

import AbiForm from "./AbiForm";
import AbiForm from './AbiForm';

const Call = () => {
const {compiledContracts} = useDeployed()
function Call() {
const { compiledContracts } = useDeployed();

return (
<div className="h-full w-full overflow-scroll">
<div className="text-primary-100 font-medium text-[14px] py-2 px-1">
<span>Call</span>
</div>
<div className="mt-2">
{compiledContracts.length === 0 ?
<span className="text-primary-100 text-[12px]">No contract has been deployed yet.</span> : ''}
{compiledContracts.map((v) => {
return <div className="mt-2" key={v.address}>
{compiledContracts.length === 0
? <span className="text-primary-100 text-[12px]">No contract has been deployed yet.</span> : ''}
{compiledContracts.map((v) => (
<div className="mt-2" key={v.address}>
<Disclosure key={v.address}>
{({open}) => (
{({ open }) => (
<>
<Disclosure.Button
className="flex items-center justify-between box-border w-full py-2 px-2 rounded border-none text-white placeholder:text-[#878E95] text-left bg-[#36384A] focus:outline-none focus:shadow-outline">
<span className="block truncate text-[12px] min-h-[12px]">{v.name}</span>
className="flex items-center justify-between box-border w-full py-2 px-2 rounded border-none text-white placeholder:text-[#878E95] text-left bg-[#36384A] focus:outline-none focus:shadow-outline"
>
<span className="block truncate text-[12px] min-h-[12px]">
{v.name}
{' ('}
{v.address}
)
</span>
<ChevronUpIcon
className={`${
open ? 'rotate-180 transform' : ''
} h-3 w-3 transition-transform duration-200 ease-in-out`}
/>
</Disclosure.Button>
<Disclosure.Panel className="px-4 pt-4 pb-2 text-sm text-primary-100">
<AbiForm abi={v.abi} signerAddress={v.signerAddress} address={v.address} name={v.name}
key={v.address}/>
<AbiForm
abi={v.abi}
signerAddress={v.signerAddress}
address={v.address}
name={v.name}
key={v.address}
/>
</Disclosure.Panel>
</>
)}
</Disclosure>
</div>
})}
))}
</div>
</div>
);
Expand Down
Loading

0 comments on commit 65beaad

Please sign in to comment.