diff --git a/components/Transaction/index.tsx b/components/Transaction/index.tsx index 3bc2bcb..d72ba27 100644 --- a/components/Transaction/index.tsx +++ b/components/Transaction/index.tsx @@ -4,6 +4,7 @@ import { useSnapshot } from "valtio"; import state from "../../state"; import { modifyTransaction, + prepareState, prepareTransaction, TransactionState, } from "../../state/transactions"; @@ -27,39 +28,43 @@ const Transaction: FC = ({ const { accounts, editorSettings } = useSnapshot(state); const { selectedAccount, - selectedDestAccount, selectedTransaction, - txFields, txIsDisabled, txIsLoading, viewType, editorSavedValue, + editorValue, } = txState; const setState = useCallback( (pTx?: Partial) => { - modifyTransaction(header, pTx); + return modifyTransaction(header, pTx); }, [header] ); - const prepareOptions = useCallback(() => { - const TransactionType = selectedTransaction?.value; - const Destination = selectedDestAccount?.value; - const Account = selectedAccount?.value; + const prepareOptions = useCallback( + (state: TransactionState = txState) => { + const { + selectedTransaction, + selectedDestAccount, + selectedAccount, + txFields, + } = state; - return prepareTransaction({ - ...txFields, - TransactionType, - Destination, - Account, - }); - }, [ - selectedAccount?.value, - selectedDestAccount?.value, - selectedTransaction?.value, - txFields, - ]); + const TransactionType = selectedTransaction?.value; + const Destination = selectedDestAccount?.value; + const Account = selectedAccount?.value; + + return prepareTransaction({ + ...txFields, + TransactionType, + Destination, + Account, + }); + }, + [txState] + ); useEffect(() => { const transactionType = selectedTransaction?.value; @@ -72,6 +77,15 @@ const Transaction: FC = ({ }, [txIsLoading, selectedTransaction, selectedAccount, accounts, setState]); const submitTest = useCallback(async () => { + let st: TransactionState | undefined; + if (viewType === "json") { + // save the editor state first + const pst = prepareState(editorValue); + if (!pst) return; + + st = setState(pst); + } + const account = accounts.find( acc => acc.address === selectedAccount?.value ); @@ -80,7 +94,7 @@ const Transaction: FC = ({ setState({ txIsLoading: true }); try { - const options = prepareOptions(); + const options = prepareOptions(st); const logPrefix = header ? `${header.split(".")[0]}: ` : undefined; await sendTransaction(account, options, { logPrefix }); @@ -92,12 +106,14 @@ const Transaction: FC = ({ } setState({ txIsLoading: false }); }, [ + viewType, + editorValue, accounts, selectedTransaction?.value, txIsDisabled, setState, - prepareOptions, selectedAccount?.value, + prepareOptions, header, ]); @@ -115,7 +131,12 @@ const Transaction: FC = ({ return ( {viewType === "json" ? ( - + ) : ( )} diff --git a/components/Transaction/json.tsx b/components/Transaction/json.tsx index be8e424..52052bf 100644 --- a/components/Transaction/json.tsx +++ b/components/Transaction/json.tsx @@ -5,7 +5,7 @@ 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, { TransactionState } from "../../state"; +import state, { parseJSON, prepareState, TransactionState } from "../../state"; import Text from "../Text"; import Flex from "../Flex"; import { Link } from ".."; @@ -20,25 +20,23 @@ interface JsonProps { value?: string; header?: string; setState: (pTx?: Partial | undefined) => void; + state: TransactionState; } -function parseJSON(str: string): any | undefined { - try { - const parsed = JSON.parse(str); - return typeof parsed === "object" ? parsed : undefined; - } catch (error) { - return undefined; - } -} - -export const TxJson: FC = ({ value = "", header, setState }) => { +export const TxJson: FC = ({ + value = "", + state: txState, + header, + setState, +}) => { const { editorSettings } = useSnapshot(state); + const { editorValue = value } = txState; const { theme } = useTheme(); - const [editorValue, setEditorValue] = useState(value); const [hasUnsaved, setHasUnsaved] = useState(false); useEffect(() => { - setEditorValue(value); + setState({ editorValue: value }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]); useEffect(() => { @@ -47,79 +45,14 @@ export const TxJson: FC = ({ value = "", header, setState }) => { }, [editorValue, value]); const saveState = (value: string) => { - const options = parseJSON(value); - if (!options) return alert("Cannot save dirty editor"); - - const { Account, TransactionType, Destination, ...rest } = options; - let tx: Partial = {}; - - if (Account) { - const acc = state.accounts.find(acc => acc.address === Account); - if (acc) { - tx.selectedAccount = { - label: acc.name, - value: acc.address, - }; - } else { - tx.selectedAccount = { - label: Account, - value: Account, - }; - } - } else { - tx.selectedAccount = null; - } - - if (TransactionType) { - tx.selectedTransaction = { - label: TransactionType, - value: TransactionType, - }; - } else { - tx.selectedTransaction = null; - } - - if (Destination) { - const dest = state.accounts.find(acc => acc.address === Destination); - if (dest) { - tx.selectedDestAccount = { - label: dest.name, - value: dest.address, - }; - } else { - tx.selectedDestAccount = { - label: Destination, - value: Destination, - }; - } - } - - Object.keys(rest).forEach(field => { - const value = rest[field]; - console.log({ field, value }); - if (field === "Amount") { - rest[field] = { - type: "currency", - value: +value / 1000000, // TODO handle object currencies - }; - } else if (typeof value === "object") { - rest[field] = { - type: "json", - value, - }; - } - }); - - tx.txFields = rest; - tx.editorSavedValue = null; - - setState(tx); + const tx = prepareState(value); + if (tx) setState(tx); }; const discardChanges = () => { let discard = confirm("Are you sure to discard these changes"); if (discard) { - setEditorValue(value); + setState({ editorValue: value }); } }; @@ -135,7 +68,7 @@ export const TxJson: FC = ({ value = "", header, setState }) => { if (!discard) { setState({ viewType: "json", editorSavedValue: value }); } else { - setEditorValue(value); + setState({ editorValue: value }); } }; @@ -156,7 +89,7 @@ export const TxJson: FC = ({ value = "", header, setState }) => { monaco.editor.defineTheme("light", light as any); }} value={editorValue} - onChange={val => setEditorValue(val || "")} + onChange={val => setState({ editorValue: val })} onMount={(editor, monaco) => { editor.updateOptions({ minimap: { enabled: false }, diff --git a/state/transactions.ts b/state/transactions.ts index 1b58f5c..2d87f3e 100644 --- a/state/transactions.ts +++ b/state/transactions.ts @@ -1,6 +1,7 @@ import { proxy } from 'valtio'; import { deepEqual } from '../utils/object'; import transactionsData from "../content/transactions.json"; +import state from '.'; export type SelectOption = { value: string; @@ -15,7 +16,8 @@ export interface TransactionState { txIsDisabled: boolean; txFields: TxFields; viewType: 'json' | 'ui', - editorSavedValue: null | string + editorSavedValue: null | string, + editorValue?: string } @@ -68,14 +70,15 @@ export const modifyTransaction = ( } if (!tx) { + const state = { + ...defaultTransaction, + ...partialTx, + } transactionsState.transactions.push({ header, - state: { - ...defaultTransaction, - ...partialTx, - }, + state, }); - return; + return state; } if (opts.replaceState) { @@ -84,7 +87,7 @@ export const modifyTransaction = ( ...partialTx, } tx.state = repTx - return + return repTx } Object.keys(partialTx).forEach(k => { @@ -93,8 +96,11 @@ export const modifyTransaction = ( const p = partialTx as any; if (!deepEqual(s[k], p[k])) s[k] = p[k]; }); + + return tx.state }; +// state to tx options export const prepareTransaction = (data: any) => { let options = { ...data }; @@ -134,4 +140,85 @@ export const prepareTransaction = (data: any) => { return options } +// editor value to state +export const prepareState = (value?: string) => { + const options = parseJSON(value); + if (!options) return alert("Cannot save dirty editor"); + + const { Account, TransactionType, Destination, ...rest } = options; + let tx: Partial = {}; + + if (Account) { + const acc = state.accounts.find(acc => acc.address === Account); + if (acc) { + tx.selectedAccount = { + label: acc.name, + value: acc.address, + }; + } else { + tx.selectedAccount = { + label: Account, + value: Account, + }; + } + } else { + tx.selectedAccount = null; + } + + if (TransactionType) { + tx.selectedTransaction = { + label: TransactionType, + value: TransactionType, + }; + } else { + tx.selectedTransaction = null; + } + + if (Destination) { + const dest = state.accounts.find(acc => acc.address === Destination); + if (dest) { + tx.selectedDestAccount = { + label: dest.name, + value: dest.address, + }; + } else { + tx.selectedDestAccount = { + label: Destination, + value: Destination, + }; + } + } + + Object.keys(rest).forEach(field => { + const value = rest[field]; + console.log({ field, value }); + if (field === "Amount") { + rest[field] = { + type: "currency", + value: +value / 1000000, // TODO handle object currencies + }; + } else if (typeof value === "object") { + rest[field] = { + type: "json", + value, + }; + } + }); + + tx.txFields = rest; + tx.editorSavedValue = null; + + return tx +} + +export const parseJSON = (str?: string | null): any | undefined => { + if (!str) return undefined + try { + const parsed = JSON.parse(str); + return typeof parsed === "object" ? parsed : undefined; + } catch (error) { + return undefined; + } +} + export { transactionsData }