Compare commits
9 Commits
transactio
...
feat/tabs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bff01b4a9f | ||
|
|
de5380d6f3 | ||
|
|
eda2a9596a | ||
|
|
4f042ef3b7 | ||
|
|
17c67822a9 | ||
|
|
e6f613ae0b | ||
|
|
9b822cfda4 | ||
|
|
739918647d | ||
|
|
1f334d6253 |
@@ -350,7 +350,7 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
borderBottom: props.card ? "1px solid $mauve6" : undefined,
|
borderBottom: props.card ? "1px solid $mauve6" : undefined,
|
||||||
"@hover": {
|
"@hover": {
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
background: "$mauve3",
|
background: "$backgroundAlt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
86
components/DebugStream.tsx
Normal file
86
components/DebugStream.tsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useSnapshot } from "valtio";
|
||||||
|
import { Select } from ".";
|
||||||
|
import state from "../state";
|
||||||
|
import LogBox from "./LogBox";
|
||||||
|
import Text from "./Text";
|
||||||
|
|
||||||
|
const DebugStream = () => {
|
||||||
|
const snap = useSnapshot(state);
|
||||||
|
|
||||||
|
const accountOptions = snap.accounts.map(acc => ({
|
||||||
|
label: acc.name,
|
||||||
|
value: acc.address,
|
||||||
|
}));
|
||||||
|
const [selectedAccount, setSelectedAccount] = useState<typeof accountOptions[0] | null>(null);
|
||||||
|
|
||||||
|
const renderNav = () => (
|
||||||
|
<>
|
||||||
|
<Text css={{ mx: "$2", fontSize: "inherit" }}>Account: </Text>
|
||||||
|
<Select
|
||||||
|
instanceId="debugStreamAccount"
|
||||||
|
placeholder="Select account"
|
||||||
|
options={accountOptions}
|
||||||
|
hideSelectedOptions
|
||||||
|
value={selectedAccount}
|
||||||
|
onChange={acc => setSelectedAccount(acc as any)}
|
||||||
|
css={{ width: "30%" }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const account = selectedAccount?.value;
|
||||||
|
if (!account) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const socket = new WebSocket(`wss://hooks-testnet-debugstream.xrpl-labs.com/${account}`);
|
||||||
|
|
||||||
|
const onOpen = () => {
|
||||||
|
state.debugLogs = [];
|
||||||
|
state.debugLogs.push({
|
||||||
|
type: "success",
|
||||||
|
message: `Debug stream opened for account ${account}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onError = () => {
|
||||||
|
state.debugLogs.push({
|
||||||
|
type: "error",
|
||||||
|
message: "Something went wrong in establishing connection!",
|
||||||
|
});
|
||||||
|
setSelectedAccount(null);
|
||||||
|
};
|
||||||
|
const onMessage = (event: any) => {
|
||||||
|
if (!event.data) return;
|
||||||
|
state.debugLogs.push({
|
||||||
|
type: "log",
|
||||||
|
message: event.data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.addEventListener("open", onOpen);
|
||||||
|
socket.addEventListener("close", onError);
|
||||||
|
socket.addEventListener("error", onError);
|
||||||
|
socket.addEventListener("message", onMessage);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.removeEventListener("open", onOpen);
|
||||||
|
socket.removeEventListener("close", onError);
|
||||||
|
socket.removeEventListener("message", onMessage);
|
||||||
|
|
||||||
|
socket.close();
|
||||||
|
};
|
||||||
|
}, [selectedAccount]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LogBox
|
||||||
|
enhanced
|
||||||
|
renderNav={renderNav}
|
||||||
|
title="Debug stream"
|
||||||
|
logs={snap.debugLogs}
|
||||||
|
clearLog={() => (state.debugLogs = [])}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DebugStream;
|
||||||
@@ -71,7 +71,7 @@ const HooksEditor = () => {
|
|||||||
keepCurrentModel
|
keepCurrentModel
|
||||||
defaultLanguage={snap.files?.[snap.active]?.language}
|
defaultLanguage={snap.files?.[snap.active]?.language}
|
||||||
language={snap.files?.[snap.active]?.language}
|
language={snap.files?.[snap.active]?.language}
|
||||||
path={`file://work/c/${snap.files?.[snap.active]?.name}`}
|
path={`file:///work/c/${snap.files?.[snap.active]?.name}`}
|
||||||
defaultValue={snap.files?.[snap.active]?.content}
|
defaultValue={snap.files?.[snap.active]?.content}
|
||||||
beforeMount={monaco => {
|
beforeMount={monaco => {
|
||||||
if (!snap.editorCtx) {
|
if (!snap.editorCtx) {
|
||||||
@@ -79,7 +79,7 @@ const HooksEditor = () => {
|
|||||||
monaco.editor.createModel(
|
monaco.editor.createModel(
|
||||||
file.content,
|
file.content,
|
||||||
file.language,
|
file.language,
|
||||||
monaco.Uri.parse(`file://work/c/${file.name}`)
|
monaco.Uri.parse(`file:///work/c/${file.name}`)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, useLayoutEffect } from "react";
|
import React, { useRef, useLayoutEffect, ReactNode } from "react";
|
||||||
import { Notepad, Prohibit } from "phosphor-react";
|
import { Notepad, Prohibit } from "phosphor-react";
|
||||||
import useStayScrolled from "react-stay-scrolled";
|
import useStayScrolled from "react-stay-scrolled";
|
||||||
import NextLink from "next/link";
|
import NextLink from "next/link";
|
||||||
@@ -17,9 +17,11 @@ interface ILogBox {
|
|||||||
title: string;
|
title: string;
|
||||||
clearLog?: () => void;
|
clearLog?: () => void;
|
||||||
logs: ILog[];
|
logs: ILog[];
|
||||||
|
renderNav?: () => ReactNode;
|
||||||
|
enhanced?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
|
const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav, enhanced }) => {
|
||||||
const logRef = useRef<HTMLPreElement>(null);
|
const logRef = useRef<HTMLPreElement>(null);
|
||||||
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
|
const { stayScrolled /*, scrollBottom*/ } = useStayScrolled(logRef);
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container css={{ px: 0, flexShrink: 1 }}>
|
<Container css={{ px: 0, flexShrink: 1 }}>
|
||||||
<Flex css={{ py: "$3" }}>
|
<Flex css={{ py: "$3", alignItems: "center", fontSize: "$sm", fontWeight: 300 }}>
|
||||||
<Heading
|
<Heading
|
||||||
as="h3"
|
as="h3"
|
||||||
css={{
|
css={{
|
||||||
@@ -56,6 +58,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
|
|||||||
>
|
>
|
||||||
<Notepad size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
|
<Notepad size="15px" /> <Text css={{ lineHeight: 1 }}>{title}</Text>
|
||||||
</Heading>
|
</Heading>
|
||||||
|
{renderNav?.()}
|
||||||
<Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
|
<Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
|
||||||
{clearLog && (
|
{clearLog && (
|
||||||
<Button ghost size="xs" onClick={clearLog}>
|
<Button ghost size="xs" onClick={clearLog}>
|
||||||
@@ -84,10 +87,18 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{logs?.map((log, index) => (
|
{logs?.map((log, index) => (
|
||||||
<Box as="span" key={log.type + index}>
|
<Box
|
||||||
{/* <LogText capitalize variant={log.type}>
|
as="span"
|
||||||
{log.type}:{" "}
|
key={log.type + index}
|
||||||
</LogText> */}
|
css={{
|
||||||
|
"@hover": {
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: enhanced ? "$backgroundAlt" : undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
p: "$2 $1",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<LogText variant={log.type}>
|
<LogText variant={log.type}>
|
||||||
{log.message}{" "}
|
{log.message}{" "}
|
||||||
{log.link && (
|
{log.link && (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const Text = styled("span", {
|
|||||||
fontFamily: "$monospace",
|
fontFamily: "$monospace",
|
||||||
lineHeight: "$body",
|
lineHeight: "$body",
|
||||||
color: "$text",
|
color: "$text",
|
||||||
|
wordWrap: 'break-word',
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
log: {
|
log: {
|
||||||
|
|||||||
@@ -1,24 +1,60 @@
|
|||||||
import { useEffect, useState } from "react";
|
import React, { useEffect, useState, Fragment, isValidElement, useCallback } from "react";
|
||||||
import type { ReactNode, ReactElement } from "react";
|
import type { ReactNode, ReactElement } from "react";
|
||||||
import { Button, Stack } from ".";
|
import { Box, Button, Flex, Input, Stack, Text } from ".";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
DialogClose,
|
||||||
|
} from "./Dialog";
|
||||||
|
import { Plus, X } from "phosphor-react";
|
||||||
|
import { styled } from "../stitches.config";
|
||||||
|
|
||||||
|
const ErrorText = styled(Text, {
|
||||||
|
color: "$red9",
|
||||||
|
mt: "$1",
|
||||||
|
display: "block",
|
||||||
|
});
|
||||||
|
|
||||||
interface TabProps {
|
interface TabProps {
|
||||||
header?: string;
|
header?: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO customise strings shown
|
||||||
interface Props {
|
interface Props {
|
||||||
activeIndex?: number;
|
activeIndex?: number;
|
||||||
activeHeader?: string;
|
activeHeader?: string;
|
||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
children: ReactElement<TabProps>[];
|
children: ReactElement<TabProps>[];
|
||||||
|
keepAllAlive?: boolean;
|
||||||
|
defaultExtension?: string;
|
||||||
|
forceDefaultExtension?: boolean;
|
||||||
|
onCreateNewTab?: (name: string) => any;
|
||||||
|
onCloseTab?: (index: number, header?: string) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tab = (props: TabProps) => null;
|
export const Tab = (props: TabProps) => null;
|
||||||
|
|
||||||
export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) => {
|
export const Tabs = ({
|
||||||
|
children,
|
||||||
|
activeIndex,
|
||||||
|
activeHeader,
|
||||||
|
headless,
|
||||||
|
keepAllAlive = false,
|
||||||
|
onCreateNewTab,
|
||||||
|
onCloseTab,
|
||||||
|
defaultExtension = "",
|
||||||
|
forceDefaultExtension,
|
||||||
|
}: Props) => {
|
||||||
const [active, setActive] = useState(activeIndex || 0);
|
const [active, setActive] = useState(activeIndex || 0);
|
||||||
const tabProps: TabProps[] = children.map(elem => elem.props);
|
const tabs: TabProps[] = children.map(elem => elem.props);
|
||||||
|
|
||||||
|
const [isNewtabDialogOpen, setIsNewtabDialogOpen] = useState(false);
|
||||||
|
const [tabname, setTabname] = useState("");
|
||||||
|
const [newtabError, setNewtabError] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeIndex) setActive(activeIndex);
|
if (activeIndex) setActive(activeIndex);
|
||||||
@@ -26,10 +62,57 @@ export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) =
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeHeader) {
|
if (activeHeader) {
|
||||||
const idx = tabProps.findIndex(tab => tab.header === activeHeader);
|
const idx = tabs.findIndex(tab => tab.header === activeHeader);
|
||||||
setActive(idx);
|
setActive(idx);
|
||||||
}
|
}
|
||||||
}, [activeHeader, tabProps]);
|
}, [activeHeader, tabs]);
|
||||||
|
|
||||||
|
// when filename changes, reset error
|
||||||
|
useEffect(() => {
|
||||||
|
setNewtabError(null);
|
||||||
|
}, [tabname, setNewtabError]);
|
||||||
|
|
||||||
|
const validateTabname = useCallback(
|
||||||
|
(tabname: string): { error: string | null } => {
|
||||||
|
if (tabs.find(tab => tab.header === tabname)) {
|
||||||
|
return { error: "Name already exists." };
|
||||||
|
}
|
||||||
|
return { error: null };
|
||||||
|
},
|
||||||
|
[tabs]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCreateTab = useCallback(() => {
|
||||||
|
// add default extension in case omitted
|
||||||
|
let _tabname = tabname.includes(".") ? tabname : tabname + defaultExtension;
|
||||||
|
if (forceDefaultExtension && !_tabname.endsWith(defaultExtension)) {
|
||||||
|
_tabname = _tabname + defaultExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chk = validateTabname(_tabname);
|
||||||
|
if (chk.error) {
|
||||||
|
setNewtabError(`Error: ${chk.error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsNewtabDialogOpen(false);
|
||||||
|
setTabname("");
|
||||||
|
// switch to new tab?
|
||||||
|
setActive(tabs.length);
|
||||||
|
|
||||||
|
onCreateNewTab?.(_tabname);
|
||||||
|
}, [tabname, defaultExtension, validateTabname, onCreateNewTab, tabs.length]);
|
||||||
|
|
||||||
|
const handleCloseTab = useCallback(
|
||||||
|
(idx: number) => {
|
||||||
|
if (idx <= active && active !== 0) {
|
||||||
|
setActive(active - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseTab?.(idx, tabs[idx].header);
|
||||||
|
},
|
||||||
|
[active, onCloseTab, tabs]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -40,11 +123,13 @@ export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) =
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
flexWrap: "nowrap",
|
flexWrap: "nowrap",
|
||||||
marginBottom: "-1px",
|
marginBottom: "-1px",
|
||||||
|
width: "100%",
|
||||||
|
overflow: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabProps.map((prop, idx) => (
|
{tabs.map((tab, idx) => (
|
||||||
<Button
|
<Button
|
||||||
key={prop.header}
|
key={tab.header}
|
||||||
role="tab"
|
role="tab"
|
||||||
tabIndex={idx}
|
tabIndex={idx}
|
||||||
onClick={() => setActive(idx)}
|
onClick={() => setActive(idx)}
|
||||||
@@ -59,12 +144,99 @@ export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) =
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{prop.header || idx}
|
{tab.header || idx}
|
||||||
|
{onCloseTab && (
|
||||||
|
<Box
|
||||||
|
as="span"
|
||||||
|
css={{
|
||||||
|
display: "flex",
|
||||||
|
p: "2px",
|
||||||
|
borderRadius: "$full",
|
||||||
|
mr: "-4px",
|
||||||
|
"&:hover": {
|
||||||
|
// boxSizing: "0px 0px 1px",
|
||||||
|
backgroundColor: "$mauve2",
|
||||||
|
color: "$mauve12",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onClick={(ev: React.MouseEvent<HTMLElement>) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
handleCloseTab(idx);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X size="9px" weight="bold" />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
{onCreateNewTab && (
|
||||||
|
<Dialog open={isNewtabDialogOpen} onOpenChange={setIsNewtabDialogOpen}>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button ghost size="sm" css={{ alignItems: "center", px: "$2", mr: "$3" }}>
|
||||||
|
<Plus size="16px" /> {tabs.length === 0 && "Add new tab"}
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogTitle>Create new tab</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
<label>Tabname</label>
|
||||||
|
<Input
|
||||||
|
value={tabname}
|
||||||
|
onChange={e => setTabname(e.target.value)}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
handleCreateTab();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ErrorText>{newtabError}</ErrorText>
|
||||||
|
</DialogDescription>
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
css={{
|
||||||
|
marginTop: 25,
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
gap: "$3",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button outline>Cancel</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button variant="primary" onClick={handleCreateTab}>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
|
||||||
|
<X size="20px" />
|
||||||
|
</Box>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
{tabProps[active].children}
|
{keepAllAlive ? (
|
||||||
|
tabs.map((tab, idx) => {
|
||||||
|
// TODO Maybe rule out fragments as children
|
||||||
|
if (!isValidElement(tab.children)) {
|
||||||
|
if (active !== idx) return null;
|
||||||
|
return tab.children;
|
||||||
|
}
|
||||||
|
let key = tab.children.key || tab.header || idx;
|
||||||
|
let { children } = tab;
|
||||||
|
let { style, ...props } = children.props;
|
||||||
|
return (
|
||||||
|
<children.type
|
||||||
|
key={key}
|
||||||
|
{...props}
|
||||||
|
style={{ ...style, display: active !== idx ? "none" : undefined }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<Fragment key={tabs[active].header || active}>{tabs[active].children}</Fragment>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import dynamic from "next/dynamic";
|
|||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state from "../../state";
|
import state from "../../state";
|
||||||
import { sendTransaction } from "../../state/actions";
|
import { sendTransaction } from "../../state/actions";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState, FC } from "react";
|
||||||
import transactionsData from "../../content/transactions.json";
|
import transactionsData from "../../content/transactions.json";
|
||||||
|
|
||||||
|
const DebugStream = dynamic(() => import("../../components/DebugStream"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
const LogBox = dynamic(() => import("../../components/LogBox"), {
|
const LogBox = dynamic(() => import("../../components/LogBox"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
@@ -18,7 +22,11 @@ const Accounts = dynamic(() => import("../../components/Accounts"), {
|
|||||||
type TxFields = Omit<typeof transactionsData[0], "Account" | "Sequence" | "TransactionType">;
|
type TxFields = Omit<typeof transactionsData[0], "Account" | "Sequence" | "TransactionType">;
|
||||||
type OtherFields = (keyof Omit<TxFields, "Destination">)[];
|
type OtherFields = (keyof Omit<TxFields, "Destination">)[];
|
||||||
|
|
||||||
const Transaction = () => {
|
interface Props {
|
||||||
|
header?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Transaction: FC<Props> = ({ header, ...props }) => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
|
|
||||||
const transactionsOptions = transactionsData.map(tx => ({
|
const transactionsOptions = transactionsData.map(tx => ({
|
||||||
@@ -118,10 +126,15 @@ const Transaction = () => {
|
|||||||
delete options[field];
|
delete options[field];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await sendTransaction(account, {
|
const logPrefix = header ? `${header.split(".")[0]}: ` : undefined;
|
||||||
TransactionType,
|
await sendTransaction(
|
||||||
...options,
|
account,
|
||||||
});
|
{
|
||||||
|
TransactionType,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
{ logPrefix }
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
@@ -130,9 +143,10 @@ const Transaction = () => {
|
|||||||
}
|
}
|
||||||
setTxIsLoading(false);
|
setTxIsLoading(false);
|
||||||
}, [
|
}, [
|
||||||
selectedAccount,
|
header,
|
||||||
selectedDestAccount,
|
selectedAccount?.value,
|
||||||
selectedTransaction,
|
selectedDestAccount?.value,
|
||||||
|
selectedTransaction?.value,
|
||||||
snap.accounts,
|
snap.accounts,
|
||||||
txFields,
|
txFields,
|
||||||
txIsDisabled,
|
txIsDisabled,
|
||||||
@@ -150,7 +164,7 @@ const Transaction = () => {
|
|||||||
const usualFields = ["TransactionType", "Amount", "Account", "Destination"];
|
const usualFields = ["TransactionType", "Amount", "Account", "Destination"];
|
||||||
const otherFields = Object.keys(txFields).filter(k => !usualFields.includes(k)) as OtherFields;
|
const otherFields = Object.keys(txFields).filter(k => !usualFields.includes(k)) as OtherFields;
|
||||||
return (
|
return (
|
||||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }}>
|
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||||
<Container css={{ p: "$3 0", fontSize: "$sm", height: "calc(100% - 28px)" }}>
|
<Container css={{ p: "$3 0", fontSize: "$sm", height: "calc(100% - 28px)" }}>
|
||||||
<Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
|
<Flex column fluid css={{ height: "100%", overflowY: "auto" }}>
|
||||||
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
|
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
|
||||||
@@ -280,21 +294,30 @@ const Transaction = () => {
|
|||||||
|
|
||||||
const Test = () => {
|
const Test = () => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
|
const [tabHeaders, setTabHeaders] = useState<string[]>(["test1.json"]);
|
||||||
return (
|
return (
|
||||||
<Container css={{ py: "$3", px: 0 }}>
|
<Container css={{ py: "$3", px: 0 }}>
|
||||||
<Flex row fluid css={{ justifyContent: 'center', mb: "$2", height: '40vh', minHeight: '300px', p: '$3 $2' }}>
|
<Flex
|
||||||
|
row
|
||||||
|
fluid
|
||||||
|
css={{ justifyContent: "center", mb: "$2", height: "40vh", minHeight: "300px", p: "$3 $2" }}
|
||||||
|
>
|
||||||
<Box css={{ width: "55%", px: "$2" }}>
|
<Box css={{ width: "55%", px: "$2" }}>
|
||||||
<Tabs>
|
<Tabs
|
||||||
{/* TODO Dynamic tabs */}
|
keepAllAlive
|
||||||
<Tab header="test1.json">
|
forceDefaultExtension
|
||||||
<Transaction />
|
defaultExtension=".json"
|
||||||
</Tab>
|
onCreateNewTab={name => setTabHeaders(tabHeaders.concat(name))}
|
||||||
<Tab header="test2.json">
|
onCloseTab={index => setTabHeaders(tabHeaders.filter((_, idx) => idx !== index))}
|
||||||
<Transaction />
|
>
|
||||||
</Tab>
|
{tabHeaders.map(header => (
|
||||||
|
<Tab key={header} header={header}>
|
||||||
|
<Transaction header={header} />
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
<Box css={{ width: "45%", mx: "$2", height: '100%' }}>
|
<Box css={{ width: "45%", mx: "$2", height: "100%" }}>
|
||||||
<Accounts card hideDeployBtn showHookStats />
|
<Accounts card hideDeployBtn showHookStats />
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -302,13 +325,13 @@ const Test = () => {
|
|||||||
<Flex row fluid css={{ borderBottom: "1px solid $mauve8" }}>
|
<Flex row fluid css={{ borderBottom: "1px solid $mauve8" }}>
|
||||||
<Box css={{ width: "50%", borderRight: "1px solid $mauve8" }}>
|
<Box css={{ width: "50%", borderRight: "1px solid $mauve8" }}>
|
||||||
<LogBox
|
<LogBox
|
||||||
title="From Log"
|
title="Development Log"
|
||||||
logs={snap.transactionLogs}
|
logs={snap.transactionLogs}
|
||||||
clearLog={() => (state.transactionLogs = [])}
|
clearLog={() => (state.transactionLogs = [])}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box css={{ width: "50%" }}>
|
<Box css={{ width: "50%" }}>
|
||||||
<LogBox title="To Log" logs={[]} clearLog={() => {}} />
|
<DebugStream />
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import state from '../index';
|
|||||||
// Saves the current editor content to global state
|
// Saves the current editor content to global state
|
||||||
export const saveFile = (showToast: boolean = true) => {
|
export const saveFile = (showToast: boolean = true) => {
|
||||||
const editorModels = state.editorCtx?.getModels();
|
const editorModels = state.editorCtx?.getModels();
|
||||||
|
const sought = '/' + state.files[state.active].name;
|
||||||
const currentModel = editorModels?.find((editorModel) => {
|
const currentModel = editorModels?.find((editorModel) => {
|
||||||
return editorModel.uri.path === `/c/${state.files[state.active].name}`;
|
return editorModel.uri.path.endsWith(sought);
|
||||||
});
|
});
|
||||||
if (state.files.length > 0) {
|
if (state.files.length > 0) {
|
||||||
state.files[state.active].content = currentModel?.getValue() || "";
|
state.files[state.active].content = currentModel?.getValue() || "";
|
||||||
@@ -13,4 +14,4 @@ export const saveFile = (showToast: boolean = true) => {
|
|||||||
if (showToast) {
|
if (showToast) {
|
||||||
toast.success("Saved successfully", { position: "bottom-center" });
|
toast.success("Saved successfully", { position: "bottom-center" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ interface TransactionOptions {
|
|||||||
Destination?: string
|
Destination?: string
|
||||||
[index: string]: any
|
[index: string]: any
|
||||||
}
|
}
|
||||||
|
interface OtherOptions {
|
||||||
|
logPrefix?: string
|
||||||
|
}
|
||||||
|
|
||||||
export const sendTransaction = async (account: IAccount, txOptions: TransactionOptions) => {
|
export const sendTransaction = async (account: IAccount, txOptions: TransactionOptions, options?: OtherOptions) => {
|
||||||
if (!state.client) throw Error('XRPL client not initalized')
|
if (!state.client) throw Error('XRPL client not initalized')
|
||||||
|
|
||||||
const { Fee = "1000", ...opts } = txOptions
|
const { Fee = "1000", ...opts } = txOptions
|
||||||
@@ -21,7 +24,7 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
|
|||||||
Fee, // TODO auto-fillable
|
Fee, // TODO auto-fillable
|
||||||
...opts
|
...opts
|
||||||
};
|
};
|
||||||
console.log({ tx });
|
const { logPrefix = '' } = options || {}
|
||||||
try {
|
try {
|
||||||
const signedAccount = derive.familySeed(account.secret);
|
const signedAccount = derive.familySeed(account.secret);
|
||||||
const { signedTransaction } = sign(tx, signedAccount);
|
const { signedTransaction } = sign(tx, signedAccount);
|
||||||
@@ -32,19 +35,19 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
|
|||||||
if (response.engine_result === "tesSUCCESS") {
|
if (response.engine_result === "tesSUCCESS") {
|
||||||
state.transactionLogs.push({
|
state.transactionLogs.push({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: `Transaction success [${response.engine_result}]: ${response.engine_result_message}`
|
message: `${logPrefix}[${response.engine_result}] ${response.engine_result_message}`
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
state.transactionLogs.push({
|
state.transactionLogs.push({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: `[${response.error || response.engine_result}] ${response.error_exception || response.engine_result_message}`,
|
message: `${logPrefix}[${response.error || response.engine_result}] ${response.error_exception || response.engine_result_message}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
state.transactionLogs.push({
|
state.transactionLogs.push({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: err instanceof Error ? `Error: ${err.message}` : 'Something went wrong, try again later',
|
message: err instanceof Error ? `${logPrefix}Error: ${err.message}` : `${logPrefix}Something went wrong, try again later`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -50,6 +50,7 @@ export interface IState {
|
|||||||
logs: ILog[];
|
logs: ILog[];
|
||||||
deployLogs: ILog[];
|
deployLogs: ILog[];
|
||||||
transactionLogs: ILog[];
|
transactionLogs: ILog[];
|
||||||
|
debugLogs: ILog[];
|
||||||
editorCtx?: typeof monaco.editor;
|
editorCtx?: typeof monaco.editor;
|
||||||
editorSettings: {
|
editorSettings: {
|
||||||
tabSize: number;
|
tabSize: number;
|
||||||
@@ -72,6 +73,7 @@ let initialState: IState = {
|
|||||||
logs: [],
|
logs: [],
|
||||||
deployLogs: [],
|
deployLogs: [],
|
||||||
transactionLogs: [],
|
transactionLogs: [],
|
||||||
|
debugLogs: [],
|
||||||
editorCtx: undefined,
|
editorCtx: undefined,
|
||||||
gistId: undefined,
|
gistId: undefined,
|
||||||
gistOwner: undefined,
|
gistOwner: undefined,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// stitches.config.ts
|
// stitches.config.ts
|
||||||
import type Stitches from '@stitches/react';
|
import type Stitches from '@stitches/react';
|
||||||
import { createStitches } from '@stitches/react';
|
import { createStitches } from '@stitches/react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
gray,
|
gray,
|
||||||
blue,
|
blue,
|
||||||
@@ -47,6 +48,7 @@ export const {
|
|||||||
...yellow,
|
...yellow,
|
||||||
...purple,
|
...purple,
|
||||||
background: "$gray1",
|
background: "$gray1",
|
||||||
|
backgroundAlt: "$gray4",
|
||||||
text: "$gray12",
|
text: "$gray12",
|
||||||
primary: "$plum",
|
primary: "$plum",
|
||||||
white: "white",
|
white: "white",
|
||||||
@@ -304,7 +306,8 @@ export const darkTheme = createTheme('dark', {
|
|||||||
...pinkDark,
|
...pinkDark,
|
||||||
...yellowDark,
|
...yellowDark,
|
||||||
...purpleDark,
|
...purpleDark,
|
||||||
deep: 'rgb(10, 10, 10)'
|
deep: 'rgb(10, 10, 10)',
|
||||||
|
// backgroundA: transparentize(0.1, grayDark.gray1),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user