Readonly tx json view
This commit is contained in:
@@ -1,377 +0,0 @@
|
||||
import { Play } from "phosphor-react";
|
||||
import { FC, useCallback, useEffect } from "react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import transactionsData from "../content/transactions.json";
|
||||
import state, { modifyTransaction } from "../state";
|
||||
import { sendTransaction } from "../state/actions";
|
||||
import Box from "./Box";
|
||||
import Button from "./Button";
|
||||
import Container from "./Container";
|
||||
import { streamState } from "./DebugStream";
|
||||
import Flex from "./Flex";
|
||||
import Input from "./Input";
|
||||
import Text from "./Text";
|
||||
import Select from "./Select";
|
||||
|
||||
type TxFields = Omit<
|
||||
typeof transactionsData[0],
|
||||
"Account" | "Sequence" | "TransactionType"
|
||||
>;
|
||||
|
||||
type OtherFields = (keyof Omit<TxFields, "Destination">)[];
|
||||
|
||||
type SelectOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export interface TransactionState {
|
||||
selectedTransaction: SelectOption | null;
|
||||
selectedAccount: SelectOption | null;
|
||||
selectedDestAccount: SelectOption | null;
|
||||
txIsLoading: boolean;
|
||||
txIsDisabled: boolean;
|
||||
txFields: TxFields;
|
||||
}
|
||||
|
||||
export interface TransactionProps {
|
||||
header: string;
|
||||
state: TransactionState;
|
||||
}
|
||||
|
||||
const Transaction: FC<TransactionProps> = ({
|
||||
header,
|
||||
state: {
|
||||
selectedAccount,
|
||||
selectedDestAccount,
|
||||
selectedTransaction,
|
||||
txFields,
|
||||
txIsDisabled,
|
||||
txIsLoading,
|
||||
},
|
||||
...props
|
||||
}) => {
|
||||
const { accounts } = useSnapshot(state);
|
||||
|
||||
const setState = useCallback(
|
||||
(pTx?: Partial<TransactionState>) => {
|
||||
modifyTransaction(header, pTx);
|
||||
},
|
||||
[header]
|
||||
);
|
||||
|
||||
const transactionsOptions = transactionsData.map(tx => ({
|
||||
value: tx.TransactionType,
|
||||
label: tx.TransactionType,
|
||||
}));
|
||||
|
||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||
label: acc.name,
|
||||
value: acc.address,
|
||||
}));
|
||||
|
||||
const destAccountOptions: SelectOption[] = accounts
|
||||
.map(acc => ({
|
||||
label: acc.name,
|
||||
value: acc.address,
|
||||
}))
|
||||
.filter(acc => acc.value !== selectedAccount?.value);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionType = selectedTransaction?.value;
|
||||
const account = accounts.find(
|
||||
acc => acc.address === selectedAccount?.value
|
||||
);
|
||||
if (!account || !transactionType || txIsLoading) {
|
||||
setState({ txIsDisabled: true });
|
||||
} else {
|
||||
setState({ txIsDisabled: false });
|
||||
}
|
||||
}, [txIsLoading, selectedTransaction, selectedAccount, accounts, setState]);
|
||||
|
||||
useEffect(() => {
|
||||
let _txFields: TxFields | undefined = transactionsData.find(
|
||||
tx => tx.TransactionType === selectedTransaction?.value
|
||||
);
|
||||
if (!_txFields) return setState({ txFields: {} });
|
||||
_txFields = { ..._txFields } as TxFields;
|
||||
|
||||
if (!_txFields.Destination) setState({ selectedDestAccount: null });
|
||||
// @ts-ignore
|
||||
delete _txFields.TransactionType;
|
||||
// @ts-ignore
|
||||
delete _txFields.Account;
|
||||
// @ts-ignore
|
||||
delete _txFields.Sequence;
|
||||
setState({ txFields: _txFields });
|
||||
}, [setState, selectedTransaction]);
|
||||
|
||||
const submitTest = useCallback(async () => {
|
||||
const account = accounts.find(
|
||||
acc => acc.address === selectedAccount?.value
|
||||
);
|
||||
const TransactionType = selectedTransaction?.value;
|
||||
if (!account || !TransactionType || txIsDisabled) return;
|
||||
|
||||
setState({ txIsLoading: true });
|
||||
// setTxIsError(null)
|
||||
try {
|
||||
let options = { ...txFields };
|
||||
|
||||
options.Destination = selectedDestAccount?.value;
|
||||
(Object.keys(options) as (keyof TxFields)[]).forEach(field => {
|
||||
let _value = options[field];
|
||||
// convert currency
|
||||
if (typeof _value === "object" && _value.type === "currency") {
|
||||
if (+_value.value) {
|
||||
options[field] = (+_value.value * 1000000 + "") as any;
|
||||
} else {
|
||||
options[field] = undefined; // 👇 💀
|
||||
}
|
||||
}
|
||||
// handle type: `json`
|
||||
if (typeof _value === "object" && _value.type === "json") {
|
||||
if (typeof _value.value === "object") {
|
||||
options[field] = _value.value as any;
|
||||
} else {
|
||||
try {
|
||||
options[field] = JSON.parse(_value.value);
|
||||
} catch (error) {
|
||||
const message = `Input error for json field '${field}': ${
|
||||
error instanceof Error ? error.message : ""
|
||||
}`;
|
||||
throw Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete unneccesary fields
|
||||
if (!options[field]) {
|
||||
delete options[field];
|
||||
}
|
||||
});
|
||||
const logPrefix = header ? `${header.split(".")[0]}: ` : undefined;
|
||||
await sendTransaction(
|
||||
account,
|
||||
{
|
||||
TransactionType,
|
||||
...options,
|
||||
},
|
||||
{ logPrefix }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof Error) {
|
||||
state.transactionLogs.push({ type: "error", message: error.message });
|
||||
}
|
||||
}
|
||||
setState({ txIsLoading: false });
|
||||
}, [
|
||||
header,
|
||||
setState,
|
||||
selectedAccount?.value,
|
||||
selectedDestAccount?.value,
|
||||
selectedTransaction?.value,
|
||||
accounts,
|
||||
txFields,
|
||||
txIsDisabled,
|
||||
]);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setState({});
|
||||
}, [setState]);
|
||||
|
||||
const handleSetAccount = (acc: SelectOption) => {
|
||||
setState({ selectedAccount: acc });
|
||||
streamState.selectedAccount = acc;
|
||||
};
|
||||
|
||||
const usualFields = ["TransactionType", "Amount", "Account", "Destination"];
|
||||
const otherFields = Object.keys(txFields).filter(
|
||||
k => !usualFields.includes(k)
|
||||
) as OtherFields;
|
||||
return (
|
||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||
<Container
|
||||
css={{
|
||||
p: "$3 01",
|
||||
fontSize: "$sm",
|
||||
height: "calc(100% - 45px)",
|
||||
}}
|
||||
>
|
||||
<Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
mt: "1px",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Transaction type:{" "}
|
||||
</Text>
|
||||
<Select
|
||||
instanceId="transactionsType"
|
||||
placeholder="Select transaction type"
|
||||
options={transactionsOptions}
|
||||
hideSelectedOptions
|
||||
css={{ width: "70%" }}
|
||||
value={selectedTransaction}
|
||||
onChange={(tx: any) => setState({ selectedTransaction: tx })}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Account:{" "}
|
||||
</Text>
|
||||
<Select
|
||||
instanceId="from-account"
|
||||
placeholder="Select your account"
|
||||
css={{ width: "70%" }}
|
||||
options={accountOptions}
|
||||
value={selectedAccount}
|
||||
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
||||
/>
|
||||
</Flex>
|
||||
{txFields.Amount !== undefined && (
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Amount (XRP):{" "}
|
||||
</Text>
|
||||
<Input
|
||||
value={txFields.Amount.value}
|
||||
onChange={e =>
|
||||
setState({
|
||||
txFields: {
|
||||
...txFields,
|
||||
Amount: { type: "currency", value: e.target.value },
|
||||
},
|
||||
})
|
||||
}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{txFields.Destination !== undefined && (
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Destination account:{" "}
|
||||
</Text>
|
||||
<Select
|
||||
instanceId="to-account"
|
||||
placeholder="Select the destination account"
|
||||
css={{ width: "70%" }}
|
||||
options={destAccountOptions}
|
||||
value={selectedDestAccount}
|
||||
isClearable
|
||||
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{otherFields.map(field => {
|
||||
let _value = txFields[field];
|
||||
let value = typeof _value === "object" ? _value.value : _value;
|
||||
value =
|
||||
typeof value === "object"
|
||||
? JSON.stringify(value)
|
||||
: value?.toLocaleString();
|
||||
let isCurrency =
|
||||
typeof _value === "object" && _value.type === "currency";
|
||||
return (
|
||||
<Flex
|
||||
key={field}
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
{field + (isCurrency ? " (XRP)" : "")}:{" "}
|
||||
</Text>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={e =>
|
||||
setState({
|
||||
txFields: {
|
||||
...txFields,
|
||||
[field]:
|
||||
typeof _value === "object"
|
||||
? { ..._value, value: e.target.value }
|
||||
: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</Container>
|
||||
<Flex
|
||||
row
|
||||
css={{
|
||||
justifyContent: "space-between",
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
width: "100%",
|
||||
mb: "$1"
|
||||
}}
|
||||
>
|
||||
<Button outline>VIEW AS JSON</Button>
|
||||
<Flex row>
|
||||
<Button onClick={resetState} outline css={{ mr: "$3" }}>
|
||||
RESET
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={submitTest}
|
||||
isLoading={txIsLoading}
|
||||
disabled={txIsDisabled}
|
||||
>
|
||||
<Play weight="bold" size="16px" />
|
||||
RUN TEST
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Transaction;
|
||||
141
components/Transaction/index.tsx
Normal file
141
components/Transaction/index.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import { Play } from "phosphor-react";
|
||||
import { FC, useCallback, useState } from "react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import state from "../../state";
|
||||
import {
|
||||
modifyTransaction,
|
||||
prepareTransaction,
|
||||
TransactionState,
|
||||
} from "../../state/transactions";
|
||||
import { sendTransaction } from "../../state/actions";
|
||||
import Box from "../Box";
|
||||
import Button from "../Button";
|
||||
import Flex from "../Flex";
|
||||
import { TxJson } from "./json";
|
||||
import { TxUI } from "./ui";
|
||||
|
||||
export interface TransactionProps {
|
||||
header: string;
|
||||
state: TransactionState;
|
||||
}
|
||||
|
||||
const Transaction: FC<TransactionProps> = ({
|
||||
header,
|
||||
state: txState,
|
||||
...props
|
||||
}) => {
|
||||
const { accounts } = useSnapshot(state);
|
||||
const {
|
||||
selectedAccount,
|
||||
selectedDestAccount,
|
||||
selectedTransaction,
|
||||
txFields,
|
||||
txIsDisabled,
|
||||
txIsLoading,
|
||||
} = txState;
|
||||
|
||||
const [viewType, setViewType] = useState<"ui" | "json">("ui");
|
||||
|
||||
const setState = useCallback(
|
||||
(pTx?: Partial<TransactionState>) => {
|
||||
modifyTransaction(header, pTx);
|
||||
},
|
||||
[header]
|
||||
);
|
||||
|
||||
const prepareOptions = useCallback(() => {
|
||||
const TransactionType = selectedTransaction?.value;
|
||||
const Destination = selectedDestAccount?.value;
|
||||
const Account = selectedAccount?.value;
|
||||
|
||||
return prepareTransaction({
|
||||
...txFields,
|
||||
TransactionType,
|
||||
Destination,
|
||||
Account,
|
||||
});
|
||||
}, [
|
||||
selectedAccount?.value,
|
||||
selectedDestAccount?.value,
|
||||
selectedTransaction?.value,
|
||||
txFields,
|
||||
]);
|
||||
|
||||
const submitTest = useCallback(async () => {
|
||||
const account = accounts.find(
|
||||
acc => acc.address === selectedAccount?.value
|
||||
);
|
||||
const TransactionType = selectedTransaction?.value;
|
||||
if (!account || !TransactionType || txIsDisabled) return;
|
||||
|
||||
setState({ txIsLoading: true });
|
||||
try {
|
||||
const options = prepareOptions();
|
||||
const logPrefix = header ? `${header.split(".")[0]}: ` : undefined;
|
||||
|
||||
await sendTransaction(account, options, { logPrefix });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof Error) {
|
||||
state.transactionLogs.push({ type: "error", message: error.message });
|
||||
}
|
||||
}
|
||||
setState({ txIsLoading: false });
|
||||
}, [
|
||||
accounts,
|
||||
selectedTransaction?.value,
|
||||
txIsDisabled,
|
||||
setState,
|
||||
prepareOptions,
|
||||
selectedAccount?.value,
|
||||
header,
|
||||
]);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setState({});
|
||||
}, [setState]);
|
||||
|
||||
return (
|
||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||
{viewType === "json" ? (
|
||||
<TxJson prepareOptions={prepareOptions} />
|
||||
) : (
|
||||
<TxUI state={txState} setState={setState} />
|
||||
)}
|
||||
<Flex
|
||||
row
|
||||
css={{
|
||||
justifyContent: "space-between",
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
width: "100%",
|
||||
mb: "$1",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => setViewType(viewType === "ui" ? "json" : "ui")}
|
||||
outline
|
||||
>
|
||||
{viewType === "ui" ? "VIEW AS JSON" : "EXIT JSON VIEW"}
|
||||
</Button>
|
||||
<Flex row>
|
||||
<Button onClick={resetState} outline css={{ mr: "$3" }}>
|
||||
RESET
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={submitTest}
|
||||
isLoading={txIsLoading}
|
||||
disabled={txIsDisabled}
|
||||
>
|
||||
<Play weight="bold" size="16px" />
|
||||
RUN TEST
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Transaction;
|
||||
56
components/Transaction/json.tsx
Normal file
56
components/Transaction/json.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import Editor, { loader } from "@monaco-editor/react";
|
||||
import { FC, useMemo } from 'react';
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import dark from "../../theme/editor/amy.json";
|
||||
import light from "../../theme/editor/xcode_default.json";
|
||||
import { useSnapshot } from 'valtio';
|
||||
import state from '../../state';
|
||||
|
||||
loader.config({
|
||||
paths: {
|
||||
vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.30.1/min/vs",
|
||||
},
|
||||
});
|
||||
|
||||
interface JsonProps {
|
||||
prepareOptions?: () => any
|
||||
}
|
||||
|
||||
export const TxJson: FC<JsonProps> = ({ prepareOptions }) => {
|
||||
const { editorSettings } = useSnapshot(state);
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const value = useMemo(() => {
|
||||
return JSON.stringify(
|
||||
prepareOptions?.() || {},
|
||||
null,
|
||||
editorSettings.tabSize
|
||||
);
|
||||
}, [editorSettings.tabSize, prepareOptions])
|
||||
|
||||
return (
|
||||
<Editor
|
||||
className="hooks-editor"
|
||||
language={"json"}
|
||||
height="calc(100% - 45px)"
|
||||
beforeMount={monaco => {
|
||||
monaco.editor.defineTheme("dark", dark as any);
|
||||
monaco.editor.defineTheme("light", light as any);
|
||||
}}
|
||||
value={value}
|
||||
onMount={(editor, monaco) => {
|
||||
editor.updateOptions({
|
||||
minimap: { enabled: false },
|
||||
glyphMargin: true,
|
||||
tabSize: editorSettings.tabSize,
|
||||
dragAndDrop: true,
|
||||
fontSize: 14,
|
||||
readOnly: true,
|
||||
});
|
||||
}}
|
||||
theme={theme === "dark" ? "dark" : "light"}
|
||||
/>
|
||||
);
|
||||
};
|
||||
243
components/Transaction/ui.tsx
Normal file
243
components/Transaction/ui.tsx
Normal file
@@ -0,0 +1,243 @@
|
||||
import { FC, useEffect } from "react";
|
||||
import Container from "../Container";
|
||||
import Flex from "../Flex";
|
||||
import Input from "../Input";
|
||||
import Select from "../Select";
|
||||
import Text from "../Text";
|
||||
import {
|
||||
SelectOption,
|
||||
TransactionState,
|
||||
transactionsData,
|
||||
TxFields,
|
||||
OtherFields,
|
||||
} from "../../state/transactions";
|
||||
import { useSnapshot } from "valtio";
|
||||
import state from "../../state";
|
||||
import { streamState } from "../DebugStream";
|
||||
|
||||
interface UIProps {
|
||||
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
||||
state: TransactionState;
|
||||
}
|
||||
|
||||
export const TxUI: FC<UIProps> = ({ state: txState, setState }) => {
|
||||
const { accounts } = useSnapshot(state);
|
||||
const {
|
||||
selectedAccount,
|
||||
selectedDestAccount,
|
||||
selectedTransaction,
|
||||
txFields,
|
||||
txIsLoading,
|
||||
} = txState;
|
||||
|
||||
const handleSetAccount = (acc: SelectOption) => {
|
||||
setState({ selectedAccount: acc });
|
||||
streamState.selectedAccount = acc;
|
||||
};
|
||||
|
||||
const transactionsOptions = transactionsData.map(tx => ({
|
||||
value: tx.TransactionType,
|
||||
label: tx.TransactionType,
|
||||
}));
|
||||
|
||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||
label: acc.name,
|
||||
value: acc.address,
|
||||
}));
|
||||
|
||||
const destAccountOptions: SelectOption[] = accounts
|
||||
.map(acc => ({
|
||||
label: acc.name,
|
||||
value: acc.address,
|
||||
}))
|
||||
.filter(acc => acc.value !== selectedAccount?.value);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionType = selectedTransaction?.value;
|
||||
const account = accounts.find(
|
||||
acc => acc.address === selectedAccount?.value
|
||||
);
|
||||
if (!account || !transactionType || txIsLoading) {
|
||||
setState({ txIsDisabled: true });
|
||||
} else {
|
||||
setState({ txIsDisabled: false });
|
||||
}
|
||||
}, [txIsLoading, selectedTransaction, selectedAccount, accounts, setState]);
|
||||
|
||||
useEffect(() => {
|
||||
const txFields: TxFields | undefined = transactionsData.find(
|
||||
tx => tx.TransactionType === selectedTransaction?.value
|
||||
);
|
||||
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 });
|
||||
}, [setState, selectedTransaction]);
|
||||
|
||||
const usualFields = ["TransactionType", "Amount", "Account", "Destination"];
|
||||
|
||||
const otherFields = Object.keys(txFields).filter(
|
||||
k => !usualFields.includes(k)
|
||||
) as OtherFields;
|
||||
|
||||
return (
|
||||
<Container
|
||||
css={{
|
||||
p: "$3 01",
|
||||
fontSize: "$sm",
|
||||
height: "calc(100% - 45px)",
|
||||
}}
|
||||
>
|
||||
<Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
mt: "1px",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Transaction type:{" "}
|
||||
</Text>
|
||||
<Select
|
||||
instanceId="transactionsType"
|
||||
placeholder="Select transaction type"
|
||||
options={transactionsOptions}
|
||||
hideSelectedOptions
|
||||
css={{ width: "70%" }}
|
||||
value={selectedTransaction}
|
||||
onChange={(tx: any) => setState({ selectedTransaction: tx })}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Account:{" "}
|
||||
</Text>
|
||||
<Select
|
||||
instanceId="from-account"
|
||||
placeholder="Select your account"
|
||||
css={{ width: "70%" }}
|
||||
options={accountOptions}
|
||||
value={selectedAccount}
|
||||
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
||||
/>
|
||||
</Flex>
|
||||
{txFields.Amount !== undefined && (
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Amount (XRP):{" "}
|
||||
</Text>
|
||||
<Input
|
||||
value={txFields.Amount.value}
|
||||
onChange={e =>
|
||||
setState({
|
||||
txFields: {
|
||||
...txFields,
|
||||
Amount: { type: "currency", value: e.target.value },
|
||||
},
|
||||
})
|
||||
}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{txFields.Destination !== undefined && (
|
||||
<Flex
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
Destination account:{" "}
|
||||
</Text>
|
||||
<Select
|
||||
instanceId="to-account"
|
||||
placeholder="Select the destination account"
|
||||
css={{ width: "70%" }}
|
||||
options={destAccountOptions}
|
||||
value={selectedDestAccount}
|
||||
isClearable
|
||||
onChange={(acc: any) => setState({ selectedDestAccount: acc })}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{otherFields.map(field => {
|
||||
let _value = txFields[field];
|
||||
let value = typeof _value === "object" ? _value.value : _value;
|
||||
value =
|
||||
typeof value === "object"
|
||||
? JSON.stringify(value)
|
||||
: value?.toLocaleString();
|
||||
let isCurrency =
|
||||
typeof _value === "object" && _value.type === "currency";
|
||||
return (
|
||||
<Flex
|
||||
key={field}
|
||||
row
|
||||
fluid
|
||||
css={{
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
mb: "$3",
|
||||
pr: "1px",
|
||||
}}
|
||||
>
|
||||
<Text muted css={{ mr: "$3" }}>
|
||||
{field + (isCurrency ? " (XRP)" : "")}:{" "}
|
||||
</Text>
|
||||
<Input
|
||||
value={value}
|
||||
onChange={e =>
|
||||
setState({
|
||||
txFields: {
|
||||
...txFields,
|
||||
[field]:
|
||||
typeof _value === "object"
|
||||
? { ..._value, value: e.target.value }
|
||||
: e.target.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
css={{ width: "70%", flex: "inherit" }}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,28 @@
|
||||
import { proxy } from 'valtio';
|
||||
import { TransactionState } from '../components/Transaction';
|
||||
import { deepEqual } from '../utils/object';
|
||||
import transactionsData from "../content/transactions.json";
|
||||
|
||||
export type SelectOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export interface TransactionState {
|
||||
selectedTransaction: SelectOption | null;
|
||||
selectedAccount: SelectOption | null;
|
||||
selectedDestAccount: SelectOption | null;
|
||||
txIsLoading: boolean;
|
||||
txIsDisabled: boolean;
|
||||
txFields: TxFields;
|
||||
}
|
||||
|
||||
|
||||
export type TxFields = Omit<
|
||||
typeof transactionsData[0],
|
||||
"Account" | "Sequence" | "TransactionType"
|
||||
>;
|
||||
|
||||
export type OtherFields = (keyof Omit<TxFields, "Destination">)[];
|
||||
|
||||
export const defaultTransaction: TransactionState = {
|
||||
selectedTransaction: null,
|
||||
@@ -50,6 +72,11 @@ export const modifyTransaction = (
|
||||
return;
|
||||
}
|
||||
|
||||
if (deepEqual(partialTx, {})) {
|
||||
tx.state = { ...defaultTransaction }
|
||||
console.log({ tx: tx.state, is: tx.state === defaultTransaction })
|
||||
}
|
||||
|
||||
Object.keys(partialTx).forEach(k => {
|
||||
// Typescript mess here, but is definetly safe!
|
||||
const s = tx.state as any;
|
||||
@@ -57,3 +84,43 @@ export const modifyTransaction = (
|
||||
if (!deepEqual(s[k], p[k])) s[k] = p[k];
|
||||
});
|
||||
};
|
||||
|
||||
export const prepareTransaction = (data: any) => {
|
||||
let options = { ...data };
|
||||
|
||||
// options.Destination = selectedDestAccount?.value;
|
||||
(Object.keys(options)).forEach(field => {
|
||||
let _value = options[field];
|
||||
// convert currency
|
||||
if (typeof _value === "object" && _value.type === "currency") {
|
||||
if (+_value.value) {
|
||||
options[field] = (+_value.value * 1000000 + "") as any;
|
||||
} else {
|
||||
options[field] = undefined; // 👇 💀
|
||||
}
|
||||
}
|
||||
// handle type: `json`
|
||||
if (typeof _value === "object" && _value.type === "json") {
|
||||
if (typeof _value.value === "object") {
|
||||
options[field] = _value.value as any;
|
||||
} else {
|
||||
try {
|
||||
options[field] = JSON.parse(_value.value);
|
||||
} catch (error) {
|
||||
const message = `Input error for json field '${field}': ${error instanceof Error ? error.message : ""
|
||||
}`;
|
||||
throw Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete unneccesary fields
|
||||
if (!options[field]) {
|
||||
delete options[field];
|
||||
}
|
||||
});
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
export { transactionsData }
|
||||
|
||||
Reference in New Issue
Block a user