Suggest fee button in transaction ui
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Play } from "phosphor-react";
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { FC, useCallback, useEffect, useMemo } from "react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import state from "../../state";
|
||||
import {
|
||||
@@ -14,7 +14,7 @@ import Button from "../Button";
|
||||
import Flex from "../Flex";
|
||||
import { TxJson } from "./json";
|
||||
import { TxUI } from "./ui";
|
||||
import estimateFee from "../../utils/estimateFee";
|
||||
import { default as _estimateFee } from "../../utils/estimateFee";
|
||||
|
||||
export interface TransactionProps {
|
||||
header: string;
|
||||
@@ -145,23 +145,23 @@ const Transaction: FC<TransactionProps> = ({
|
||||
[editorSavedValue, editorSettings.tabSize, prepareOptions]
|
||||
);
|
||||
|
||||
const [estimatedFee, setEstimatedFee] = useState<string>();
|
||||
useEffect(() => {
|
||||
const ptx = prepareOptions(txState);
|
||||
const account = accounts.find(
|
||||
acc => acc.address === selectedAccount?.value
|
||||
);
|
||||
if (!account) return;
|
||||
const estimateFee = useCallback(
|
||||
async (st?: TransactionState) => {
|
||||
const state = st || txState;
|
||||
const ptx = prepareOptions(state);
|
||||
const account = accounts.find(
|
||||
acc => acc.address === state.selectedAccount?.value
|
||||
);
|
||||
if (!account) return;
|
||||
|
||||
ptx.Account = account.address;
|
||||
ptx.Sequence = account.sequence;
|
||||
ptx.Account = account.address;
|
||||
ptx.Sequence = account.sequence;
|
||||
|
||||
estimateFee(ptx, account, { silent: true })
|
||||
.then(res => res?.base_fee)
|
||||
.then(fee => {
|
||||
setEstimatedFee(fee)
|
||||
});
|
||||
}, [accounts, prepareOptions, selectedAccount?.value, txState]);
|
||||
const res = await _estimateFee(ptx, account, { silent: true });
|
||||
return res?.base_fee;
|
||||
},
|
||||
[accounts, prepareOptions, txState]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||
@@ -171,10 +171,10 @@ const Transaction: FC<TransactionProps> = ({
|
||||
header={header}
|
||||
state={txState}
|
||||
setState={setState}
|
||||
estimatedFee={estimatedFee}
|
||||
estimateFee={estimateFee}
|
||||
/>
|
||||
) : (
|
||||
<TxUI state={txState} setState={setState} estimatedFee={estimatedFee} />
|
||||
<TxUI state={txState} setState={setState} estimateFee={estimateFee} />
|
||||
)}
|
||||
<Flex
|
||||
row
|
||||
|
||||
@@ -29,7 +29,7 @@ interface JsonProps {
|
||||
header?: string;
|
||||
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
||||
state: TransactionState;
|
||||
estimatedFee?: string
|
||||
estimateFee?: () => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export const TxJson: FC<JsonProps> = ({
|
||||
@@ -37,7 +37,7 @@ export const TxJson: FC<JsonProps> = ({
|
||||
state: txState,
|
||||
header,
|
||||
setState,
|
||||
estimatedFee
|
||||
estimateFee,
|
||||
}) => {
|
||||
const { editorSettings, accounts } = useSnapshot(state);
|
||||
const { editorValue = value, selectedTransaction } = txState;
|
||||
@@ -84,7 +84,7 @@ export const TxJson: FC<JsonProps> = ({
|
||||
const path = `file:///${header}`;
|
||||
const monaco = useMonaco();
|
||||
|
||||
const getSchemas = useCallback((): any[] => {
|
||||
const getSchemas = useCallback(async (): Promise<any[]> => {
|
||||
const tt = selectedTransaction?.value;
|
||||
const txObj = transactionsData.find(td => td.TransactionType === tt);
|
||||
|
||||
@@ -100,6 +100,7 @@ export const TxJson: FC<JsonProps> = ({
|
||||
{}
|
||||
);
|
||||
}
|
||||
const estimatedFee = await estimateFee?.();
|
||||
return [
|
||||
{
|
||||
uri: "file:///main-schema.json", // id of the first schema
|
||||
@@ -158,13 +159,16 @@ export const TxJson: FC<JsonProps> = ({
|
||||
...amountSchema,
|
||||
},
|
||||
];
|
||||
}, [accounts, estimatedFee, header, selectedTransaction?.value]);
|
||||
}, [accounts, header, selectedTransaction?.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!monaco) return;
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas: getSchemas(),
|
||||
getSchemas().then(schemas => {
|
||||
console.log('seeung schmea')
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas,
|
||||
});
|
||||
});
|
||||
}, [getSchemas, monaco]);
|
||||
|
||||
@@ -197,12 +201,6 @@ export const TxJson: FC<JsonProps> = ({
|
||||
// register onExit cb
|
||||
const model = editor.getModel();
|
||||
model?.onWillDispose(() => onExit(model.getValue()));
|
||||
|
||||
// set json defaults
|
||||
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas: getSchemas(),
|
||||
});
|
||||
}}
|
||||
theme={theme === "dark" ? "dark" : "light"}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useCallback, useState } from "react";
|
||||
import Container from "../Container";
|
||||
import Flex from "../Flex";
|
||||
import Input from "../Input";
|
||||
@@ -9,22 +9,25 @@ import {
|
||||
TransactionState,
|
||||
transactionsData,
|
||||
TxFields,
|
||||
getTxFields,
|
||||
} from "../../state/transactions";
|
||||
import { useSnapshot } from "valtio";
|
||||
import state from "../../state";
|
||||
import { streamState } from "../DebugStream";
|
||||
import { Label } from "..";
|
||||
import { Button } from "..";
|
||||
|
||||
interface UIProps {
|
||||
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
||||
setState: (
|
||||
pTx?: Partial<TransactionState> | undefined
|
||||
) => TransactionState | undefined;
|
||||
state: TransactionState;
|
||||
estimatedFee?: string;
|
||||
estimateFee?: (state?: TransactionState) => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export const TxUI: FC<UIProps> = ({
|
||||
state: txState,
|
||||
setState,
|
||||
estimatedFee,
|
||||
estimateFee,
|
||||
}) => {
|
||||
const { accounts } = useSnapshot(state);
|
||||
const {
|
||||
@@ -51,32 +54,54 @@ export const TxUI: FC<UIProps> = ({
|
||||
}))
|
||||
.filter(acc => acc.value !== selectedAccount?.value);
|
||||
|
||||
const resetOptions = (tt: string) => {
|
||||
const txFields: TxFields | undefined = transactionsData.find(
|
||||
tx => tx.TransactionType === tt
|
||||
);
|
||||
const [feeLoading, setFeeLoading] = useState(false);
|
||||
|
||||
if (!txFields) return setState({ txFields: {} });
|
||||
|
||||
const _txFields = Object.keys(txFields)
|
||||
.filter(key => !["TransactionType", "Account", "Sequence"].includes(key))
|
||||
.reduce<TxFields>(
|
||||
(tf, key) => ((tf[key as keyof TxFields] = (txFields as any)[key]), tf),
|
||||
{}
|
||||
);
|
||||
|
||||
if (!_txFields.Destination) setState({ selectedDestAccount: null });
|
||||
setState({ txFields: _txFields });
|
||||
};
|
||||
const resetOptions = useCallback(
|
||||
(tt: string) => {
|
||||
const fields = getTxFields(tt);
|
||||
if (!fields.Destination) setState({ selectedDestAccount: null });
|
||||
return setState({ txFields: fields });
|
||||
},
|
||||
[setState]
|
||||
);
|
||||
|
||||
const handleSetAccount = (acc: SelectOption) => {
|
||||
setState({ selectedAccount: acc });
|
||||
streamState.selectedAccount = acc;
|
||||
};
|
||||
|
||||
const handleSetField = useCallback(
|
||||
(field: keyof TxFields, value: string, opFields?: TxFields) => {
|
||||
const fields = opFields || txFields;
|
||||
const obj = fields[field];
|
||||
setState({
|
||||
txFields: {
|
||||
...fields,
|
||||
[field]: typeof obj === "object" ? { ...obj, $value: value } : value,
|
||||
},
|
||||
});
|
||||
},
|
||||
[setState, txFields]
|
||||
);
|
||||
|
||||
const handleEstimateFee = useCallback(
|
||||
async (state?: TransactionState) => {
|
||||
setFeeLoading(true);
|
||||
|
||||
const fee = await estimateFee?.(state);
|
||||
if (fee) handleSetField("Fee", fee, state?.txFields);
|
||||
|
||||
setFeeLoading(false);
|
||||
},
|
||||
[estimateFee, handleSetField]
|
||||
);
|
||||
|
||||
const handleChangeTxType = (tt: SelectOption) => {
|
||||
setState({ selectedTransaction: tt });
|
||||
resetOptions(tt.value);
|
||||
|
||||
const newState = resetOptions(tt.value);
|
||||
|
||||
handleEstimateFee(newState);
|
||||
};
|
||||
|
||||
const specialFields = ["TransactionType", "Account", "Destination"];
|
||||
@@ -85,6 +110,8 @@ export const TxUI: FC<UIProps> = ({
|
||||
k => !specialFields.includes(k)
|
||||
) as [keyof TxFields];
|
||||
|
||||
console.log("render ui");
|
||||
|
||||
return (
|
||||
<Container
|
||||
css={{
|
||||
@@ -181,10 +208,7 @@ export const TxUI: FC<UIProps> = ({
|
||||
|
||||
let isXrp = typeof _value === "object" && _value.$type === "xrp";
|
||||
|
||||
const hint =
|
||||
field === "Fee" && estimatedFee
|
||||
? `Suggested Fee: ${estimatedFee}`
|
||||
: undefined;
|
||||
const isFee = field === "Fee";
|
||||
return (
|
||||
<Flex column key={field} css={{ mb: "$2", pr: "1px" }}>
|
||||
<Flex
|
||||
@@ -193,6 +217,7 @@ export const TxUI: FC<UIProps> = ({
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
@@ -201,24 +226,30 @@ export const TxUI: FC<UIProps> = ({
|
||||
<Input
|
||||
value={value}
|
||||
onChange={e => {
|
||||
setState({
|
||||
txFields: {
|
||||
...txFields,
|
||||
[field]:
|
||||
typeof _value === "object"
|
||||
? { ..._value, $value: e.target.value }
|
||||
: e.target.value,
|
||||
},
|
||||
});
|
||||
handleSetField(field, e.target.value);
|
||||
}}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
{isFee && (
|
||||
<Button
|
||||
size="xs"
|
||||
variant="primary"
|
||||
outline
|
||||
isLoading={feeLoading}
|
||||
css={{
|
||||
position: "absolute",
|
||||
right: "$2",
|
||||
fontSize: "$xs",
|
||||
cursor: "pointer",
|
||||
alignContent: "center",
|
||||
display: "flex",
|
||||
}}
|
||||
onClick={() => handleEstimateFee()}
|
||||
>
|
||||
Suggest
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
<Label
|
||||
css={{ color: "$success", textAlign: "right", mt: "$1", mb: 0, fontSize: "$sm" }}
|
||||
>
|
||||
{hint}
|
||||
</Label>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -93,7 +93,7 @@ export const modifyTransaction = (
|
||||
Object.keys(partialTx).forEach(k => {
|
||||
// Typescript mess here, but is definetly safe!
|
||||
const s = tx.state as any;
|
||||
const p = partialTx as any;
|
||||
const p = partialTx as any; // ? Make copy
|
||||
if (!deepEqual(s[k], p[k])) s[k] = p[k];
|
||||
});
|
||||
|
||||
@@ -222,4 +222,24 @@ export const prepareState = (value: string, txState: TransactionState) => {
|
||||
return tx
|
||||
}
|
||||
|
||||
export const getTxFields = (tt: string) => {
|
||||
const txFields: TxFields | undefined = transactionsData.find(
|
||||
tx => tx.TransactionType === tt
|
||||
);
|
||||
|
||||
if (!txFields) return {}
|
||||
|
||||
let _txFields = Object.keys(txFields)
|
||||
.filter(
|
||||
key => !["TransactionType", "Account", "Sequence"].includes(key)
|
||||
)
|
||||
.reduce<TxFields>(
|
||||
(tf, key) => (
|
||||
(tf[key as keyof TxFields] = (txFields as any)[key]), tf
|
||||
),
|
||||
{}
|
||||
);
|
||||
return _txFields
|
||||
}
|
||||
|
||||
export { transactionsData }
|
||||
|
||||
Reference in New Issue
Block a user