Compare commits
18 Commits
feat/debug
...
feature/pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc03c64f0a | ||
|
|
3647aa6274 | ||
|
|
a2a58f0ba9 | ||
|
|
c544a03be4 | ||
|
|
9a09da88ec | ||
|
|
5850551906 | ||
|
|
e35e520d24 | ||
|
|
8077fc5865 | ||
|
|
bff01b4a9f | ||
|
|
de5380d6f3 | ||
|
|
eda2a9596a | ||
|
|
195d33b1db | ||
|
|
4f042ef3b7 | ||
|
|
e6f613ae0b | ||
|
|
9b822cfda4 | ||
|
|
b5b918d877 | ||
|
|
739918647d | ||
|
|
1f334d6253 |
@@ -36,11 +36,13 @@ const AccountDialog = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
const [showSecret, setShowSecret] = useState(false);
|
const [showSecret, setShowSecret] = useState(false);
|
||||||
const activeAccount = snap.accounts.find(account => account.address === activeAccountAddress);
|
const activeAccount = snap.accounts.find(
|
||||||
|
(account) => account.address === activeAccountAddress
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={Boolean(activeAccountAddress)}
|
open={Boolean(activeAccountAddress)}
|
||||||
onOpenChange={open => {
|
onOpenChange={(open) => {
|
||||||
setShowSecret(false);
|
setShowSecret(false);
|
||||||
!open && setActiveAccountAddress(null);
|
!open && setActiveAccountAddress(null);
|
||||||
}}
|
}}
|
||||||
@@ -135,7 +137,7 @@ const AccountDialog = ({
|
|||||||
}}
|
}}
|
||||||
ghost
|
ghost
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => setShowSecret(curr => !curr)}
|
onClick={() => setShowSecret((curr) => !curr)}
|
||||||
>
|
>
|
||||||
{showSecret ? "Hide" : "Show"}
|
{showSecret ? "Hide" : "Show"}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -181,7 +183,11 @@ const AccountDialog = ({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>
|
>
|
||||||
<Button size="sm" ghost css={{ color: "$green11 !important", mt: "$3" }}>
|
<Button
|
||||||
|
size="sm"
|
||||||
|
ghost
|
||||||
|
css={{ color: "$green11 !important", mt: "$3" }}
|
||||||
|
>
|
||||||
<ArrowSquareOut size="15px" />
|
<ArrowSquareOut size="15px" />
|
||||||
</Button>
|
</Button>
|
||||||
</a>
|
</a>
|
||||||
@@ -197,8 +203,10 @@ const AccountDialog = ({
|
|||||||
>
|
>
|
||||||
{activeAccount && activeAccount?.hooks?.length > 0
|
{activeAccount && activeAccount?.hooks?.length > 0
|
||||||
? activeAccount?.hooks
|
? activeAccount?.hooks
|
||||||
.map(i => {
|
.map((i) => {
|
||||||
return `${i?.substring(0, 6)}...${i?.substring(i.length - 4)}`;
|
return `${i?.substring(0, 6)}...${i?.substring(
|
||||||
|
i.length - 4
|
||||||
|
)}`;
|
||||||
})
|
})
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "–"}
|
: "–"}
|
||||||
@@ -223,13 +231,15 @@ interface AccountProps {
|
|||||||
showHookStats?: boolean;
|
showHookStats?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Accounts: FC<AccountProps> = props => {
|
const Accounts: FC<AccountProps> = (props) => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
const [activeAccountAddress, setActiveAccountAddress] = useState<string | null>(null);
|
const [activeAccountAddress, setActiveAccountAddress] = useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAccInfo = async () => {
|
const fetchAccInfo = async () => {
|
||||||
if (snap.clientStatus === "online") {
|
if (snap.clientStatus === "online") {
|
||||||
const requests = snap.accounts.map(acc =>
|
const requests = snap.accounts.map((acc) =>
|
||||||
snap.client?.send({
|
snap.client?.send({
|
||||||
id: acc.address,
|
id: acc.address,
|
||||||
command: "account_info",
|
command: "account_info",
|
||||||
@@ -241,13 +251,15 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
const address = res?.account_data?.Account as string;
|
const address = res?.account_data?.Account as string;
|
||||||
const balance = res?.account_data?.Balance as string;
|
const balance = res?.account_data?.Balance as string;
|
||||||
const sequence = res?.account_data?.Sequence as number;
|
const sequence = res?.account_data?.Sequence as number;
|
||||||
const accountToUpdate = state.accounts.find(acc => acc.address === address);
|
const accountToUpdate = state.accounts.find(
|
||||||
|
(acc) => acc.address === address
|
||||||
|
);
|
||||||
if (accountToUpdate) {
|
if (accountToUpdate) {
|
||||||
accountToUpdate.xrp = balance;
|
accountToUpdate.xrp = balance;
|
||||||
accountToUpdate.sequence = sequence;
|
accountToUpdate.sequence = sequence;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const objectRequests = snap.accounts.map(acc => {
|
const objectRequests = snap.accounts.map((acc) => {
|
||||||
return snap.client?.send({
|
return snap.client?.send({
|
||||||
id: `${acc.address}-hooks`,
|
id: `${acc.address}-hooks`,
|
||||||
command: "account_objects",
|
command: "account_objects",
|
||||||
@@ -257,7 +269,9 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
const objectResponses = await Promise.all(objectRequests);
|
const objectResponses = await Promise.all(objectRequests);
|
||||||
objectResponses.forEach((res: any) => {
|
objectResponses.forEach((res: any) => {
|
||||||
const address = res?.account as string;
|
const address = res?.account as string;
|
||||||
const accountToUpdate = state.accounts.find(acc => acc.address === address);
|
const accountToUpdate = state.accounts.find(
|
||||||
|
(acc) => acc.address === address
|
||||||
|
);
|
||||||
if (accountToUpdate) {
|
if (accountToUpdate) {
|
||||||
accountToUpdate.hooks = res.account_objects
|
accountToUpdate.hooks = res.account_objects
|
||||||
.filter((ac: any) => ac?.LedgerEntryType === "Hook")
|
.filter((ac: any) => ac?.LedgerEntryType === "Hook")
|
||||||
@@ -289,18 +303,19 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
backgroundColor: props.card ? "$deep" : "$mauve1",
|
backgroundColor: props.card ? "$deep" : "$mauve1",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
width: "100%",
|
flex: "1",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
flexShrink: 0,
|
border: "1px solid $mauve6",
|
||||||
borderTop: "1px solid $mauve6",
|
|
||||||
borderRight: "1px solid $mauve6",
|
|
||||||
borderLeft: "1px solid $mauve6",
|
|
||||||
borderBottom: "1px solid $mauve6",
|
|
||||||
borderRadius: props.card ? "$md" : undefined,
|
borderRadius: props.card ? "$md" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
|
<Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
|
||||||
<Flex css={{ py: "$3", borderBottom: props.card ? "1px solid $mauve6" : undefined }}>
|
<Flex
|
||||||
|
css={{
|
||||||
|
py: "$3",
|
||||||
|
borderBottom: props.card ? "1px solid $mauve6" : undefined,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Heading
|
<Heading
|
||||||
as="h3"
|
as="h3"
|
||||||
css={{
|
css={{
|
||||||
@@ -338,7 +353,7 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{snap.accounts.map(account => (
|
{snap.accounts.map((account) => (
|
||||||
<Flex
|
<Flex
|
||||||
column
|
column
|
||||||
key={account.address + account.name}
|
key={account.address + account.name}
|
||||||
@@ -363,7 +378,12 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Text>{account.name} </Text>
|
<Text>{account.name} </Text>
|
||||||
<Text css={{ color: "$mauve9" }}>
|
<Text
|
||||||
|
css={{
|
||||||
|
color: "$mauve9",
|
||||||
|
wordBreak: "break-word",
|
||||||
|
}}
|
||||||
|
>
|
||||||
{account.address} (
|
{account.address} (
|
||||||
{Dinero({
|
{Dinero({
|
||||||
amount: Number(account?.xrp || "0"),
|
amount: Number(account?.xrp || "0"),
|
||||||
@@ -386,10 +406,11 @@ const Accounts: FC<AccountProps> = props => {
|
|||||||
isLoading={account.isLoading}
|
isLoading={account.isLoading}
|
||||||
disabled={
|
disabled={
|
||||||
account.isLoading ||
|
account.isLoading ||
|
||||||
!snap.files.filter(file => file.compiledWatContent).length
|
!snap.files.filter((file) => file.compiledWatContent)
|
||||||
|
.length
|
||||||
}
|
}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={e => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
deployHook(account);
|
deployHook(account);
|
||||||
}}
|
}}
|
||||||
@@ -432,7 +453,7 @@ const ImportAccountDialog = () => {
|
|||||||
name="secret"
|
name="secret"
|
||||||
type="password"
|
type="password"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={e => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
|
||||||
|
|||||||
@@ -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}`)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,14 @@ interface ILogBox {
|
|||||||
enhanced?: boolean;
|
enhanced?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav, enhanced }) => {
|
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);
|
||||||
|
|
||||||
@@ -38,10 +45,23 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
|
|||||||
background: "$mauve1",
|
background: "$mauve1",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
height: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container css={{ px: 0, flexShrink: 1 }}>
|
<Container
|
||||||
<Flex css={{ py: "$3", alignItems: "center", fontSize: "$sm", fontWeight: 300 }}>
|
css={{
|
||||||
|
px: 0,
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
css={{
|
||||||
|
height: "48px",
|
||||||
|
alignItems: "center",
|
||||||
|
fontSize: "$sm",
|
||||||
|
fontWeight: 300,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Heading
|
<Heading
|
||||||
as="h3"
|
as="h3"
|
||||||
css={{
|
css={{
|
||||||
@@ -67,6 +87,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
as="pre"
|
as="pre"
|
||||||
ref={logRef}
|
ref={logRef}
|
||||||
@@ -76,14 +97,14 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "160px",
|
height: "calc(100% - 48px)", // 100% minus the logbox header height
|
||||||
|
overflowY: "auto",
|
||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
fontWeight: "$body",
|
fontWeight: "$body",
|
||||||
fontFamily: "$monospace",
|
fontFamily: "$monospace",
|
||||||
px: "$3",
|
px: "$3",
|
||||||
pb: "$2",
|
pb: "$2",
|
||||||
whiteSpace: "normal",
|
whiteSpace: "normal",
|
||||||
overflowY: "auto",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{logs?.map((log, index) => (
|
{logs?.map((log, index) => (
|
||||||
@@ -96,7 +117,7 @@ const LogBox: React.FC<ILogBox> = ({ title, clearLog, logs, children, renderNav,
|
|||||||
backgroundColor: enhanced ? "$backgroundAlt" : undefined,
|
backgroundColor: enhanced ? "$backgroundAlt" : undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
p: "$2 $1",
|
p: enhanced ? "$2 $1" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LogText variant={log.type}>
|
<LogText variant={log.type}>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const Text = styled("span", {
|
|||||||
fontFamily: "$monospace",
|
fontFamily: "$monospace",
|
||||||
lineHeight: "$body",
|
lineHeight: "$body",
|
||||||
color: "$text",
|
color: "$text",
|
||||||
wordWrap: 'break-word',
|
wordWrap: "break-word",
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
log: {
|
log: {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "./Dialog";
|
} from "./Dialog";
|
||||||
import PanelBox from "./PanelBox";
|
import PanelBox from "./PanelBox";
|
||||||
import { templateFileIds } from '../state/constants';
|
import { templateFileIds } from "../state/constants";
|
||||||
|
|
||||||
const Navigation = () => {
|
const Navigation = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -43,6 +43,7 @@ const Navigation = () => {
|
|||||||
borderBottom: "1px solid $mauve6",
|
borderBottom: "1px solid $mauve6",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
zIndex: 2003,
|
zIndex: 2003,
|
||||||
|
height: "60px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container
|
<Container
|
||||||
@@ -83,8 +84,12 @@ const Navigation = () => {
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Heading css={{ lineHeight: 1 }}>{snap.files?.[0]?.name || "XRPL Hooks"}</Heading>
|
<Heading css={{ lineHeight: 1 }}>
|
||||||
<Text css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}>
|
{snap.files?.[0]?.name || "XRPL Hooks"}
|
||||||
|
</Heading>
|
||||||
|
<Text
|
||||||
|
css={{ fontSize: "$xs", color: "$mauve10", lineHeight: 1 }}
|
||||||
|
>
|
||||||
{snap.files.length > 0 ? "Gist: " : "Playground"}
|
{snap.files.length > 0 ? "Gist: " : "Playground"}
|
||||||
<Text css={{ color: "$mauve12" }}>
|
<Text css={{ color: "$mauve12" }}>
|
||||||
{snap.files.length > 0 &&
|
{snap.files.length > 0 &&
|
||||||
@@ -96,7 +101,10 @@ const Navigation = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
{router.isReady && (
|
{router.isReady && (
|
||||||
<ButtonGroup css={{ marginLeft: "auto" }}>
|
<ButtonGroup css={{ marginLeft: "auto" }}>
|
||||||
<Dialog open={snap.mainModalOpen} onOpenChange={open => (state.mainModalOpen = open)}>
|
<Dialog
|
||||||
|
open={snap.mainModalOpen}
|
||||||
|
onOpenChange={(open) => (state.mainModalOpen = open)}
|
||||||
|
>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button outline>
|
<Button outline>
|
||||||
<FolderOpen size="15px" />
|
<FolderOpen size="15px" />
|
||||||
@@ -157,9 +165,12 @@ const Navigation = () => {
|
|||||||
mb: "$7",
|
mb: "$7",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Hooks add smart contract functionality to the XRP Ledger.
|
Hooks add smart contract functionality to the XRP
|
||||||
|
Ledger.
|
||||||
</Text>
|
</Text>
|
||||||
<Flex css={{ flexDirection: "column", gap: "$2", mt: "$2" }}>
|
<Flex
|
||||||
|
css={{ flexDirection: "column", gap: "$2", mt: "$2" }}
|
||||||
|
>
|
||||||
<Text
|
<Text
|
||||||
css={{
|
css={{
|
||||||
display: "inline-flex",
|
display: "inline-flex",
|
||||||
@@ -244,27 +255,54 @@ const Navigation = () => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PanelBox as="a" href={`/develop/${templateFileIds.starter}`}>
|
<PanelBox
|
||||||
|
as="a"
|
||||||
|
href={`/develop/${templateFileIds.starter}`}
|
||||||
|
>
|
||||||
<Heading>Starter</Heading>
|
<Heading>Starter</Heading>
|
||||||
<Text>Just an empty starter with essential imports</Text>
|
<Text>
|
||||||
|
Just an empty starter with essential imports
|
||||||
|
</Text>
|
||||||
</PanelBox>
|
</PanelBox>
|
||||||
<PanelBox as="a" href={`/develop/${templateFileIds.starter}`}>
|
<PanelBox
|
||||||
|
as="a"
|
||||||
|
href={`/develop/${templateFileIds.starter}`}
|
||||||
|
>
|
||||||
<Heading>Firewall</Heading>
|
<Heading>Firewall</Heading>
|
||||||
<Text>This Hook essentially checks a blacklist of accounts</Text>
|
<Text>
|
||||||
|
This Hook essentially checks a blacklist of accounts
|
||||||
|
</Text>
|
||||||
</PanelBox>
|
</PanelBox>
|
||||||
<PanelBox as="a" href={`/develop/${templateFileIds.accept}`}>
|
<PanelBox
|
||||||
|
as="a"
|
||||||
|
href={`/develop/${templateFileIds.accept}`}
|
||||||
|
>
|
||||||
<Heading>Accept</Heading>
|
<Heading>Accept</Heading>
|
||||||
<Text>This hook just accepts any transaction coming through it</Text>
|
<Text>
|
||||||
|
This hook just accepts any transaction coming through
|
||||||
|
it
|
||||||
|
</Text>
|
||||||
</PanelBox>
|
</PanelBox>
|
||||||
<PanelBox as="a" href={`/develop/${templateFileIds.notary}`}>
|
<PanelBox
|
||||||
|
as="a"
|
||||||
|
href={`/develop/${templateFileIds.notary}`}
|
||||||
|
>
|
||||||
<Heading>Notary</Heading>
|
<Heading>Notary</Heading>
|
||||||
<Text>Collecting signatures for multi-sign transactions</Text>
|
<Text>
|
||||||
|
Collecting signatures for multi-sign transactions
|
||||||
|
</Text>
|
||||||
</PanelBox>
|
</PanelBox>
|
||||||
<PanelBox as="a" href={`/develop/${templateFileIds.carbon}`}>
|
<PanelBox
|
||||||
|
as="a"
|
||||||
|
href={`/develop/${templateFileIds.carbon}`}
|
||||||
|
>
|
||||||
<Heading>Carbon</Heading>
|
<Heading>Carbon</Heading>
|
||||||
<Text>Send a percentage of sum to an address</Text>
|
<Text>Send a percentage of sum to an address</Text>
|
||||||
</PanelBox>
|
</PanelBox>
|
||||||
<PanelBox as="a" href={`/develop/${templateFileIds.peggy}`}>
|
<PanelBox
|
||||||
|
as="a"
|
||||||
|
href={`/develop/${templateFileIds.peggy}`}
|
||||||
|
>
|
||||||
<Heading>Peggy</Heading>
|
<Heading>Peggy</Heading>
|
||||||
<Text>An oracle based stabe coin hook</Text>
|
<Text>An oracle based stabe coin hook</Text>
|
||||||
</PanelBox>
|
</PanelBox>
|
||||||
@@ -313,18 +351,42 @@ const Navigation = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Link href={gistId ? `/develop/${gistId}` : "/develop"} passHref shallow>
|
<Link
|
||||||
<Button as="a" outline={!router.pathname.includes("/develop")} uppercase>
|
href={gistId ? `/develop/${gistId}` : "/develop"}
|
||||||
|
passHref
|
||||||
|
shallow
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
as="a"
|
||||||
|
outline={!router.pathname.includes("/develop")}
|
||||||
|
uppercase
|
||||||
|
>
|
||||||
Develop
|
Develop
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={gistId ? `/deploy/${gistId}` : "/deploy"} passHref shallow>
|
<Link
|
||||||
<Button as="a" outline={!router.pathname.includes("/deploy")} uppercase>
|
href={gistId ? `/deploy/${gistId}` : "/deploy"}
|
||||||
|
passHref
|
||||||
|
shallow
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
as="a"
|
||||||
|
outline={!router.pathname.includes("/deploy")}
|
||||||
|
uppercase
|
||||||
|
>
|
||||||
Deploy
|
Deploy
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href={gistId ? `/test/${gistId}` : "/test"} passHref shallow>
|
<Link
|
||||||
<Button as="a" outline={!router.pathname.includes("/test")} uppercase>
|
href={gistId ? `/test/${gistId}` : "/test"}
|
||||||
|
passHref
|
||||||
|
shallow
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
as="a"
|
||||||
|
outline={!router.pathname.includes("/test")}
|
||||||
|
uppercase
|
||||||
|
>
|
||||||
Test
|
Test
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { gray, grayDark } from "@radix-ui/colors";
|
import { mauve, mauveDark } from "@radix-ui/colors";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { styled } from '../stitches.config';
|
import { styled } from '../stitches.config';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
@@ -11,11 +11,11 @@ const Select: FC<Props> = props => {
|
|||||||
const isDark = theme === "dark";
|
const isDark = theme === "dark";
|
||||||
const colors: any = {
|
const colors: any = {
|
||||||
// primary: pink.pink9,
|
// primary: pink.pink9,
|
||||||
primary: isDark ? grayDark.gray4 : gray.gray4,
|
primary: isDark ? mauveDark.mauve4 : mauve.mauve4,
|
||||||
secondary: isDark ? grayDark.gray8 : gray.gray8,
|
secondary: isDark ? mauveDark.mauve8 : mauve.mauve8,
|
||||||
background: isDark ? "rgb(10, 10, 10)" : "rgb(244, 244, 244)",
|
background: isDark ? "rgb(10, 10, 10)" : "rgb(245, 245, 245)",
|
||||||
searchText: isDark ? grayDark.gray12 : gray.gray12,
|
searchText: isDark ? mauveDark.mauve12 : mauve.mauve12,
|
||||||
placeholder: isDark ? grayDark.gray11 : gray.gray11,
|
placeholder: isDark ? mauveDark.mauve11 : mauve.mauve11,
|
||||||
};
|
};
|
||||||
colors.outline = colors.background;
|
colors.outline = colors.background;
|
||||||
colors.selected = colors.secondary;
|
colors.selected = colors.secondary;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
"react-hot-toast": "^2.1.1",
|
"react-hot-toast": "^2.1.1",
|
||||||
"react-new-window": "^0.2.1",
|
"react-new-window": "^0.2.1",
|
||||||
"react-select": "^5.2.1",
|
"react-select": "^5.2.1",
|
||||||
|
"react-split": "^2.0.14",
|
||||||
"react-stay-scrolled": "^7.4.0",
|
"react-stay-scrolled": "^7.4.0",
|
||||||
"reconnecting-websocket": "^4.4.0",
|
"reconnecting-websocket": "^4.4.0",
|
||||||
"valtio": "^1.2.5",
|
"valtio": "^1.2.5",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { Flex, Box } from "../../components";
|
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state from "../../state";
|
import state from "../../state";
|
||||||
|
import Split from "react-split";
|
||||||
|
|
||||||
const DeployEditor = dynamic(() => import("../../components/DeployEditor"), {
|
const DeployEditor = dynamic(() => import("../../components/DeployEditor"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@@ -19,23 +19,41 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
|
|||||||
const Deploy = () => {
|
const Deploy = () => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
return (
|
return (
|
||||||
<>
|
<Split
|
||||||
<main style={{ display: "flex", flex: 1, height: 'calc(100vh - 30vh - 60px)' }}>
|
direction="vertical"
|
||||||
|
gutterSize={4}
|
||||||
|
gutterAlign="center"
|
||||||
|
sizes={[40, 60]}
|
||||||
|
style={{ height: "calc(100vh - 60px)" }}
|
||||||
|
>
|
||||||
|
<main style={{ display: "flex", flex: 1, position: "relative" }}>
|
||||||
<DeployEditor />
|
<DeployEditor />
|
||||||
</main>
|
</main>
|
||||||
<Flex css={{ flexDirection: "row", width: "100%", minHeight: '225px', height: '30vh' }}>
|
<Split
|
||||||
<Box css={{ width: "100%" }}>
|
direction="horizontal"
|
||||||
|
sizes={[50, 50]}
|
||||||
|
minSize={[320, 160]}
|
||||||
|
gutterSize={4}
|
||||||
|
gutterAlign="center"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ alignItems: "stretch", display: "flex" }}>
|
||||||
<Accounts />
|
<Accounts />
|
||||||
</Box>
|
</div>
|
||||||
<Box css={{ width: "100%" }}>
|
<div>
|
||||||
<LogBox
|
<LogBox
|
||||||
title="Deploy Log"
|
title="Deploy Log"
|
||||||
logs={snap.deployLogs}
|
logs={snap.deployLogs}
|
||||||
clearLog={() => (state.deployLogs = [])}
|
clearLog={() => (state.deployLogs = [])}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</div>
|
||||||
</Flex>
|
</Split>
|
||||||
</>
|
</Split>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import dynamic from "next/dynamic";
|
|||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import Hotkeys from "react-hot-keys";
|
import Hotkeys from "react-hot-keys";
|
||||||
import { Play } from "phosphor-react";
|
import { Play } from "phosphor-react";
|
||||||
|
import Split from "react-split";
|
||||||
|
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { compileCode } from "../../state/actions";
|
import { compileCode } from "../../state/actions";
|
||||||
@@ -19,8 +20,16 @@ const LogBox = dynamic(() => import("../../components/LogBox"), {
|
|||||||
|
|
||||||
const Home: NextPage = () => {
|
const Home: NextPage = () => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Split
|
||||||
|
direction="vertical"
|
||||||
|
sizes={[70, 30]}
|
||||||
|
minSize={[100, 100]}
|
||||||
|
gutterAlign="center"
|
||||||
|
gutterSize={4}
|
||||||
|
style={{ height: "calc(100vh - 60px)" }}
|
||||||
|
>
|
||||||
<main style={{ display: "flex", flex: 1, position: "relative" }}>
|
<main style={{ display: "flex", flex: 1, position: "relative" }}>
|
||||||
<HooksEditor />
|
<HooksEditor />
|
||||||
{snap.files[snap.active]?.name?.split(".")?.[1].toLowerCase() ===
|
{snap.files[snap.active]?.name?.split(".")?.[1].toLowerCase() ===
|
||||||
@@ -65,7 +74,7 @@ const Home: NextPage = () => {
|
|||||||
logs={snap.logs}
|
logs={snap.logs}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</Split>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
import { Container, Flex, Box, Tabs, Tab, Input, Select, Text, Button } from "../../components";
|
import {
|
||||||
|
Container,
|
||||||
|
Flex,
|
||||||
|
Box,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
Text,
|
||||||
|
Button,
|
||||||
|
} from "../../components";
|
||||||
import { Play } from "phosphor-react";
|
import { Play } from "phosphor-react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
|
import Split from "react-split";
|
||||||
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"), {
|
const DebugStream = dynamic(() => import("../../components/DebugStream"), {
|
||||||
@@ -19,13 +30,20 @@ const Accounts = dynamic(() => import("../../components/Accounts"), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// type SelectOption<T> = { value: T, label: string };
|
// type SelectOption<T> = { value: T, label: string };
|
||||||
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) => ({
|
||||||
value: tx.TransactionType,
|
value: tx.TransactionType,
|
||||||
label: tx.TransactionType,
|
label: tx.TransactionType,
|
||||||
}));
|
}));
|
||||||
@@ -33,18 +51,20 @@ const Transaction = () => {
|
|||||||
typeof transactionsOptions[0] | null
|
typeof transactionsOptions[0] | null
|
||||||
>(null);
|
>(null);
|
||||||
|
|
||||||
const accountOptions = snap.accounts.map(acc => ({
|
const accountOptions = snap.accounts.map((acc) => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address,
|
value: acc.address,
|
||||||
}));
|
}));
|
||||||
const [selectedAccount, setSelectedAccount] = useState<typeof accountOptions[0] | null>(null);
|
const [selectedAccount, setSelectedAccount] = useState<
|
||||||
|
typeof accountOptions[0] | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
const destAccountOptions = snap.accounts
|
const destAccountOptions = snap.accounts
|
||||||
.map(acc => ({
|
.map((acc) => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address,
|
value: acc.address,
|
||||||
}))
|
}))
|
||||||
.filter(acc => acc.value !== selectedAccount?.value);
|
.filter((acc) => acc.value !== selectedAccount?.value);
|
||||||
const [selectedDestAccount, setSelectedDestAccount] = useState<
|
const [selectedDestAccount, setSelectedDestAccount] = useState<
|
||||||
typeof destAccountOptions[0] | null
|
typeof destAccountOptions[0] | null
|
||||||
>(null);
|
>(null);
|
||||||
@@ -55,7 +75,9 @@ const Transaction = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const transactionType = selectedTransaction?.value;
|
const transactionType = selectedTransaction?.value;
|
||||||
const account = snap.accounts.find(acc => acc.address === selectedAccount?.value);
|
const account = snap.accounts.find(
|
||||||
|
(acc) => acc.address === selectedAccount?.value
|
||||||
|
);
|
||||||
if (!account || !transactionType || txIsLoading) {
|
if (!account || !transactionType || txIsLoading) {
|
||||||
setTxIsDisabled(true);
|
setTxIsDisabled(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -65,7 +87,7 @@ const Transaction = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let _txFields: TxFields | undefined = transactionsData.find(
|
let _txFields: TxFields | undefined = transactionsData.find(
|
||||||
tx => tx.TransactionType === selectedTransaction?.value
|
(tx) => tx.TransactionType === selectedTransaction?.value
|
||||||
);
|
);
|
||||||
if (!_txFields) return setTxFields({});
|
if (!_txFields) return setTxFields({});
|
||||||
_txFields = { ..._txFields } as TxFields;
|
_txFields = { ..._txFields } as TxFields;
|
||||||
@@ -81,7 +103,9 @@ const Transaction = () => {
|
|||||||
}, [selectedTransaction, setSelectedDestAccount]);
|
}, [selectedTransaction, setSelectedDestAccount]);
|
||||||
|
|
||||||
const submitTest = useCallback(async () => {
|
const submitTest = useCallback(async () => {
|
||||||
const account = snap.accounts.find(acc => acc.address === selectedAccount?.value);
|
const account = snap.accounts.find(
|
||||||
|
(acc) => acc.address === selectedAccount?.value
|
||||||
|
);
|
||||||
const TransactionType = selectedTransaction?.value;
|
const TransactionType = selectedTransaction?.value;
|
||||||
if (!account || !TransactionType || txIsDisabled) return;
|
if (!account || !TransactionType || txIsDisabled) return;
|
||||||
|
|
||||||
@@ -91,7 +115,7 @@ const Transaction = () => {
|
|||||||
let options = { ...txFields };
|
let options = { ...txFields };
|
||||||
|
|
||||||
options.Destination = selectedDestAccount?.value;
|
options.Destination = selectedDestAccount?.value;
|
||||||
(Object.keys(options) as (keyof TxFields)[]).forEach(field => {
|
(Object.keys(options) as (keyof TxFields)[]).forEach((field) => {
|
||||||
let _value = options[field];
|
let _value = options[field];
|
||||||
// convert currency
|
// convert currency
|
||||||
if (typeof _value === "object" && _value.type === "currency") {
|
if (typeof _value === "object" && _value.type === "currency") {
|
||||||
@@ -122,10 +146,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) {
|
||||||
@@ -134,9 +163,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,
|
||||||
@@ -152,12 +182,20 @@ 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" }}
|
||||||
|
>
|
||||||
<Text muted css={{ mr: "$3" }}>
|
<Text muted css={{ mr: "$3" }}>
|
||||||
Transaction type:{" "}
|
Transaction type:{" "}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -168,10 +206,14 @@ const Transaction = () => {
|
|||||||
hideSelectedOptions
|
hideSelectedOptions
|
||||||
css={{ width: "70%" }}
|
css={{ width: "70%" }}
|
||||||
value={selectedTransaction}
|
value={selectedTransaction}
|
||||||
onChange={tt => setSelectedTransaction(tt as any)}
|
onChange={(tt) => setSelectedTransaction(tt as any)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
|
<Flex
|
||||||
|
row
|
||||||
|
fluid
|
||||||
|
css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
|
||||||
|
>
|
||||||
<Text muted css={{ mr: "$3" }}>
|
<Text muted css={{ mr: "$3" }}>
|
||||||
Account:{" "}
|
Account:{" "}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -181,17 +223,25 @@ const Transaction = () => {
|
|||||||
css={{ width: "70%" }}
|
css={{ width: "70%" }}
|
||||||
options={accountOptions}
|
options={accountOptions}
|
||||||
value={selectedAccount}
|
value={selectedAccount}
|
||||||
onChange={acc => setSelectedAccount(acc as any)}
|
onChange={(acc) => setSelectedAccount(acc as any)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
{txFields.Amount !== undefined && (
|
{txFields.Amount !== undefined && (
|
||||||
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
|
<Flex
|
||||||
|
row
|
||||||
|
fluid
|
||||||
|
css={{
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
alignItems: "center",
|
||||||
|
mb: "$3",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text muted css={{ mr: "$3" }}>
|
<Text muted css={{ mr: "$3" }}>
|
||||||
Amount (XRP):{" "}
|
Amount (XRP):{" "}
|
||||||
</Text>
|
</Text>
|
||||||
<Input
|
<Input
|
||||||
value={txFields.Amount.value}
|
value={txFields.Amount.value}
|
||||||
onChange={e =>
|
onChange={(e) =>
|
||||||
setTxFields({
|
setTxFields({
|
||||||
...txFields,
|
...txFields,
|
||||||
Amount: { type: "currency", value: e.target.value },
|
Amount: { type: "currency", value: e.target.value },
|
||||||
@@ -203,7 +253,15 @@ const Transaction = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{txFields.Destination !== undefined && (
|
{txFields.Destination !== undefined && (
|
||||||
<Flex row fluid css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}>
|
<Flex
|
||||||
|
row
|
||||||
|
fluid
|
||||||
|
css={{
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
alignItems: "center",
|
||||||
|
mb: "$3",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text muted css={{ mr: "$3" }}>
|
<Text muted css={{ mr: "$3" }}>
|
||||||
Destination account:{" "}
|
Destination account:{" "}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -214,28 +272,36 @@ const Transaction = () => {
|
|||||||
options={destAccountOptions}
|
options={destAccountOptions}
|
||||||
value={selectedDestAccount}
|
value={selectedDestAccount}
|
||||||
isClearable
|
isClearable
|
||||||
onChange={acc => setSelectedDestAccount(acc as any)}
|
onChange={(acc) => setSelectedDestAccount(acc as any)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{otherFields.map(field => {
|
{otherFields.map((field) => {
|
||||||
let _value = txFields[field];
|
let _value = txFields[field];
|
||||||
let value = typeof _value === "object" ? _value.value : _value;
|
let value = typeof _value === "object" ? _value.value : _value;
|
||||||
value = typeof value === "object" ? JSON.stringify(value) : value?.toLocaleString();
|
value =
|
||||||
let isCurrency = typeof _value === "object" && _value.type === "currency";
|
typeof value === "object"
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: value?.toLocaleString();
|
||||||
|
let isCurrency =
|
||||||
|
typeof _value === "object" && _value.type === "currency";
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
key={field}
|
key={field}
|
||||||
row
|
row
|
||||||
fluid
|
fluid
|
||||||
css={{ justifyContent: "flex-end", alignItems: "center", mb: "$3" }}
|
css={{
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
alignItems: "center",
|
||||||
|
mb: "$3",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text muted css={{ mr: "$3" }}>
|
<Text muted css={{ mr: "$3" }}>
|
||||||
{field + (isCurrency ? " (XRP)" : "")}:{" "}
|
{field + (isCurrency ? " (XRP)" : "")}:{" "}
|
||||||
</Text>
|
</Text>
|
||||||
<Input
|
<Input
|
||||||
value={value}
|
value={value}
|
||||||
onChange={e =>
|
onChange={(e) =>
|
||||||
setTxFields({
|
setTxFields({
|
||||||
...txFields,
|
...txFields,
|
||||||
[field]:
|
[field]:
|
||||||
@@ -284,41 +350,94 @@ 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={{ px: 0 }}>
|
||||||
<Flex
|
<Split
|
||||||
row
|
direction="vertical"
|
||||||
fluid
|
sizes={[50, 50]}
|
||||||
css={{ justifyContent: "center", mb: "$2", height: "40vh", minHeight: "300px", p: "$3 $2" }}
|
gutterSize={4}
|
||||||
|
gutterAlign="center"
|
||||||
|
style={{ height: "calc(100vh - 60px)" }}
|
||||||
>
|
>
|
||||||
<Box css={{ width: "60%", px: "$2", maxWidth: "800px", height: "100%", overflow: "auto" }}>
|
<Flex
|
||||||
<Tabs>
|
row
|
||||||
{/* TODO Dynamic tabs */}
|
fluid
|
||||||
<Tab header="test1.json">
|
css={{
|
||||||
<Transaction />
|
justifyContent: "center",
|
||||||
</Tab>
|
p: "$3 $2",
|
||||||
<Tab header="test2.json">
|
}}
|
||||||
<Transaction />
|
>
|
||||||
</Tab>
|
<Split
|
||||||
</Tabs>
|
direction="horizontal"
|
||||||
</Box>
|
sizes={[50, 50]}
|
||||||
<Box css={{ width: "40%", mx: "$2", height: "100%", maxWidth: "750px" }}>
|
minSize={[180, 320]}
|
||||||
<Accounts card hideDeployBtn showHookStats />
|
gutterSize={4}
|
||||||
</Box>
|
gutterAlign="center"
|
||||||
</Flex>
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box css={{ width: "55%", px: "$2" }}>
|
||||||
|
<Tabs
|
||||||
|
keepAllAlive
|
||||||
|
forceDefaultExtension
|
||||||
|
defaultExtension=".json"
|
||||||
|
onCreateNewTab={(name) =>
|
||||||
|
setTabHeaders(tabHeaders.concat(name))
|
||||||
|
}
|
||||||
|
onCloseTab={(index) =>
|
||||||
|
setTabHeaders(tabHeaders.filter((_, idx) => idx !== index))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{tabHeaders.map((header) => (
|
||||||
|
<Tab key={header} header={header}>
|
||||||
|
<Transaction header={header} />
|
||||||
|
</Tab>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</Box>
|
||||||
|
<Box css={{ width: "45%", mx: "$2", height: "100%" }}>
|
||||||
|
<Accounts card hideDeployBtn showHookStats />
|
||||||
|
</Box>
|
||||||
|
</Split>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
<Flex row fluid css={{ borderBottom: "1px solid $mauve8" }}>
|
<Flex row fluid>
|
||||||
<Box css={{ width: "50%", borderRight: "1px solid $mauve8" }}>
|
<Split
|
||||||
<LogBox
|
direction="horizontal"
|
||||||
title="Development Log"
|
sizes={[50, 50]}
|
||||||
logs={snap.transactionLogs}
|
minSize={[320, 160]}
|
||||||
clearLog={() => (state.transactionLogs = [])}
|
gutterSize={4}
|
||||||
/>
|
gutterAlign="center"
|
||||||
</Box>
|
style={{
|
||||||
<Box css={{ width: "50%" }}>
|
display: "flex",
|
||||||
<DebugStream />
|
flexDirection: "row",
|
||||||
</Box>
|
width: "100%",
|
||||||
</Flex>
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
css={{
|
||||||
|
borderRight: "1px solid $mauve8",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogBox
|
||||||
|
title="Development Log"
|
||||||
|
logs={snap.transactionLogs}
|
||||||
|
clearLog={() => (state.transactionLogs = [])}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box css={{ height: "100%" }}>
|
||||||
|
<DebugStream />
|
||||||
|
</Box>
|
||||||
|
</Split>
|
||||||
|
</Flex>
|
||||||
|
</Split>
|
||||||
</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`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -6,8 +6,36 @@ body,
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gutter {
|
||||||
|
position: relative;
|
||||||
|
transition: border-color 0.3s, background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutter-vertical {
|
||||||
|
margin-top: -4px;
|
||||||
|
}
|
||||||
|
.gutter-horizontal {
|
||||||
|
margin-left: -4px;
|
||||||
|
}
|
||||||
|
.gutter-vertical:hover {
|
||||||
|
cursor: row-resize;
|
||||||
|
background-color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
html.light .gutter-vertical:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.gutter-horizontal:hover {
|
||||||
|
cursor: col-resize;
|
||||||
|
background-color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.light .gutter-horizontal:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|||||||
22
yarn.lock
22
yarn.lock
@@ -3688,6 +3688,15 @@ progress@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||||
|
|
||||||
|
prop-types@^15.5.7:
|
||||||
|
version "15.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
react-is "^16.13.1"
|
||||||
|
|
||||||
prop-types@^15.6.0, prop-types@^15.6.2:
|
prop-types@^15.6.0, prop-types@^15.6.2:
|
||||||
version "15.8.0"
|
version "15.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.0.tgz#d237e624c45a9846e469f5f31117f970017ff588"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.0.tgz#d237e624c45a9846e469f5f31117f970017ff588"
|
||||||
@@ -3855,6 +3864,14 @@ react-select@^5.2.1:
|
|||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
react-transition-group "^4.3.0"
|
react-transition-group "^4.3.0"
|
||||||
|
|
||||||
|
react-split@^2.0.14:
|
||||||
|
version "2.0.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-split/-/react-split-2.0.14.tgz#ef198259bf43264d605f792fb3384f15f5b34432"
|
||||||
|
integrity sha512-bKWydgMgaKTg/2JGQnaJPg51T6dmumTWZppFgEbbY0Fbme0F5TuatAScCLaqommbGQQf/ZT1zaejuPDriscISA==
|
||||||
|
dependencies:
|
||||||
|
prop-types "^15.5.7"
|
||||||
|
split.js "^1.6.0"
|
||||||
|
|
||||||
react-stay-scrolled@^7.4.0:
|
react-stay-scrolled@^7.4.0:
|
||||||
version "7.4.0"
|
version "7.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-stay-scrolled/-/react-stay-scrolled-7.4.0.tgz#cb109b8dfd7834e5406d9c9322035ab40c398368"
|
resolved "https://registry.yarnpkg.com/react-stay-scrolled/-/react-stay-scrolled-7.4.0.tgz#cb109b8dfd7834e5406d9c9322035ab40c398368"
|
||||||
@@ -4259,6 +4276,11 @@ source-map@^0.6.1:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
|
||||||
|
split.js@^1.6.0:
|
||||||
|
version "1.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/split.js/-/split.js-1.6.5.tgz#f7f61da1044c9984cb42947df4de4fadb5a3f300"
|
||||||
|
integrity sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==
|
||||||
|
|
||||||
sprintf-js@~1.0.2:
|
sprintf-js@~1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
|
|||||||
Reference in New Issue
Block a user