Implement auto save
This commit is contained in:
@@ -4,6 +4,7 @@ import { useSnapshot } from "valtio";
|
||||
import state from "../../state";
|
||||
import {
|
||||
modifyTransaction,
|
||||
prepareState,
|
||||
prepareTransaction,
|
||||
TransactionState,
|
||||
} from "../../state/transactions";
|
||||
@@ -27,23 +28,30 @@ const Transaction: FC<TransactionProps> = ({
|
||||
const { accounts, editorSettings } = useSnapshot(state);
|
||||
const {
|
||||
selectedAccount,
|
||||
selectedDestAccount,
|
||||
selectedTransaction,
|
||||
txFields,
|
||||
txIsDisabled,
|
||||
txIsLoading,
|
||||
viewType,
|
||||
editorSavedValue,
|
||||
editorValue,
|
||||
} = txState;
|
||||
|
||||
const setState = useCallback(
|
||||
(pTx?: Partial<TransactionState>) => {
|
||||
modifyTransaction(header, pTx);
|
||||
return modifyTransaction(header, pTx);
|
||||
},
|
||||
[header]
|
||||
);
|
||||
|
||||
const prepareOptions = useCallback(() => {
|
||||
const prepareOptions = useCallback(
|
||||
(state: TransactionState = txState) => {
|
||||
const {
|
||||
selectedTransaction,
|
||||
selectedDestAccount,
|
||||
selectedAccount,
|
||||
txFields,
|
||||
} = state;
|
||||
|
||||
const TransactionType = selectedTransaction?.value;
|
||||
const Destination = selectedDestAccount?.value;
|
||||
const Account = selectedAccount?.value;
|
||||
@@ -54,12 +62,9 @@ const Transaction: FC<TransactionProps> = ({
|
||||
Destination,
|
||||
Account,
|
||||
});
|
||||
}, [
|
||||
selectedAccount?.value,
|
||||
selectedDestAccount?.value,
|
||||
selectedTransaction?.value,
|
||||
txFields,
|
||||
]);
|
||||
},
|
||||
[txState]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionType = selectedTransaction?.value;
|
||||
@@ -72,6 +77,15 @@ const Transaction: FC<TransactionProps> = ({
|
||||
}, [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<TransactionProps> = ({
|
||||
|
||||
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<TransactionProps> = ({
|
||||
}
|
||||
setState({ txIsLoading: false });
|
||||
}, [
|
||||
viewType,
|
||||
editorValue,
|
||||
accounts,
|
||||
selectedTransaction?.value,
|
||||
txIsDisabled,
|
||||
setState,
|
||||
prepareOptions,
|
||||
selectedAccount?.value,
|
||||
prepareOptions,
|
||||
header,
|
||||
]);
|
||||
|
||||
@@ -115,7 +131,12 @@ const Transaction: FC<TransactionProps> = ({
|
||||
return (
|
||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||
{viewType === "json" ? (
|
||||
<TxJson value={jsonValue} header={header} setState={setState} />
|
||||
<TxJson
|
||||
value={jsonValue}
|
||||
header={header}
|
||||
state={txState}
|
||||
setState={setState}
|
||||
/>
|
||||
) : (
|
||||
<TxUI state={txState} setState={setState} />
|
||||
)}
|
||||
|
||||
@@ -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<TransactionState> | 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<JsonProps> = ({ value = "", header, setState }) => {
|
||||
export const TxJson: FC<JsonProps> = ({
|
||||
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<JsonProps> = ({ 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<TransactionState> = {};
|
||||
|
||||
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<JsonProps> = ({ value = "", header, setState }) => {
|
||||
if (!discard) {
|
||||
setState({ viewType: "json", editorSavedValue: value });
|
||||
} else {
|
||||
setEditorValue(value);
|
||||
setState({ editorValue: value });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,7 +89,7 @@ export const TxJson: FC<JsonProps> = ({ 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 },
|
||||
|
||||
@@ -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) {
|
||||
transactionsState.transactions.push({
|
||||
header,
|
||||
state: {
|
||||
const state = {
|
||||
...defaultTransaction,
|
||||
...partialTx,
|
||||
},
|
||||
}
|
||||
transactionsState.transactions.push({
|
||||
header,
|
||||
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<TransactionState> = {};
|
||||
|
||||
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 }
|
||||
|
||||
Reference in New Issue
Block a user