Compare commits
22 Commits
fix/tx-amo
...
fix/accoun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ee8dcb536 | ||
|
|
41ba096ef9 | ||
|
|
3897f2d823 | ||
|
|
bf792f1495 | ||
|
|
df3210a663 | ||
|
|
bad7730c32 | ||
|
|
adb6a78549 | ||
|
|
8cc27f20c3 | ||
|
|
8e49a3f5f1 | ||
|
|
3179757469 | ||
|
|
554cfb3db9 | ||
|
|
637a066f69 | ||
|
|
c9a852e9be | ||
|
|
307a5407eb | ||
|
|
faa28845c8 | ||
|
|
168d11d48e | ||
|
|
60f2bb558c | ||
|
|
fdf33b9f45 | ||
|
|
d05180d148 | ||
|
|
bfaa6be17d | ||
|
|
9e368dec84 | ||
|
|
25eec6980f |
152
components/ContextMenu/index.tsx
Normal file
152
components/ContextMenu/index.tsx
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { CaretRight, Check, Circle } from "phosphor-react";
|
||||||
|
import { FC, Fragment, ReactNode } from "react";
|
||||||
|
import { Flex, Text } from "../";
|
||||||
|
import {
|
||||||
|
ContextMenuCheckboxItem,
|
||||||
|
ContextMenuContent,
|
||||||
|
ContextMenuItem,
|
||||||
|
ContextMenuItemIndicator,
|
||||||
|
ContextMenuLabel,
|
||||||
|
ContextMenuRadioGroup,
|
||||||
|
ContextMenuRadioItem,
|
||||||
|
ContextMenuRoot,
|
||||||
|
ContextMenuSeparator,
|
||||||
|
ContextMenuTrigger,
|
||||||
|
ContextMenuTriggerItem,
|
||||||
|
} from "./primitive";
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: "Show bookmarks",
|
||||||
|
type: "checkbox",
|
||||||
|
checked: true,
|
||||||
|
indicator: "*",
|
||||||
|
onCheckedChange: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "radio",
|
||||||
|
label: "People",
|
||||||
|
value: "pedro",
|
||||||
|
onValueChange: () => {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "pedro",
|
||||||
|
label: "Pedro Duarte",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "colm",
|
||||||
|
label: "Colm Tuite",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export type TextOption = {
|
||||||
|
type: "text";
|
||||||
|
label: ReactNode;
|
||||||
|
onClick?: () => any;
|
||||||
|
children?: Option[];
|
||||||
|
};
|
||||||
|
export type SeparatorOption = { type: "separator" };
|
||||||
|
export type CheckboxOption = {
|
||||||
|
type: "checkbox";
|
||||||
|
label: ReactNode;
|
||||||
|
checked?: boolean;
|
||||||
|
onCheckedChange?: (isChecked: boolean) => any;
|
||||||
|
};
|
||||||
|
export type RadioOption<T extends string = string> = {
|
||||||
|
type: "radio";
|
||||||
|
label: ReactNode;
|
||||||
|
onValueChange?: (value: string) => any;
|
||||||
|
value: T;
|
||||||
|
options?: { value: T; label?: ReactNode }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type WithCommons = { key: string; disabled?: boolean };
|
||||||
|
|
||||||
|
type Option = (TextOption | SeparatorOption | CheckboxOption | RadioOption) &
|
||||||
|
WithCommons;
|
||||||
|
|
||||||
|
interface IContextMenu {
|
||||||
|
options?: Option[];
|
||||||
|
isNested?: boolean;
|
||||||
|
}
|
||||||
|
export const ContextMenu: FC<IContextMenu> = ({
|
||||||
|
children,
|
||||||
|
options,
|
||||||
|
isNested,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ContextMenuRoot>
|
||||||
|
{isNested ? (
|
||||||
|
<ContextMenuTriggerItem>{children}</ContextMenuTriggerItem>
|
||||||
|
) : (
|
||||||
|
<ContextMenuTrigger>{children}</ContextMenuTrigger>
|
||||||
|
)}
|
||||||
|
{options && (
|
||||||
|
<ContextMenuContent sideOffset={isNested ? 2 : 5}>
|
||||||
|
{options.map(({ key, ...option }) => {
|
||||||
|
if (option.type === "text") {
|
||||||
|
const { children, label } = option;
|
||||||
|
if (children)
|
||||||
|
return (
|
||||||
|
<ContextMenu isNested key={key} options={children}>
|
||||||
|
<Flex fluid row justify="space-between" align="center">
|
||||||
|
<Text>{label}</Text>
|
||||||
|
<CaretRight />
|
||||||
|
</Flex>
|
||||||
|
</ContextMenu>
|
||||||
|
);
|
||||||
|
return <ContextMenuItem key={key}>{label}</ContextMenuItem>;
|
||||||
|
}
|
||||||
|
if (option.type === "checkbox") {
|
||||||
|
const { label, checked, onCheckedChange } = option;
|
||||||
|
return (
|
||||||
|
<ContextMenuCheckboxItem
|
||||||
|
key={key}
|
||||||
|
checked={checked}
|
||||||
|
onCheckedChange={onCheckedChange}
|
||||||
|
>
|
||||||
|
<Flex row align="center">
|
||||||
|
<ContextMenuItemIndicator>
|
||||||
|
<Check />
|
||||||
|
</ContextMenuItemIndicator>
|
||||||
|
<Text css={{ ml: checked ? "$4" : undefined }}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</ContextMenuCheckboxItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (option.type === "radio") {
|
||||||
|
const { label, options, onValueChange, value } = option;
|
||||||
|
return (
|
||||||
|
<Fragment key={key}>
|
||||||
|
<ContextMenuLabel>{label}</ContextMenuLabel>
|
||||||
|
<ContextMenuRadioGroup
|
||||||
|
value={value}
|
||||||
|
onValueChange={onValueChange}
|
||||||
|
>
|
||||||
|
{options?.map(({ value: v, label }) => {
|
||||||
|
return (
|
||||||
|
<ContextMenuRadioItem key={v} value={v}>
|
||||||
|
<ContextMenuItemIndicator>
|
||||||
|
<Circle weight="fill" />
|
||||||
|
</ContextMenuItemIndicator>
|
||||||
|
<Text css={{ ml: "$4" }}>{label}</Text>
|
||||||
|
</ContextMenuRadioItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ContextMenuRadioGroup>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <ContextMenuSeparator key={key} />;
|
||||||
|
})}
|
||||||
|
</ContextMenuContent>
|
||||||
|
)}
|
||||||
|
</ContextMenuRoot>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContextMenu;
|
||||||
107
components/ContextMenu/primitive.tsx
Normal file
107
components/ContextMenu/primitive.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
|
||||||
|
import { styled } from "../../stitches.config";
|
||||||
|
import {
|
||||||
|
slideDownAndFade,
|
||||||
|
slideLeftAndFade,
|
||||||
|
slideRightAndFade,
|
||||||
|
slideUpAndFade,
|
||||||
|
} from "../../styles/keyframes";
|
||||||
|
|
||||||
|
const StyledContent = styled(ContextMenuPrimitive.Content, {
|
||||||
|
minWidth: 140,
|
||||||
|
backgroundColor: "$backgroundOverlay",
|
||||||
|
borderRadius: 6,
|
||||||
|
overflow: "hidden",
|
||||||
|
padding: "5px",
|
||||||
|
boxShadow:
|
||||||
|
"0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)",
|
||||||
|
"@media (prefers-reduced-motion: no-preference)": {
|
||||||
|
animationDuration: "400ms",
|
||||||
|
animationTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)",
|
||||||
|
willChange: "transform, opacity",
|
||||||
|
'&[data-state="open"]': {
|
||||||
|
'&[data-side="top"]': { animationName: slideDownAndFade },
|
||||||
|
'&[data-side="right"]': { animationName: slideLeftAndFade },
|
||||||
|
'&[data-side="bottom"]': { animationName: slideUpAndFade },
|
||||||
|
'&[data-side="left"]': { animationName: slideRightAndFade },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
".dark &": {
|
||||||
|
boxShadow:
|
||||||
|
"0px 10px 38px -10px rgba(22, 23, 24, 0.85), 0px 10px 20px -15px rgba(22, 23, 24, 0.6)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const itemStyles = {
|
||||||
|
all: "unset",
|
||||||
|
fontSize: 13,
|
||||||
|
lineHeight: 1,
|
||||||
|
color: "$text",
|
||||||
|
borderRadius: 3,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
height: 28,
|
||||||
|
padding: "0 7px",
|
||||||
|
position: "relative",
|
||||||
|
paddingLeft: 10,
|
||||||
|
userSelect: "none",
|
||||||
|
|
||||||
|
"&[data-disabled]": {
|
||||||
|
color: "$textMuted",
|
||||||
|
pointerEvents: "none",
|
||||||
|
},
|
||||||
|
|
||||||
|
"&:focus": {
|
||||||
|
backgroundColor: "$purple9",
|
||||||
|
color: "$white",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledItem = styled(ContextMenuPrimitive.Item, { ...itemStyles });
|
||||||
|
const StyledCheckboxItem = styled(ContextMenuPrimitive.CheckboxItem, {
|
||||||
|
...itemStyles,
|
||||||
|
});
|
||||||
|
const StyledRadioItem = styled(ContextMenuPrimitive.RadioItem, {
|
||||||
|
...itemStyles,
|
||||||
|
});
|
||||||
|
const StyledTriggerItem = styled(ContextMenuPrimitive.TriggerItem, {
|
||||||
|
'&[data-state="open"]': {
|
||||||
|
backgroundColor: "$purple9",
|
||||||
|
color: "$purple9",
|
||||||
|
},
|
||||||
|
...itemStyles,
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledLabel = styled(ContextMenuPrimitive.Label, {
|
||||||
|
paddingLeft: 10,
|
||||||
|
fontSize: 12,
|
||||||
|
lineHeight: "25px",
|
||||||
|
color: "$text",
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledSeparator = styled(ContextMenuPrimitive.Separator, {
|
||||||
|
height: 1,
|
||||||
|
backgroundColor: "$backgroundAlt",
|
||||||
|
margin: 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledItemIndicator = styled(ContextMenuPrimitive.ItemIndicator, {
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
width: 25,
|
||||||
|
display: "inline-flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ContextMenuRoot = ContextMenuPrimitive.Root;
|
||||||
|
export const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
||||||
|
export const ContextMenuContent = StyledContent;
|
||||||
|
export const ContextMenuItem = StyledItem;
|
||||||
|
export const ContextMenuCheckboxItem = StyledCheckboxItem;
|
||||||
|
export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
||||||
|
export const ContextMenuRadioItem = StyledRadioItem;
|
||||||
|
export const ContextMenuItemIndicator = StyledItemIndicator;
|
||||||
|
export const ContextMenuTriggerItem = StyledTriggerItem;
|
||||||
|
export const ContextMenuLabel = StyledLabel;
|
||||||
|
export const ContextMenuSeparator = StyledSeparator;
|
||||||
@@ -13,7 +13,7 @@ import state from "../state";
|
|||||||
import wat from "../utils/wat-highlight";
|
import wat from "../utils/wat-highlight";
|
||||||
|
|
||||||
import EditorNavigation from "./EditorNavigation";
|
import EditorNavigation from "./EditorNavigation";
|
||||||
import { Button, Text, Link, Flex } from ".";
|
import { Button, Text, Link, Flex, Tabs, Tab } from ".";
|
||||||
import Monaco from "./Monaco";
|
import Monaco from "./Monaco";
|
||||||
|
|
||||||
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
|
const FILESIZE_BREAKPOINTS: [number, number] = [2 * 1024, 5 * 1024];
|
||||||
@@ -25,9 +25,20 @@ const DeployEditor = () => {
|
|||||||
|
|
||||||
const [showContent, setShowContent] = useState(false);
|
const [showContent, setShowContent] = useState(false);
|
||||||
|
|
||||||
const activeFile = snap.files[snap.active]?.compiledContent
|
const compiledFiles = snap.files.filter(file => file.compiledContent);
|
||||||
? snap.files[snap.active]
|
const activeFile = compiledFiles[snap.activeWat];
|
||||||
: snap.files.filter(file => file.compiledContent)[0];
|
|
||||||
|
const renderNav = () => (
|
||||||
|
<Tabs
|
||||||
|
activeIndex={snap.activeWat}
|
||||||
|
onChangeActive={idx => (state.activeWat = idx)}
|
||||||
|
>
|
||||||
|
{compiledFiles.map((file, index) => {
|
||||||
|
return <Tab key={file.name} header={`${file.name}.wat`} />;
|
||||||
|
})}
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
|
||||||
const compiledSize = activeFile?.compiledContent?.byteLength || 0;
|
const compiledSize = activeFile?.compiledContent?.byteLength || 0;
|
||||||
const color =
|
const color =
|
||||||
compiledSize > FILESIZE_BREAKPOINTS[1]
|
compiledSize > FILESIZE_BREAKPOINTS[1]
|
||||||
@@ -38,7 +49,7 @@ const DeployEditor = () => {
|
|||||||
|
|
||||||
const isContentChanged =
|
const isContentChanged =
|
||||||
activeFile && activeFile.compiledValueSnapshot !== activeFile.content;
|
activeFile && activeFile.compiledValueSnapshot !== activeFile.content;
|
||||||
// const hasDeployErros = activeFile && activeFile.containsErrors;
|
// const hasDeployErrors = activeFile && activeFile.containsErrors;
|
||||||
|
|
||||||
const CompiledStatView = activeFile && (
|
const CompiledStatView = activeFile && (
|
||||||
<Flex
|
<Flex
|
||||||
@@ -99,6 +110,7 @@ const DeployEditor = () => {
|
|||||||
</NextLink>
|
</NextLink>
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
||||||
const isContent =
|
const isContent =
|
||||||
snap.files?.filter(file => file.compiledWatContent).length > 0 &&
|
snap.files?.filter(file => file.compiledWatContent).length > 0 &&
|
||||||
router.isReady;
|
router.isReady;
|
||||||
@@ -113,7 +125,7 @@ const DeployEditor = () => {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EditorNavigation showWat />
|
<EditorNavigation renderNav={renderNav} />
|
||||||
<Container
|
<Container
|
||||||
css={{
|
css={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|||||||
@@ -1,27 +1,7 @@
|
|||||||
import { keyframes } from "@stitches/react";
|
|
||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
|
|
||||||
import { styled } from "../stitches.config";
|
import { styled } from "../stitches.config";
|
||||||
|
import { slideDownAndFade, slideLeftAndFade, slideRightAndFade, slideUpAndFade } from '../styles/keyframes';
|
||||||
const slideUpAndFade = keyframes({
|
|
||||||
"0%": { opacity: 0, transform: "translateY(2px)" },
|
|
||||||
"100%": { opacity: 1, transform: "translateY(0)" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const slideRightAndFade = keyframes({
|
|
||||||
"0%": { opacity: 0, transform: "translateX(-2px)" },
|
|
||||||
"100%": { opacity: 1, transform: "translateX(0)" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const slideDownAndFade = keyframes({
|
|
||||||
"0%": { opacity: 0, transform: "translateY(-2px)" },
|
|
||||||
"100%": { opacity: 1, transform: "translateY(0)" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const slideLeftAndFade = keyframes({
|
|
||||||
"0%": { opacity: 0, transform: "translateX(2px)" },
|
|
||||||
"100%": { opacity: 1, transform: "translateX(0)" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const StyledContent = styled(DropdownMenuPrimitive.Content, {
|
const StyledContent = styled(DropdownMenuPrimitive.Content, {
|
||||||
minWidth: 220,
|
minWidth: 220,
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
import React, {
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
ReactNode,
|
||||||
|
} from "react";
|
||||||
import {
|
import {
|
||||||
Plus,
|
|
||||||
Share,
|
Share,
|
||||||
DownloadSimple,
|
DownloadSimple,
|
||||||
Gear,
|
Gear,
|
||||||
@@ -28,7 +32,6 @@ import { useSnapshot } from "valtio";
|
|||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createNewFile,
|
|
||||||
syncToGist,
|
syncToGist,
|
||||||
updateEditorSettings,
|
updateEditorSettings,
|
||||||
downloadAsZip,
|
downloadAsZip,
|
||||||
@@ -48,36 +51,23 @@ import {
|
|||||||
import Flex from "./Flex";
|
import Flex from "./Flex";
|
||||||
import Stack from "./Stack";
|
import Stack from "./Stack";
|
||||||
import { Input, Label } from "./Input";
|
import { Input, Label } from "./Input";
|
||||||
import Text from "./Text";
|
|
||||||
import Tooltip from "./Tooltip";
|
import Tooltip from "./Tooltip";
|
||||||
import { styled } from "../stitches.config";
|
|
||||||
import { showAlert } from "../state/actions/showAlert";
|
import { showAlert } from "../state/actions/showAlert";
|
||||||
|
|
||||||
const ErrorText = styled(Text, {
|
|
||||||
color: "$error",
|
|
||||||
mt: "$1",
|
|
||||||
display: "block",
|
|
||||||
});
|
|
||||||
|
|
||||||
const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
const [editorSettingsOpen, setEditorSettingsOpen] = useState(false);
|
const [editorSettingsOpen, setEditorSettingsOpen] = useState(false);
|
||||||
const [isNewfileDialogOpen, setIsNewfileDialogOpen] = useState(false);
|
|
||||||
const [newfileError, setNewfileError] = useState<string | null>(null);
|
|
||||||
const [filename, setFilename] = useState("");
|
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
const [popup, setPopUp] = useState(false);
|
const [popup, setPopUp] = useState(false);
|
||||||
const [editorSettings, setEditorSettings] = useState(snap.editorSettings);
|
const [editorSettings, setEditorSettings] = useState(snap.editorSettings);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session && session.user && popup) {
|
if (session && session.user && popup) {
|
||||||
setPopUp(false);
|
setPopUp(false);
|
||||||
}
|
}
|
||||||
}, [session, popup]);
|
}, [session, popup]);
|
||||||
|
|
||||||
// when filename changes, reset error
|
|
||||||
useEffect(() => {
|
|
||||||
setNewfileError(null);
|
|
||||||
}, [filename, setNewfileError]);
|
|
||||||
|
|
||||||
const showNewGistAlert = () => {
|
const showNewGistAlert = () => {
|
||||||
showAlert("Are you sure?", {
|
showAlert("Are you sure?", {
|
||||||
@@ -95,46 +85,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateFilename = useCallback(
|
|
||||||
(filename: string): { error: string | null } => {
|
|
||||||
// check if filename already exists
|
|
||||||
if (!filename) {
|
|
||||||
return { error: "You need to add filename" };
|
|
||||||
}
|
|
||||||
if (snap.files.find((file) => file.name === filename)) {
|
|
||||||
return { error: "Filename already exists." };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filename.includes(".") || filename[filename.length - 1] === ".") {
|
|
||||||
return { error: "Filename should include file extension" };
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for illegal characters
|
|
||||||
const ALPHA_NUMERICAL_REGEX = /^[A-Za-z0-9_-]+[.][A-Za-z0-9]{1,4}$/g;
|
|
||||||
if (!filename.match(ALPHA_NUMERICAL_REGEX)) {
|
|
||||||
return {
|
|
||||||
error: `Filename can contain only characters from a-z, A-Z, 0-9, "_" and "-" and it needs to have file extension (e.g. ".c")`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return { error: null };
|
|
||||||
},
|
|
||||||
[snap.files]
|
|
||||||
);
|
|
||||||
const handleConfirm = useCallback(() => {
|
|
||||||
// add default extension in case omitted
|
|
||||||
const chk = validateFilename(filename);
|
|
||||||
if (chk && chk.error) {
|
|
||||||
setNewfileError(`Error: ${chk.error}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsNewfileDialogOpen(false);
|
|
||||||
createNewFile(filename);
|
|
||||||
setFilename("");
|
|
||||||
}, [filename, setIsNewfileDialogOpen, setFilename, validateFilename]);
|
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const files = snap.files;
|
|
||||||
return (
|
return (
|
||||||
<Flex css={{ flexShrink: 0, gap: "$0" }}>
|
<Flex css={{ flexShrink: 0, gap: "$0" }}>
|
||||||
<Flex
|
<Flex
|
||||||
@@ -174,131 +126,14 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|||||||
scrollbarWidth: "thin",
|
scrollbarWidth: "thin",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onWheelCapture={(e) => {
|
onWheelCapture={e => {
|
||||||
if (scrollRef.current) {
|
if (scrollRef.current) {
|
||||||
scrollRef.current.scrollLeft += e.deltaY;
|
scrollRef.current.scrollLeft += e.deltaY;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container css={{ flex: 1 }} ref={containerRef}>
|
<Container css={{ flex: 1 }} ref={containerRef}>
|
||||||
<Stack
|
{renderNav?.()}
|
||||||
css={{
|
|
||||||
gap: "$3",
|
|
||||||
flex: 1,
|
|
||||||
flexWrap: "nowrap",
|
|
||||||
marginBottom: "-1px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{files &&
|
|
||||||
files.length > 0 &&
|
|
||||||
files.map((file, index) => {
|
|
||||||
if (!file.compiledContent && showWat) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
outline={
|
|
||||||
showWat ? snap.activeWat !== index : snap.active !== index
|
|
||||||
}
|
|
||||||
onClick={() => (state.active = index)}
|
|
||||||
key={file.name + index}
|
|
||||||
css={{
|
|
||||||
"&:hover": {
|
|
||||||
span: {
|
|
||||||
visibility: "visible",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{file.name}
|
|
||||||
{showWat && ".wat"}
|
|
||||||
{!showWat && (
|
|
||||||
<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();
|
|
||||||
// Remove file from state
|
|
||||||
state.files.splice(index, 1);
|
|
||||||
// Change active file state
|
|
||||||
// If deleted file is behind active tab
|
|
||||||
// we keep the current state otherwise
|
|
||||||
// select previous file on the list
|
|
||||||
state.active =
|
|
||||||
index > snap.active ? snap.active : snap.active - 1;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<X size="9px" weight="bold" />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{!showWat && (
|
|
||||||
<Dialog
|
|
||||||
open={isNewfileDialogOpen}
|
|
||||||
onOpenChange={setIsNewfileDialogOpen}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button
|
|
||||||
ghost
|
|
||||||
size="sm"
|
|
||||||
css={{ alignItems: "center", px: "$2", mr: "$3" }}
|
|
||||||
>
|
|
||||||
<Plus size="16px" />{" "}
|
|
||||||
{snap.files.length === 0 && "Add new file"}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogTitle>Create new file</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
<Label>Filename</Label>
|
|
||||||
<Input
|
|
||||||
value={filename}
|
|
||||||
onChange={(e) => setFilename(e.target.value)}
|
|
||||||
onKeyPress={(e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
handleConfirm();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ErrorText>{newfileError}</ErrorText>
|
|
||||||
</DialogDescription>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
css={{
|
|
||||||
marginTop: 25,
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
gap: "$3",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button outline>Cancel</Button>
|
|
||||||
</DialogClose>
|
|
||||||
<Button variant="primary" onClick={handleConfirm}>
|
|
||||||
Create file
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Box css={{ position: "absolute", top: "$3", right: "$3" }}>
|
|
||||||
<X size="20px" />
|
|
||||||
</Box>
|
|
||||||
</DialogClose>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Container>
|
</Container>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
@@ -542,8 +377,8 @@ const EditorNavigation = ({ showWat }: { showWat?: boolean }) => {
|
|||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
value={editorSettings.tabSize}
|
value={editorSettings.tabSize}
|
||||||
onChange={(e) =>
|
onChange={e =>
|
||||||
setEditorSettings((curr) => ({
|
setEditorSettings(curr => ({
|
||||||
...curr,
|
...curr,
|
||||||
tabSize: Number(e.target.value),
|
tabSize: Number(e.target.value),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import Box from "./Box";
|
import Box from "./Box";
|
||||||
import Container from "./Container";
|
import Container from "./Container";
|
||||||
import { saveFile } from "../state/actions";
|
import { createNewFile, saveFile } from "../state/actions";
|
||||||
import { apiHeaderFiles } from "../state/constants";
|
import { apiHeaderFiles } from "../state/constants";
|
||||||
import state from "../state";
|
import state from "../state";
|
||||||
|
|
||||||
@@ -20,7 +20,8 @@ import ReconnectingWebSocket from "reconnecting-websocket";
|
|||||||
|
|
||||||
import docs from "../xrpl-hooks-docs/docs";
|
import docs from "../xrpl-hooks-docs/docs";
|
||||||
import Monaco from "./Monaco";
|
import Monaco from "./Monaco";
|
||||||
import { saveAllFiles } from '../state/actions/saveFile';
|
import { saveAllFiles } from "../state/actions/saveFile";
|
||||||
|
import { Tab, Tabs } from "./Tabs";
|
||||||
|
|
||||||
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
|
const validateWritability = (editor: monaco.editor.IStandaloneCodeEditor) => {
|
||||||
const currPath = editor.getModel()?.uri.path;
|
const currPath = editor.getModel()?.uri.path;
|
||||||
@@ -119,6 +120,26 @@ const HooksEditor = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const file = snap.files[snap.active];
|
const file = snap.files[snap.active];
|
||||||
|
|
||||||
|
const renderNav = () => (
|
||||||
|
<Tabs
|
||||||
|
label="File"
|
||||||
|
activeIndex={snap.active}
|
||||||
|
onChangeActive={idx => (state.active = idx)}
|
||||||
|
extensionRequired
|
||||||
|
onCreateNewTab={createNewFile}
|
||||||
|
onCloseTab={idx => state.files.splice(idx, 1)}
|
||||||
|
headerExtraValidation={{
|
||||||
|
regex: /^[A-Za-z0-9_-]+[.][A-Za-z0-9]{1,4}$/g,
|
||||||
|
error:
|
||||||
|
'Filename can contain only characters from a-z, A-Z, 0-9, "_" and "-"',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{snap.files.map((file, index) => {
|
||||||
|
return <Tab key={file.name} header={file.name} />;
|
||||||
|
})}
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
css={{
|
css={{
|
||||||
@@ -131,7 +152,7 @@ const HooksEditor = () => {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EditorNavigation />
|
<EditorNavigation renderNav={renderNav} />
|
||||||
{snap.files.length > 0 && router.isReady ? (
|
{snap.files.length > 0 && router.isReady ? (
|
||||||
<Monaco
|
<Monaco
|
||||||
keepCurrentModel
|
keepCurrentModel
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ const Select = forwardRef<any, Props>((props, ref) => {
|
|||||||
...provided,
|
...provided,
|
||||||
color: colors.searchText,
|
color: colors.searchText,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
state.isSelected || state.isFocused
|
state.isFocused
|
||||||
? colors.activeLight
|
? colors.activeLight
|
||||||
: colors.dropDownBg,
|
: colors.dropDownBg,
|
||||||
":hover": {
|
":hover": {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import { TTS, tts } from "../utils/hookOnCalculator";
|
import { TTS, tts } from "../utils/hookOnCalculator";
|
||||||
import { deployHook } from "../state/actions";
|
import { deployHook } from "../state/actions";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state, { SelectOption } from "../state";
|
import state, { IFile, SelectOption } from "../state";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
|
import { prepareDeployHookTx, sha256 } from "../state/actions/deployHook";
|
||||||
import estimateFee from "../utils/estimateFee";
|
import estimateFee from "../utils/estimateFee";
|
||||||
@@ -56,9 +56,9 @@ export type SetHookData = {
|
|||||||
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
||||||
({ accountAddress }) => {
|
({ accountAddress }) => {
|
||||||
const snap = useSnapshot(state);
|
const snap = useSnapshot(state);
|
||||||
const activeFile = snap.files[snap.active]?.compiledContent
|
const compiledFiles = snap.files.filter(file => file.compiledContent);
|
||||||
? snap.files[snap.active]
|
const activeFile = compiledFiles[snap.activeWat] as IFile | undefined;
|
||||||
: snap.files.filter(file => file.compiledContent)[0];
|
|
||||||
const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
|
const [isSetHookDialogOpen, setIsSetHookDialogOpen] = useState(false);
|
||||||
|
|
||||||
const accountOptions: SelectOption[] = snap.accounts.map(acc => ({
|
const accountOptions: SelectOption[] = snap.accounts.map(acc => ({
|
||||||
@@ -72,6 +72,15 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
const account = snap.accounts.find(
|
const account = snap.accounts.find(
|
||||||
acc => acc.address === selectedAccount?.value
|
acc => acc.address === selectedAccount?.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getHookNamespace = useCallback(
|
||||||
|
() =>
|
||||||
|
activeFile && snap.deployValues[activeFile.name]
|
||||||
|
? snap.deployValues[activeFile.name].HookNamespace
|
||||||
|
: activeFile?.name.split(".")[0] || "",
|
||||||
|
[activeFile, snap.deployValues]
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -81,13 +90,10 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
getValues,
|
getValues,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<SetHookData>({
|
} = useForm<SetHookData>({
|
||||||
defaultValues: snap.deployValues?.[activeFile?.name]
|
defaultValues: (activeFile && snap.deployValues[activeFile.name]) || {
|
||||||
? snap.deployValues[activeFile?.name]
|
HookNamespace: activeFile?.name.split(".")[0] || "",
|
||||||
: {
|
Invoke: transactionOptions.filter(to => to.label === "ttPAYMENT"),
|
||||||
HookNamespace:
|
},
|
||||||
snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "",
|
|
||||||
Invoke: transactionOptions.filter(to => to.label === "ttPAYMENT"),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const { fields, append, remove } = useFieldArray({
|
const { fields, append, remove } = useFieldArray({
|
||||||
control,
|
control,
|
||||||
@@ -97,20 +103,15 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
const [estimateLoading, setEstimateLoading] = useState(false);
|
const [estimateLoading, setEstimateLoading] = useState(false);
|
||||||
const watchedFee = watch("Fee");
|
const watchedFee = watch("Fee");
|
||||||
|
|
||||||
// Update value if activeWat changes
|
// Update value if activeFile changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const defaultValue = snap.deployValues?.[activeFile?.name]
|
if (!activeFile) return;
|
||||||
? snap.deployValues?.[activeFile?.name].HookNamespace
|
const defaultValue = getHookNamespace();
|
||||||
: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || "";
|
|
||||||
setValue("HookNamespace", defaultValue);
|
setValue("HookNamespace", defaultValue);
|
||||||
setFormInitialized(true);
|
setFormInitialized(true);
|
||||||
}, [
|
}, [setValue, activeFile, snap.deployValues, getHookNamespace]);
|
||||||
snap.activeWat,
|
|
||||||
snap.files,
|
|
||||||
setValue,
|
|
||||||
activeFile?.name,
|
|
||||||
snap.deployValues,
|
|
||||||
]);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
watchedFee &&
|
watchedFee &&
|
||||||
@@ -128,21 +129,19 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
// name: "HookGrants", // unique name for your Field Array
|
// name: "HookGrants", // unique name for your Field Array
|
||||||
// });
|
// });
|
||||||
const [hashedNamespace, setHashedNamespace] = useState("");
|
const [hashedNamespace, setHashedNamespace] = useState("");
|
||||||
const namespace = watch(
|
|
||||||
"HookNamespace",
|
const namespace = watch("HookNamespace", getHookNamespace());
|
||||||
snap.deployValues?.[activeFile?.name]
|
|
||||||
? snap.deployValues?.[activeFile?.name].HookNamespace
|
|
||||||
: snap.files?.[snap.activeWat]?.name?.split(".")?.[0] || ""
|
|
||||||
);
|
|
||||||
const calculateHashedValue = useCallback(async () => {
|
const calculateHashedValue = useCallback(async () => {
|
||||||
const hashedVal = await sha256(namespace);
|
const hashedVal = await sha256(namespace);
|
||||||
setHashedNamespace(hashedVal.toUpperCase());
|
setHashedNamespace(hashedVal.toUpperCase());
|
||||||
}, [namespace]);
|
}, [namespace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
calculateHashedValue();
|
calculateHashedValue();
|
||||||
}, [namespace, calculateHashedValue]);
|
}, [namespace, calculateHashedValue]);
|
||||||
|
|
||||||
// Calcucate initial fee estimate when modal opens
|
// Calculate initial fee estimate when modal opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (formInitialized && account) {
|
if (formInitialized && account) {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -161,18 +160,15 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
}, [formInitialized]);
|
}, [formInitialized]);
|
||||||
|
|
||||||
const tooLargeFile = () => {
|
const tooLargeFile = () => {
|
||||||
const activeFile = snap.files[snap.active].compiledContent
|
|
||||||
? snap.files[snap.active]
|
|
||||||
: snap.files.filter(file => file.compiledContent)[0];
|
|
||||||
return Boolean(
|
return Boolean(
|
||||||
activeFile?.compiledContent?.byteLength &&
|
activeFile?.compiledContent?.byteLength &&
|
||||||
activeFile?.compiledContent?.byteLength >= 64000
|
activeFile?.compiledContent?.byteLength >= 64000
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit: SubmitHandler<SetHookData> = async (data) => {
|
const onSubmit: SubmitHandler<SetHookData> = async data => {
|
||||||
const currAccount = state.accounts.find(
|
const currAccount = state.accounts.find(
|
||||||
(acc) => acc.address === account?.address
|
acc => acc.address === account?.address
|
||||||
);
|
);
|
||||||
if (!account) return;
|
if (!account) return;
|
||||||
if (currAccount) currAccount.isLoading = true;
|
if (currAccount) currAccount.isLoading = true;
|
||||||
@@ -194,10 +190,7 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
uppercase
|
uppercase
|
||||||
variant={"secondary"}
|
variant={"secondary"}
|
||||||
disabled={
|
disabled={
|
||||||
!account ||
|
!account || account.isLoading || !activeFile || tooLargeFile()
|
||||||
account.isLoading ||
|
|
||||||
!snap.files.filter(file => file.compiledWatContent).length ||
|
|
||||||
tooLargeFile()
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Set Hook
|
Set Hook
|
||||||
@@ -213,7 +206,6 @@ export const SetHookDialog: React.FC<{ accountAddress: string }> = React.memo(
|
|||||||
<Select
|
<Select
|
||||||
instanceId="deploy-account"
|
instanceId="deploy-account"
|
||||||
placeholder="Select account"
|
placeholder="Select account"
|
||||||
hideSelectedOptions
|
|
||||||
options={accountOptions}
|
options={accountOptions}
|
||||||
value={selectedAccount}
|
value={selectedAccount}
|
||||||
onChange={(acc: any) => setSelectedAccount(acc)}
|
onChange={(acc: any) => setSelectedAccount(acc)}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import {
|
|||||||
} from "./Dialog";
|
} from "./Dialog";
|
||||||
import { Plus, X } from "phosphor-react";
|
import { Plus, X } from "phosphor-react";
|
||||||
import { styled } from "../stitches.config";
|
import { styled } from "../stitches.config";
|
||||||
|
import { capitalize } from "../utils/helpers";
|
||||||
|
import ContextMenu from "./ContextMenu";
|
||||||
|
|
||||||
const ErrorText = styled(Text, {
|
const ErrorText = styled(Text, {
|
||||||
color: "$error",
|
color: "$error",
|
||||||
@@ -25,11 +27,11 @@ const ErrorText = styled(Text, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
interface TabProps {
|
interface TabProps {
|
||||||
header?: string;
|
header: string;
|
||||||
children: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO customise messages shown
|
// TODO customize messages shown
|
||||||
interface Props {
|
interface Props {
|
||||||
label?: string;
|
label?: string;
|
||||||
activeIndex?: number;
|
activeIndex?: number;
|
||||||
@@ -38,8 +40,12 @@ interface Props {
|
|||||||
children: ReactElement<TabProps>[];
|
children: ReactElement<TabProps>[];
|
||||||
keepAllAlive?: boolean;
|
keepAllAlive?: boolean;
|
||||||
defaultExtension?: string;
|
defaultExtension?: string;
|
||||||
appendDefaultExtension?: boolean;
|
extensionRequired?: boolean;
|
||||||
allowedExtensions?: string[];
|
allowedExtensions?: string[];
|
||||||
|
headerExtraValidation?: {
|
||||||
|
regex: string | RegExp;
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
onCreateNewTab?: (name: string) => any;
|
onCreateNewTab?: (name: string) => any;
|
||||||
onCloseTab?: (index: number, header?: string) => any;
|
onCloseTab?: (index: number, header?: string) => any;
|
||||||
onChangeActive?: (index: number, header?: string) => any;
|
onChangeActive?: (index: number, header?: string) => any;
|
||||||
@@ -57,8 +63,9 @@ export const Tabs = ({
|
|||||||
onCreateNewTab,
|
onCreateNewTab,
|
||||||
onCloseTab,
|
onCloseTab,
|
||||||
onChangeActive,
|
onChangeActive,
|
||||||
|
headerExtraValidation,
|
||||||
|
extensionRequired,
|
||||||
defaultExtension = "",
|
defaultExtension = "",
|
||||||
appendDefaultExtension = false,
|
|
||||||
allowedExtensions,
|
allowedExtensions,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [active, setActive] = useState(activeIndex || 0);
|
const [active, setActive] = useState(activeIndex || 0);
|
||||||
@@ -87,16 +94,38 @@ export const Tabs = ({
|
|||||||
|
|
||||||
const validateTabname = useCallback(
|
const validateTabname = useCallback(
|
||||||
(tabname: string): { error: string | null } => {
|
(tabname: string): { error: string | null } => {
|
||||||
if (tabs.find(tab => tab.header === tabname)) {
|
if (!tabname) {
|
||||||
return { error: "Name already exists." };
|
return { error: `Please enter ${label.toLocaleLowerCase()} name.` };
|
||||||
|
}
|
||||||
|
if (tabs.find(tab => tab.header === tabname)) {
|
||||||
|
return { error: `${capitalize(label)} name already exists.` };
|
||||||
|
}
|
||||||
|
const ext =
|
||||||
|
(tabname.includes(".") && tabname.split(".").pop()) ||
|
||||||
|
defaultExtension ||
|
||||||
|
"";
|
||||||
|
if (extensionRequired && !ext) {
|
||||||
|
return { error: "File extension is required!" };
|
||||||
}
|
}
|
||||||
const ext = tabname.split(".").pop() || "";
|
|
||||||
if (allowedExtensions && !allowedExtensions.includes(ext)) {
|
if (allowedExtensions && !allowedExtensions.includes(ext)) {
|
||||||
return { error: "This file extension is not allowed!" };
|
return { error: "This file extension is not allowed!" };
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
headerExtraValidation &&
|
||||||
|
!tabname.match(headerExtraValidation.regex)
|
||||||
|
) {
|
||||||
|
return { error: headerExtraValidation.error };
|
||||||
|
}
|
||||||
return { error: null };
|
return { error: null };
|
||||||
},
|
},
|
||||||
[allowedExtensions, tabs]
|
[
|
||||||
|
allowedExtensions,
|
||||||
|
defaultExtension,
|
||||||
|
extensionRequired,
|
||||||
|
headerExtraValidation,
|
||||||
|
label,
|
||||||
|
tabs,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleActiveChange = useCallback(
|
const handleActiveChange = useCallback(
|
||||||
@@ -108,31 +137,25 @@ export const Tabs = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleCreateTab = useCallback(() => {
|
const handleCreateTab = useCallback(() => {
|
||||||
// add default extension in case omitted
|
const chk = validateTabname(tabname);
|
||||||
let _tabname = tabname.includes(".")
|
|
||||||
? tabname
|
|
||||||
: `${tabname}.${defaultExtension}`;
|
|
||||||
if (appendDefaultExtension && !_tabname.endsWith(defaultExtension)) {
|
|
||||||
_tabname = `${_tabname}.${defaultExtension}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const chk = validateTabname(_tabname);
|
|
||||||
if (chk.error) {
|
if (chk.error) {
|
||||||
setNewtabError(`Error: ${chk.error}`);
|
setNewtabError(`Error: ${chk.error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let _tabname = tabname;
|
||||||
|
if (defaultExtension && !_tabname.endsWith(defaultExtension)) {
|
||||||
|
_tabname = `${_tabname}.${defaultExtension}`;
|
||||||
|
}
|
||||||
|
|
||||||
setIsNewtabDialogOpen(false);
|
setIsNewtabDialogOpen(false);
|
||||||
setTabname("");
|
setTabname("");
|
||||||
|
|
||||||
onCreateNewTab?.(_tabname);
|
onCreateNewTab?.(_tabname);
|
||||||
|
|
||||||
// switch to new tab?
|
|
||||||
handleActiveChange(tabs.length, _tabname);
|
handleActiveChange(tabs.length, _tabname);
|
||||||
}, [
|
}, [
|
||||||
tabname,
|
tabname,
|
||||||
defaultExtension,
|
defaultExtension,
|
||||||
appendDefaultExtension,
|
|
||||||
validateTabname,
|
validateTabname,
|
||||||
onCreateNewTab,
|
onCreateNewTab,
|
||||||
handleActiveChange,
|
handleActiveChange,
|
||||||
@@ -146,8 +169,10 @@ export const Tabs = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCloseTab?.(idx, tabs[idx].header);
|
onCloseTab?.(idx, tabs[idx].header);
|
||||||
|
|
||||||
|
handleActiveChange(idx, tabs[idx].header);
|
||||||
},
|
},
|
||||||
[active, onCloseTab, tabs]
|
[active, handleActiveChange, onCloseTab, tabs]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -164,46 +189,47 @@ export const Tabs = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, idx) => (
|
{tabs.map((tab, idx) => (
|
||||||
<Button
|
<ContextMenu key={tab.header}>
|
||||||
key={tab.header}
|
<Button
|
||||||
role="tab"
|
role="tab"
|
||||||
tabIndex={idx}
|
tabIndex={idx}
|
||||||
onClick={() => handleActiveChange(idx, tab.header)}
|
onClick={() => handleActiveChange(idx, tab.header)}
|
||||||
onKeyPress={() => handleActiveChange(idx, tab.header)}
|
onKeyPress={() => handleActiveChange(idx, tab.header)}
|
||||||
outline={active !== idx}
|
outline={active !== idx}
|
||||||
size="sm"
|
size="sm"
|
||||||
css={{
|
css={{
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
span: {
|
span: {
|
||||||
visibility: "visible",
|
visibility: "visible",
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{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);
|
{tab.header || idx}
|
||||||
}}
|
{onCloseTab && (
|
||||||
>
|
<Box
|
||||||
<X size="9px" weight="bold" />
|
as="span"
|
||||||
</Box>
|
css={{
|
||||||
)}
|
display: "flex",
|
||||||
</Button>
|
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>
|
||||||
|
</ContextMenu>
|
||||||
))}
|
))}
|
||||||
{onCreateNewTab && (
|
{onCreateNewTab && (
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -216,11 +242,14 @@ export const Tabs = ({
|
|||||||
size="sm"
|
size="sm"
|
||||||
css={{ alignItems: "center", px: "$2", mr: "$3" }}
|
css={{ alignItems: "center", px: "$2", mr: "$3" }}
|
||||||
>
|
>
|
||||||
<Plus size="16px" /> {tabs.length === 0 && `Add new ${label.toLocaleLowerCase()}`}
|
<Plus size="16px" />{" "}
|
||||||
|
{tabs.length === 0 && `Add new ${label.toLocaleLowerCase()}`}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogTitle>Create new {label.toLocaleLowerCase()}</DialogTitle>
|
<DialogTitle>
|
||||||
|
Create new {label.toLocaleLowerCase()}
|
||||||
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
<Label>{label} name</Label>
|
<Label>{label} name</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -259,29 +288,32 @@ export const Tabs = ({
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
{keepAllAlive ? (
|
{keepAllAlive
|
||||||
tabs.map((tab, idx) => {
|
? tabs.map((tab, idx) => {
|
||||||
// TODO Maybe rule out fragments as children
|
// TODO Maybe rule out fragments as children
|
||||||
if (!isValidElement(tab.children)) {
|
if (!isValidElement(tab.children)) {
|
||||||
if (active !== idx) return null;
|
if (active !== idx) return null;
|
||||||
return tab.children;
|
return tab.children;
|
||||||
}
|
}
|
||||||
let key = tab.children.key || tab.header || idx;
|
let key = tab.children.key || tab.header || idx;
|
||||||
let { children } = tab;
|
let { children } = tab;
|
||||||
let { style, ...props } = children.props;
|
let { style, ...props } = children.props;
|
||||||
return (
|
return (
|
||||||
<children.type
|
<children.type
|
||||||
key={key}
|
key={key}
|
||||||
{...props}
|
{...props}
|
||||||
style={{ ...style, display: active !== idx ? "none" : undefined }}
|
style={{
|
||||||
/>
|
...style,
|
||||||
);
|
display: active !== idx ? "none" : undefined,
|
||||||
})
|
}}
|
||||||
) : (
|
/>
|
||||||
<Fragment key={tabs[active].header || active}>
|
);
|
||||||
{tabs[active].children}
|
})
|
||||||
</Fragment>
|
: tabs[active] && (
|
||||||
)}
|
<Fragment key={tabs[active].header || active}>
|
||||||
|
{tabs[active].children}
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Play } from "phosphor-react";
|
import { Play } from "phosphor-react";
|
||||||
import { FC, useCallback, useEffect, useMemo } from "react";
|
import { FC, useCallback, useEffect } from "react";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state from "../../state";
|
import state from "../../state";
|
||||||
import {
|
import {
|
||||||
|
defaultTransactionType,
|
||||||
|
getTxFields,
|
||||||
modifyTransaction,
|
modifyTransaction,
|
||||||
prepareState,
|
prepareState,
|
||||||
prepareTransaction,
|
prepareTransaction,
|
||||||
|
SelectOption,
|
||||||
TransactionState,
|
TransactionState,
|
||||||
} from "../../state/transactions";
|
} from "../../state/transactions";
|
||||||
import { sendTransaction } from "../../state/actions";
|
import { sendTransaction } from "../../state/actions";
|
||||||
@@ -15,7 +18,7 @@ import Flex from "../Flex";
|
|||||||
import { TxJson } from "./json";
|
import { TxJson } from "./json";
|
||||||
import { TxUI } from "./ui";
|
import { TxUI } from "./ui";
|
||||||
import { default as _estimateFee } from "../../utils/estimateFee";
|
import { default as _estimateFee } from "../../utils/estimateFee";
|
||||||
import toast from 'react-hot-toast';
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
export interface TransactionProps {
|
export interface TransactionProps {
|
||||||
header: string;
|
header: string;
|
||||||
@@ -34,7 +37,6 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
txIsDisabled,
|
txIsDisabled,
|
||||||
txIsLoading,
|
txIsLoading,
|
||||||
viewType,
|
viewType,
|
||||||
editorSavedValue,
|
|
||||||
editorValue,
|
editorValue,
|
||||||
} = txState;
|
} = txState;
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const prepareOptions = useCallback(
|
const prepareOptions = useCallback(
|
||||||
(state: TransactionState = txState) => {
|
(state: Partial<TransactionState> = txState) => {
|
||||||
const {
|
const {
|
||||||
selectedTransaction,
|
selectedTransaction,
|
||||||
selectedDestAccount,
|
selectedDestAccount,
|
||||||
@@ -55,9 +57,7 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
const TransactionType = selectedTransaction?.value || null;
|
const TransactionType = selectedTransaction?.value || null;
|
||||||
const Destination =
|
const Destination = selectedDestAccount?.value || txFields?.Destination;
|
||||||
selectedDestAccount?.value ||
|
|
||||||
("Destination" in txFields ? null : undefined);
|
|
||||||
const Account = selectedAccount?.value || null;
|
const Account = selectedAccount?.value || null;
|
||||||
|
|
||||||
return prepareTransaction({
|
return prepareTransaction({
|
||||||
@@ -109,8 +109,9 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
}
|
}
|
||||||
const options = prepareOptions(st);
|
const options = prepareOptions(st);
|
||||||
|
|
||||||
if (options.Destination === null) {
|
const fields = getTxFields(options.TransactionType);
|
||||||
throw Error("Destination account cannot be null");
|
if (fields.Destination && !options.Destination) {
|
||||||
|
throw Error("Destination account is required!");
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendTransaction(account, options, { logPrefix });
|
await sendTransaction(account, options, { logPrefix });
|
||||||
@@ -136,15 +137,38 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
prepareOptions,
|
prepareOptions,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const resetState = useCallback(() => {
|
const getJsonString = useCallback(
|
||||||
modifyTransaction(header, { viewType }, { replaceState: true });
|
(state?: Partial<TransactionState>) =>
|
||||||
}, [header, viewType]);
|
JSON.stringify(
|
||||||
|
prepareOptions?.(state) || {},
|
||||||
|
null,
|
||||||
|
editorSettings.tabSize
|
||||||
|
),
|
||||||
|
[editorSettings.tabSize, prepareOptions]
|
||||||
|
);
|
||||||
|
|
||||||
const jsonValue = useMemo(
|
const resetState = useCallback(
|
||||||
() =>
|
(transactionType: SelectOption | undefined = defaultTransactionType) => {
|
||||||
editorSavedValue ||
|
const fields = getTxFields(transactionType?.value);
|
||||||
JSON.stringify(prepareOptions?.() || {}, null, editorSettings.tabSize),
|
|
||||||
[editorSavedValue, editorSettings.tabSize, prepareOptions]
|
const nwState: Partial<TransactionState> = {
|
||||||
|
viewType,
|
||||||
|
selectedTransaction: transactionType,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fields.Destination !== undefined) {
|
||||||
|
nwState.selectedDestAccount = null;
|
||||||
|
fields.Destination = "";
|
||||||
|
} else {
|
||||||
|
fields.Destination = undefined;
|
||||||
|
}
|
||||||
|
nwState.txFields = fields;
|
||||||
|
|
||||||
|
const state = modifyTransaction(header, nwState, { replaceState: true });
|
||||||
|
const editorValue = getJsonString(state);
|
||||||
|
return setState({ editorValue });
|
||||||
|
},
|
||||||
|
[getJsonString, header, setState, viewType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const estimateFee = useCallback(
|
const estimateFee = useCallback(
|
||||||
@@ -156,10 +180,10 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
);
|
);
|
||||||
if (!account) {
|
if (!account) {
|
||||||
if (!opts?.silent) {
|
if (!opts?.silent) {
|
||||||
toast.error("Please select account from the list.")
|
toast.error("Please select account from the list.");
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
ptx.Account = account.address;
|
ptx.Account = account.address;
|
||||||
ptx.Sequence = account.sequence;
|
ptx.Sequence = account.sequence;
|
||||||
@@ -176,7 +200,7 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
<Box css={{ position: "relative", height: "calc(100% - 28px)" }} {...props}>
|
||||||
{viewType === "json" ? (
|
{viewType === "json" ? (
|
||||||
<TxJson
|
<TxJson
|
||||||
value={jsonValue}
|
getJsonString={getJsonString}
|
||||||
header={header}
|
header={header}
|
||||||
state={txState}
|
state={txState}
|
||||||
setState={setState}
|
setState={setState}
|
||||||
@@ -199,7 +223,7 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (viewType === "ui") {
|
if (viewType === "ui") {
|
||||||
setState({ editorSavedValue: null, viewType: "json" });
|
setState({ viewType: "json" });
|
||||||
} else setState({ viewType: "ui" });
|
} else setState({ viewType: "ui" });
|
||||||
}}
|
}}
|
||||||
outline
|
outline
|
||||||
@@ -207,7 +231,7 @@ const Transaction: FC<TransactionProps> = ({
|
|||||||
{viewType === "ui" ? "EDIT AS JSON" : "EXIT JSON MODE"}
|
{viewType === "ui" ? "EDIT AS JSON" : "EXIT JSON MODE"}
|
||||||
</Button>
|
</Button>
|
||||||
<Flex row>
|
<Flex row>
|
||||||
<Button onClick={resetState} outline css={{ mr: "$3" }}>
|
<Button onClick={() => resetState()} outline css={{ mr: "$3" }}>
|
||||||
RESET
|
RESET
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FC, useCallback, useEffect, useState } from "react";
|
import { FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state, {
|
import state, {
|
||||||
prepareState,
|
prepareState,
|
||||||
@@ -15,7 +15,7 @@ import Monaco from "../Monaco";
|
|||||||
import type monaco from "monaco-editor";
|
import type monaco from "monaco-editor";
|
||||||
|
|
||||||
interface JsonProps {
|
interface JsonProps {
|
||||||
value?: string;
|
getJsonString?: (state?: Partial<TransactionState>) => string;
|
||||||
header?: string;
|
header?: string;
|
||||||
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
setState: (pTx?: Partial<TransactionState> | undefined) => void;
|
||||||
state: TransactionState;
|
state: TransactionState;
|
||||||
@@ -23,22 +23,23 @@ interface JsonProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TxJson: FC<JsonProps> = ({
|
export const TxJson: FC<JsonProps> = ({
|
||||||
value = "",
|
getJsonString,
|
||||||
state: txState,
|
state: txState,
|
||||||
header,
|
header,
|
||||||
setState,
|
setState,
|
||||||
}) => {
|
}) => {
|
||||||
const { editorSettings, accounts } = useSnapshot(state);
|
const { editorSettings, accounts } = useSnapshot(state);
|
||||||
const { editorValue = value, estimatedFee } = txState;
|
const { editorValue, estimatedFee } = txState;
|
||||||
const [hasUnsaved, setHasUnsaved] = useState(false);
|
|
||||||
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
const [currTxType, setCurrTxType] = useState<string | undefined>(
|
||||||
txState.selectedTransaction?.value
|
txState.selectedTransaction?.value
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setState({ editorValue: value });
|
setState({
|
||||||
|
editorValue: getJsonString?.(),
|
||||||
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [value]);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parsed = parseJSON(editorValue);
|
const parsed = parseJSON(editorValue);
|
||||||
@@ -52,21 +53,22 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
}
|
}
|
||||||
}, [editorValue]);
|
}, [editorValue]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (editorValue === value) setHasUnsaved(false);
|
|
||||||
else setHasUnsaved(true);
|
|
||||||
}, [editorValue, value]);
|
|
||||||
|
|
||||||
const saveState = (value: string, transactionType?: string) => {
|
const saveState = (value: string, transactionType?: string) => {
|
||||||
const tx = prepareState(value, transactionType);
|
const tx = prepareState(value, transactionType);
|
||||||
if (tx) setState(tx);
|
if (tx) {
|
||||||
|
setState(tx);
|
||||||
|
setState({
|
||||||
|
editorValue: getJsonString?.(tx),
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const discardChanges = () => {
|
const discardChanges = () => {
|
||||||
showAlert("Confirm", {
|
showAlert("Confirm", {
|
||||||
body: "Are you sure to discard these changes?",
|
body: "Are you sure to discard these changes?",
|
||||||
confirmText: "Yes",
|
confirmText: "Yes",
|
||||||
onConfirm: () => setState({ editorValue: value }),
|
onCancel: () => {},
|
||||||
|
onConfirm: () => setState({ editorValue: getJsonString?.() }),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,8 +81,8 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
showAlert("Error!", {
|
showAlert("Error!", {
|
||||||
body: `Malformed Transaction in ${header}, would you like to discard these changes?`,
|
body: `Malformed Transaction in ${header}, would you like to discard these changes?`,
|
||||||
confirmText: "Discard",
|
confirmText: "Discard",
|
||||||
onConfirm: () => setState({ editorValue: value }),
|
onConfirm: () => setState({ editorValue: getJsonString?.() }),
|
||||||
onCancel: () => setState({ viewType: "json", editorSavedValue: value }),
|
onCancel: () => setState({ viewType: "json" }),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,6 +176,11 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
});
|
});
|
||||||
}, [getSchemas, monacoInst]);
|
}, [getSchemas, monacoInst]);
|
||||||
|
|
||||||
|
const hasUnsaved = useMemo(
|
||||||
|
() => editorValue !== getJsonString?.(),
|
||||||
|
[editorValue, getJsonString]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Monaco
|
<Monaco
|
||||||
rootProps={{
|
rootProps={{
|
||||||
@@ -203,14 +210,14 @@ export const TxJson: FC<JsonProps> = ({
|
|||||||
<Flex
|
<Flex
|
||||||
row
|
row
|
||||||
align="center"
|
align="center"
|
||||||
css={{ fontSize: "$xs", color: "$textMuted", ml: 'auto' }}
|
css={{ fontSize: "$xs", color: "$textMuted", ml: "auto" }}
|
||||||
>
|
>
|
||||||
<Text muted small>
|
<Text muted small>
|
||||||
This file has unsaved changes.
|
This file has unsaved changes.
|
||||||
</Text>
|
</Text>
|
||||||
<Link
|
<Link
|
||||||
css={{ ml: "$1" }}
|
css={{ ml: "$1" }}
|
||||||
onClick={() => saveState(editorValue, currTxType)}
|
onClick={() => saveState(editorValue || "", currTxType)}
|
||||||
>
|
>
|
||||||
save
|
save
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FC, useCallback, useEffect, useState } from "react";
|
import { FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import Container from "../Container";
|
import Container from "../Container";
|
||||||
import Flex from "../Flex";
|
import Flex from "../Flex";
|
||||||
import Input from "../Input";
|
import Input from "../Input";
|
||||||
@@ -7,9 +7,10 @@ import Text from "../Text";
|
|||||||
import {
|
import {
|
||||||
SelectOption,
|
SelectOption,
|
||||||
TransactionState,
|
TransactionState,
|
||||||
transactionsData,
|
transactionsOptions,
|
||||||
TxFields,
|
TxFields,
|
||||||
getTxFields,
|
getTxFields,
|
||||||
|
defaultTransactionType,
|
||||||
} from "../../state/transactions";
|
} from "../../state/transactions";
|
||||||
import { useSnapshot } from "valtio";
|
import { useSnapshot } from "valtio";
|
||||||
import state from "../../state";
|
import state from "../../state";
|
||||||
@@ -38,12 +39,6 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
txFields,
|
txFields,
|
||||||
} = txState;
|
} = txState;
|
||||||
|
|
||||||
|
|
||||||
const transactionsOptions = transactionsData.map(tx => ({
|
|
||||||
value: tx.TransactionType,
|
|
||||||
label: tx.TransactionType,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address,
|
value: acc.address,
|
||||||
@@ -58,10 +53,16 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
|
|
||||||
const [feeLoading, setFeeLoading] = useState(false);
|
const [feeLoading, setFeeLoading] = useState(false);
|
||||||
|
|
||||||
const resetOptions = useCallback(
|
const resetFields = useCallback(
|
||||||
(tt: string) => {
|
(tt: string) => {
|
||||||
const fields = getTxFields(tt);
|
const fields = getTxFields(tt);
|
||||||
if (!fields.Destination) setState({ selectedDestAccount: null });
|
|
||||||
|
if (fields.Destination !== undefined) {
|
||||||
|
setState({ selectedDestAccount: null });
|
||||||
|
fields.Destination = "";
|
||||||
|
} else {
|
||||||
|
fields.Destination = undefined;
|
||||||
|
}
|
||||||
return setState({ txFields: fields });
|
return setState({ txFields: fields });
|
||||||
},
|
},
|
||||||
[setState]
|
[setState]
|
||||||
@@ -102,33 +103,37 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
(tt: SelectOption) => {
|
(tt: SelectOption) => {
|
||||||
setState({ selectedTransaction: tt });
|
setState({ selectedTransaction: tt });
|
||||||
|
|
||||||
const newState = resetOptions(tt.value);
|
const newState = resetFields(tt.value);
|
||||||
|
|
||||||
handleEstimateFee(newState, true);
|
handleEstimateFee(newState, true);
|
||||||
},
|
},
|
||||||
[handleEstimateFee, resetOptions, setState]
|
[handleEstimateFee, resetFields, setState]
|
||||||
);
|
);
|
||||||
|
|
||||||
const specialFields = ["TransactionType", "Account", "Destination"];
|
const switchToJson = () => setState({ viewType: "json" });
|
||||||
|
|
||||||
const otherFields = Object.keys(txFields).filter(
|
|
||||||
k => !specialFields.includes(k)
|
|
||||||
) as [keyof TxFields];
|
|
||||||
|
|
||||||
const switchToJson = () =>
|
|
||||||
setState({ editorSavedValue: null, viewType: "json" });
|
|
||||||
|
|
||||||
// default tx
|
// default tx
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTransaction?.value) return;
|
if (selectedTransaction?.value) return;
|
||||||
|
|
||||||
const defaultOption = transactionsOptions.find(
|
if (defaultTransactionType) {
|
||||||
tt => tt.value === "Payment"
|
handleChangeTxType(defaultTransactionType);
|
||||||
);
|
|
||||||
if (defaultOption) {
|
|
||||||
handleChangeTxType(defaultOption);
|
|
||||||
}
|
}
|
||||||
}, [handleChangeTxType, selectedTransaction?.value, transactionsOptions]);
|
}, [handleChangeTxType, selectedTransaction?.value]);
|
||||||
|
|
||||||
|
const fields = useMemo(
|
||||||
|
() => getTxFields(selectedTransaction?.value),
|
||||||
|
[selectedTransaction?.value]
|
||||||
|
);
|
||||||
|
|
||||||
|
const specialFields = ["TransactionType", "Account"];
|
||||||
|
if (fields.Destination !== undefined) {
|
||||||
|
specialFields.push("Destination");
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherFields = Object.keys(txFields).filter(
|
||||||
|
k => !specialFields.includes(k)
|
||||||
|
) as [keyof TxFields];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
@@ -185,7 +190,7 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
onChange={(acc: any) => handleSetAccount(acc)} // TODO make react-select have correct types for acc
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
{txFields.Destination !== undefined && (
|
{fields.Destination !== undefined && (
|
||||||
<Flex
|
<Flex
|
||||||
row
|
row
|
||||||
fluid
|
fluid
|
||||||
|
|||||||
@@ -40,9 +40,9 @@
|
|||||||
{
|
{
|
||||||
"label": "Token",
|
"label": "Token",
|
||||||
"body": {
|
"body": {
|
||||||
"currency": "${1:13.1}",
|
"currency": "${1:USD}",
|
||||||
"value": "${2:FOO}",
|
"value": "${2:100}",
|
||||||
"description": "${3:rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpns}"
|
"issuer": "${3:rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpns}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"TransactionType": "EscrowCancel",
|
"TransactionType": "EscrowCancel",
|
||||||
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"OfferSequence": 7
|
"OfferSequence": 7,
|
||||||
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
@@ -69,7 +70,8 @@
|
|||||||
"FinishAfter": 533171558,
|
"FinishAfter": 533171558,
|
||||||
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
||||||
"DestinationTag": 23480,
|
"DestinationTag": 23480,
|
||||||
"SourceTag": 11747
|
"SourceTag": 11747,
|
||||||
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
@@ -77,32 +79,50 @@
|
|||||||
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Owner": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"OfferSequence": 7,
|
"OfferSequence": 7,
|
||||||
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
"Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
|
||||||
"Fulfillment": "A0028000"
|
"Fulfillment": "A0028000",
|
||||||
|
"Fee": "10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"TransactionType": "NFTokenMint",
|
||||||
|
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||||
|
"Fee": "10",
|
||||||
|
"NFTokenTaxon": 0,
|
||||||
|
"URI": "697066733A2F2F516D614374444B5A4656767666756676626479346573745A626851483744586831364354707631686F776D424779"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "NFTokenBurn",
|
"TransactionType": "NFTokenBurn",
|
||||||
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||||
"Fee": "10",
|
"Fee": "10",
|
||||||
"TokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
|
"NFTokenID": "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "NFTokenAcceptOffer",
|
"TransactionType": "NFTokenAcceptOffer",
|
||||||
"Fee": "10"
|
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||||
|
"Fee": "10",
|
||||||
|
"NFTokenSellOffer": "A2FA1A9911FE2AEF83DAB05F437768E26A301EF899BD31EB85E704B3D528FF18",
|
||||||
|
"NFTokenBuyOffer": "4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862",
|
||||||
|
"NFTokenBrokerFee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "NFTokenCancelOffer",
|
"TransactionType": "NFTokenCancelOffer",
|
||||||
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
"Account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||||
"TokenIDs": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007"
|
"Fee": "10",
|
||||||
|
"NFTokenOffers": {
|
||||||
|
"$type": "json",
|
||||||
|
"$value": ["4AAAEEA76E3C8148473CB3840CE637676E561FB02BD4CA22CA59729EA815B862"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "NFTokenCreateOffer",
|
"TransactionType": "NFTokenCreateOffer",
|
||||||
"Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
|
"Account": "rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX",
|
||||||
"TokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
|
"NFTokenID": "000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007",
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"$value": "100",
|
"$value": "100",
|
||||||
"$type": "xrp"
|
"$type": "xrp"
|
||||||
},
|
},
|
||||||
"Flags": 1
|
"Flags": 1,
|
||||||
|
"Destination": "",
|
||||||
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TransactionType": "OfferCancel",
|
"TransactionType": "OfferCancel",
|
||||||
@@ -150,7 +170,8 @@
|
|||||||
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
|
"PublicKey": "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A",
|
||||||
"CancelAfter": 533171558,
|
"CancelAfter": 533171558,
|
||||||
"DestinationTag": 23480,
|
"DestinationTag": 23480,
|
||||||
"SourceTag": 11747
|
"SourceTag": 11747,
|
||||||
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
@@ -160,7 +181,8 @@
|
|||||||
"$value": "200",
|
"$value": "200",
|
||||||
"$type": "xrp"
|
"$type": "xrp"
|
||||||
},
|
},
|
||||||
"Expiration": 543171558
|
"Expiration": 543171558,
|
||||||
|
"Fee": "10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Flags": 0,
|
"Flags": 0,
|
||||||
@@ -212,9 +234,13 @@
|
|||||||
"Fee": "12",
|
"Fee": "12",
|
||||||
"Flags": 262144,
|
"Flags": 262144,
|
||||||
"LastLedgerSequence": 8007750,
|
"LastLedgerSequence": 8007750,
|
||||||
"Amount": {
|
"LimitAmount": {
|
||||||
"$value": "100",
|
"$type": "json",
|
||||||
"$type": "xrp"
|
"$value": {
|
||||||
|
"currency": "USD",
|
||||||
|
"issuer": "rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc",
|
||||||
|
"value": "100"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Sequence": 12
|
"Sequence": 12
|
||||||
}
|
}
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -16,8 +16,9 @@
|
|||||||
"@octokit/core": "^3.5.1",
|
"@octokit/core": "^3.5.1",
|
||||||
"@radix-ui/colors": "^0.1.7",
|
"@radix-ui/colors": "^0.1.7",
|
||||||
"@radix-ui/react-alert-dialog": "^0.1.1",
|
"@radix-ui/react-alert-dialog": "^0.1.1",
|
||||||
|
"@radix-ui/react-context-menu": "^0.1.6",
|
||||||
"@radix-ui/react-dialog": "^0.1.1",
|
"@radix-ui/react-dialog": "^0.1.1",
|
||||||
"@radix-ui/react-dropdown-menu": "^0.1.1",
|
"@radix-ui/react-dropdown-menu": "^0.1.6",
|
||||||
"@radix-ui/react-id": "^0.1.1",
|
"@radix-ui/react-id": "^0.1.1",
|
||||||
"@radix-ui/react-label": "^0.1.5",
|
"@radix-ui/react-label": "^0.1.5",
|
||||||
"@radix-ui/react-popover": "^0.1.6",
|
"@radix-ui/react-popover": "^0.1.6",
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
"lodash.xor": "^4.5.0",
|
"lodash.xor": "^4.5.0",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.33.0",
|
||||||
"next": "^12.0.4",
|
"next": "^12.0.4",
|
||||||
"next-auth": "^4.0.0-beta.5",
|
"next-auth": "^4.10.1",
|
||||||
"next-plausible": "^3.2.0",
|
"next-plausible": "^3.2.0",
|
||||||
"next-themes": "^0.1.1",
|
"next-themes": "^0.1.1",
|
||||||
"normalize-url": "^7.0.2",
|
"normalize-url": "^7.0.2",
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
"vscode-languageserver": "^7.0.0",
|
"vscode-languageserver": "^7.0.0",
|
||||||
"vscode-uri": "^3.0.2",
|
"vscode-uri": "^3.0.2",
|
||||||
"wabt": "1.0.16",
|
"wabt": "1.0.16",
|
||||||
"xrpl-accountlib": "^1.3.2",
|
"xrpl-accountlib": "^1.5.2",
|
||||||
"xrpl-client": "^1.9.4"
|
"xrpl-client": "^1.9.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -75,5 +76,8 @@
|
|||||||
"eslint-config-next": "11.1.2",
|
"eslint-config-next": "11.1.2",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"typescript": "4.4.4"
|
"typescript": "4.4.4"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"ripple-binary-codec": "=1.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,6 @@ export const sendTransaction = async (account: IAccount, txOptions: TransactionO
|
|||||||
Fee, // TODO auto-fillable default
|
Fee, // TODO auto-fillable default
|
||||||
...opts
|
...opts
|
||||||
};
|
};
|
||||||
|
|
||||||
const { logPrefix = '' } = options || {}
|
const { logPrefix = '' } = options || {}
|
||||||
try {
|
try {
|
||||||
const signedAccount = derive.familySeed(account.secret);
|
const signedAccount = derive.familySeed(account.secret);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type monaco from "monaco-editor";
|
import type monaco from "monaco-editor";
|
||||||
import { proxy, ref, subscribe } from "valtio";
|
import { proxy, ref, subscribe } from "valtio";
|
||||||
import { devtools } from 'valtio/utils';
|
import { devtools, subscribeKey } from 'valtio/utils';
|
||||||
import { XrplClient } from "xrpl-client";
|
import { XrplClient } from "xrpl-client";
|
||||||
import { SplitSize } from "./actions/persistSplits";
|
import { SplitSize } from "./actions/persistSplits";
|
||||||
|
|
||||||
@@ -168,16 +168,23 @@ if (process.env.NODE_ENV !== "production") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
subscribe(state, () => {
|
subscribe(state.accounts, () => {
|
||||||
const { accounts, active } = state;
|
const { accounts } = state;
|
||||||
const accountsNoLoading = accounts.map(acc => ({ ...acc, isLoading: false }))
|
const accountsNoLoading = accounts.map(acc => ({ ...acc, isLoading: false }))
|
||||||
localStorage.setItem("hooksIdeAccounts", JSON.stringify(accountsNoLoading));
|
localStorage.setItem("hooksIdeAccounts", JSON.stringify(accountsNoLoading));
|
||||||
if (!state.files[active]?.compiledWatContent) {
|
|
||||||
state.activeWat = 0;
|
|
||||||
} else {
|
|
||||||
state.activeWat = active;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updateActiveWat = () => {
|
||||||
|
const filename = state.files[state.active]?.name
|
||||||
|
|
||||||
|
const compiledFiles = state.files.filter(
|
||||||
|
file => file.compiledContent)
|
||||||
|
const idx = compiledFiles.findIndex(file => file.name === filename)
|
||||||
|
|
||||||
|
if (idx !== -1) state.activeWat = idx
|
||||||
|
}
|
||||||
|
subscribeKey(state, 'active', updateActiveWat)
|
||||||
|
subscribeKey(state, 'files', updateActiveWat)
|
||||||
}
|
}
|
||||||
export default state
|
export default state
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,13 @@ export interface TransactionState {
|
|||||||
txIsDisabled: boolean;
|
txIsDisabled: boolean;
|
||||||
txFields: TxFields;
|
txFields: TxFields;
|
||||||
viewType: 'json' | 'ui',
|
viewType: 'json' | 'ui',
|
||||||
editorSavedValue: null | string,
|
|
||||||
editorValue?: string,
|
editorValue?: string,
|
||||||
estimatedFee?: string
|
estimatedFee?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type TxFields = Omit<
|
export type TxFields = Omit<
|
||||||
typeof transactionsData[0],
|
Partial<typeof transactionsData[0]>,
|
||||||
"Account" | "Sequence" | "TransactionType"
|
"Account" | "Sequence" | "TransactionType"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -36,15 +35,14 @@ export const defaultTransaction: TransactionState = {
|
|||||||
txIsLoading: false,
|
txIsLoading: false,
|
||||||
txIsDisabled: false,
|
txIsDisabled: false,
|
||||||
txFields: {},
|
txFields: {},
|
||||||
viewType: 'ui',
|
viewType: 'ui'
|
||||||
editorSavedValue: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const transactionsState = proxy({
|
export const transactionsState = proxy({
|
||||||
transactions: [
|
transactions: [
|
||||||
{
|
{
|
||||||
header: "test1.json",
|
header: "test1.json",
|
||||||
state: defaultTransaction,
|
state: { ...defaultTransaction },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
activeHeader: "test1.json"
|
activeHeader: "test1.json"
|
||||||
@@ -92,7 +90,7 @@ export const modifyTransaction = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(partialTx).forEach(k => {
|
Object.keys(partialTx).forEach(k => {
|
||||||
// Typescript mess here, but is definetly safe!
|
// Typescript mess here, but is definitely safe!
|
||||||
const s = tx.state as any;
|
const s = tx.state as any;
|
||||||
const p = partialTx as any; // ? Make copy
|
const p = partialTx as any; // ? Make copy
|
||||||
if (!deepEqual(s[k], p[k])) s[k] = p[k];
|
if (!deepEqual(s[k], p[k])) s[k] = p[k];
|
||||||
@@ -118,7 +116,7 @@ export const prepareTransaction = (data: any) => {
|
|||||||
// handle type: `json`
|
// handle type: `json`
|
||||||
if (_value && typeof _value === "object" && _value.$type === "json") {
|
if (_value && typeof _value === "object" && _value.$type === "json") {
|
||||||
if (typeof _value.$value === "object") {
|
if (typeof _value.$value === "object") {
|
||||||
options[field] = _value.$value as any;
|
options[field] = _value.$value;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
options[field] = JSON.parse(_value.$value);
|
options[field] = JSON.parse(_value.$value);
|
||||||
@@ -131,8 +129,8 @@ export const prepareTransaction = (data: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete unneccesary fields
|
// delete unnecessary fields
|
||||||
if (options[field] === undefined) {
|
if (!options[field]) {
|
||||||
delete options[field];
|
delete options[field];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -152,7 +150,7 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
|
|
||||||
const { Account, TransactionType, Destination, ...rest } = options;
|
const { Account, TransactionType, Destination, ...rest } = options;
|
||||||
let tx: Partial<TransactionState> = {};
|
let tx: Partial<TransactionState> = {};
|
||||||
const txFields = getTxFields(transactionType)
|
const schema = getTxFields(transactionType)
|
||||||
|
|
||||||
if (Account) {
|
if (Account) {
|
||||||
const acc = state.accounts.find(acc => acc.address === Account);
|
const acc = state.accounts.find(acc => acc.address === Account);
|
||||||
@@ -180,9 +178,8 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
tx.selectedTransaction = null;
|
tx.selectedTransaction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txFields.Destination !== undefined) {
|
if (schema.Destination !== undefined) {
|
||||||
const dest = state.accounts.find(acc => acc.address === Destination);
|
const dest = state.accounts.find(acc => acc.address === Destination);
|
||||||
rest.Destination = null
|
|
||||||
if (dest) {
|
if (dest) {
|
||||||
tx.selectedDestAccount = {
|
tx.selectedDestAccount = {
|
||||||
label: dest.name,
|
label: dest.name,
|
||||||
@@ -199,11 +196,14 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
tx.selectedDestAccount = null
|
tx.selectedDestAccount = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Destination) {
|
||||||
|
rest.Destination = Destination
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(rest).forEach(field => {
|
Object.keys(rest).forEach(field => {
|
||||||
const value = rest[field];
|
const value = rest[field];
|
||||||
const origValue = txFields[field as keyof TxFields]
|
const schemaVal = schema[field as keyof TxFields]
|
||||||
const isXrp = typeof value !== 'object' && origValue && typeof origValue === 'object' && origValue.$type === 'xrp'
|
const isXrp = typeof value !== 'object' && schemaVal && typeof schemaVal === 'object' && schemaVal.$type === 'xrp'
|
||||||
if (isXrp) {
|
if (isXrp) {
|
||||||
rest[field] = {
|
rest[field] = {
|
||||||
$type: "xrp",
|
$type: "xrp",
|
||||||
@@ -218,7 +218,6 @@ export const prepareState = (value: string, transactionType?: string) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tx.txFields = rest;
|
tx.txFields = rest;
|
||||||
tx.editorSavedValue = null;
|
|
||||||
|
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
@@ -244,3 +243,10 @@ export const getTxFields = (tt?: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export { transactionsData }
|
export { transactionsData }
|
||||||
|
|
||||||
|
export const transactionsOptions = transactionsData.map(tx => ({
|
||||||
|
value: tx.TransactionType,
|
||||||
|
label: tx.TransactionType,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const defaultTransactionType = transactionsOptions.find(tt => tt.value === 'Payment')
|
||||||
@@ -53,6 +53,7 @@ export const {
|
|||||||
accent: "#9D2DFF",
|
accent: "#9D2DFF",
|
||||||
background: "$gray1",
|
background: "$gray1",
|
||||||
backgroundAlt: "$gray4",
|
backgroundAlt: "$gray4",
|
||||||
|
backgroundOverlay: "$mauve2",
|
||||||
text: "$gray12",
|
text: "$gray12",
|
||||||
textMuted: "$gray10",
|
textMuted: "$gray10",
|
||||||
primary: "$plum",
|
primary: "$plum",
|
||||||
@@ -365,6 +366,7 @@ export const darkTheme = createTheme("dark", {
|
|||||||
...greenDark,
|
...greenDark,
|
||||||
...redDark,
|
...redDark,
|
||||||
deep: "rgb(10, 10, 10)",
|
deep: "rgb(10, 10, 10)",
|
||||||
|
backgroundOverlay: "$mauve5"
|
||||||
// backgroundA: transparentize(0.1, grayDark.gray1),
|
// backgroundA: transparentize(0.1, grayDark.gray1),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
21
styles/keyframes.ts
Normal file
21
styles/keyframes.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { keyframes } from '../stitches.config';
|
||||||
|
|
||||||
|
export const slideUpAndFade = keyframes({
|
||||||
|
"0%": { opacity: 0, transform: "translateY(2px)" },
|
||||||
|
"100%": { opacity: 1, transform: "translateY(0)" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const slideRightAndFade = keyframes({
|
||||||
|
"0%": { opacity: 0, transform: "translateX(-2px)" },
|
||||||
|
"100%": { opacity: 1, transform: "translateX(0)" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const slideDownAndFade = keyframes({
|
||||||
|
"0%": { opacity: 0, transform: "translateY(-2px)" },
|
||||||
|
"100%": { opacity: 1, transform: "translateY(0)" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const slideLeftAndFade = keyframes({
|
||||||
|
"0%": { opacity: 0, transform: "translateX(2px)" },
|
||||||
|
"100%": { opacity: 1, transform: "translateX(0)" },
|
||||||
|
});
|
||||||
@@ -18,13 +18,18 @@ export const tts = {
|
|||||||
ttDEPOSIT_PREAUTH: 19,
|
ttDEPOSIT_PREAUTH: 19,
|
||||||
ttTRUST_SET: 20,
|
ttTRUST_SET: 20,
|
||||||
ttACCOUNT_DELETE: 21,
|
ttACCOUNT_DELETE: 21,
|
||||||
ttHOOK_SET: 22
|
ttHOOK_SET: 22,
|
||||||
|
ttNFTOKEN_MINT: 25,
|
||||||
|
ttNFTOKEN_BURN: 26,
|
||||||
|
ttNFTOKEN_CREATE_OFFER: 27,
|
||||||
|
ttNFTOKEN_CANCEL_OFFER: 28,
|
||||||
|
ttNFTOKEN_ACCEPT_OFFER: 29
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTS = typeof tts;
|
export type TTS = typeof tts;
|
||||||
|
|
||||||
const calculateHookOn = (arr: (keyof TTS)[]) => {
|
const calculateHookOn = (arr: (keyof TTS)[]) => {
|
||||||
let start = '0x00000000003ff5bf';
|
let start = '0x000000003e3ff5bf';
|
||||||
arr.forEach(n => {
|
arr.forEach(n => {
|
||||||
let v = BigInt(start);
|
let v = BigInt(start);
|
||||||
v ^= (BigInt(1) << BigInt(tts[n as keyof TTS]));
|
v ^= (BigInt(1) << BigInt(tts[n as keyof TTS]));
|
||||||
|
|||||||
91
yarn.lock
91
yarn.lock
@@ -594,6 +594,18 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-context-menu@^0.1.6":
|
||||||
|
version "0.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-0.1.6.tgz#0c75f2faffec6c8697247a4b685a432b3c4d07f0"
|
||||||
|
integrity sha512-0qa6ABaeqD+WYI+8iT0jH0QLLcV8Kv0xI+mZL4FFnG4ec9H0v+yngb5cfBBfs9e/KM8mDzFFpaeegqsQlLNqyQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "0.1.0"
|
||||||
|
"@radix-ui/react-context" "0.1.1"
|
||||||
|
"@radix-ui/react-menu" "0.1.6"
|
||||||
|
"@radix-ui/react-primitive" "0.1.4"
|
||||||
|
"@radix-ui/react-use-callback-ref" "0.1.0"
|
||||||
|
|
||||||
"@radix-ui/react-context@0.1.1":
|
"@radix-ui/react-context@0.1.1":
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-0.1.1.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-0.1.1.tgz"
|
||||||
@@ -635,9 +647,9 @@
|
|||||||
"@radix-ui/react-use-callback-ref" "0.1.0"
|
"@radix-ui/react-use-callback-ref" "0.1.0"
|
||||||
"@radix-ui/react-use-escape-keydown" "0.1.0"
|
"@radix-ui/react-use-escape-keydown" "0.1.0"
|
||||||
|
|
||||||
"@radix-ui/react-dropdown-menu@^0.1.1":
|
"@radix-ui/react-dropdown-menu@^0.1.6":
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz#3203229788cd57e552c9f19dcc7008e2b545919c"
|
||||||
integrity sha512-RZhtzjWwJ4ZBN7D8ek4Zn+ilHzYuYta9yIxFnbC0pfqMnSi67IQNONo1tuuNqtFh9SRHacPKc65zo+kBBlxtdg==
|
integrity sha512-RZhtzjWwJ4ZBN7D8ek4Zn+ilHzYuYta9yIxFnbC0pfqMnSi67IQNONo1tuuNqtFh9SRHacPKc65zo+kBBlxtdg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
@@ -1280,14 +1292,6 @@ babel-plugin-macros@^2.6.1:
|
|||||||
cosmiconfig "^6.0.0"
|
cosmiconfig "^6.0.0"
|
||||||
resolve "^1.12.0"
|
resolve "^1.12.0"
|
||||||
|
|
||||||
babel-runtime@^6.26.0:
|
|
||||||
version "6.26.0"
|
|
||||||
resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz"
|
|
||||||
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
|
|
||||||
dependencies:
|
|
||||||
core-js "^2.4.0"
|
|
||||||
regenerator-runtime "^0.11.0"
|
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
||||||
@@ -1552,11 +1556,6 @@ core-js-pure@^3.20.2:
|
|||||||
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz"
|
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz"
|
||||||
integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==
|
integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==
|
||||||
|
|
||||||
core-js@^2.4.0:
|
|
||||||
version "2.6.12"
|
|
||||||
resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz"
|
|
||||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
|
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
|
||||||
@@ -2954,10 +2953,10 @@ natural-compare@^1.4.0:
|
|||||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||||
|
|
||||||
next-auth@^4.0.0-beta.5:
|
next-auth@^4.10.1:
|
||||||
version "4.2.1"
|
version "4.10.1"
|
||||||
resolved "https://registry.npmjs.org/next-auth/-/next-auth-4.2.1.tgz"
|
resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.10.1.tgz#33b29265d12287bb2f6d267c8d415a407c27f0e9"
|
||||||
integrity sha512-XDtt7nqevkNf4EJ2zKAKkI+MFsURf11kx11vPwxrBYA1MHeqWwaWbGOUOI2ekNTvfAg4nTEJJUH3LV2cLrH3Tg==
|
integrity sha512-F00vtwBdyMIIJ8IORHOAOHjVGTDEhhm9+HpB2BQ8r40WtGxqToWWLN7Z+2ZW/z2RFlo3zhcuAtUCPUzVJxtZwQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.16.3"
|
"@babel/runtime" "^7.16.3"
|
||||||
"@panva/hkdf" "^1.0.1"
|
"@panva/hkdf" "^1.0.1"
|
||||||
@@ -3545,11 +3544,6 @@ reconnecting-websocket@^4.4.0:
|
|||||||
resolved "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz"
|
resolved "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz"
|
||||||
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
||||||
|
|
||||||
regenerator-runtime@^0.11.0:
|
|
||||||
version "0.11.1"
|
|
||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
|
|
||||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
|
||||||
|
|
||||||
regenerator-runtime@^0.13.4:
|
regenerator-runtime@^0.13.4:
|
||||||
version "0.13.9"
|
version "0.13.9"
|
||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
||||||
@@ -3648,30 +3642,25 @@ ripple-address-codec@^4.1.0, ripple-address-codec@^4.1.1, ripple-address-codec@^
|
|||||||
base-x "3.0.9"
|
base-x "3.0.9"
|
||||||
create-hash "^1.1.2"
|
create-hash "^1.1.2"
|
||||||
|
|
||||||
ripple-binary-codec@^0.2.4:
|
ripple-address-codec@^4.2.4:
|
||||||
version "0.2.7"
|
version "4.2.4"
|
||||||
resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.2.7.tgz"
|
resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.2.4.tgz#a56c2168c8bb81269ea4d15ed96d6824c5a866f8"
|
||||||
integrity sha512-VD+sHgZK76q3kmO765klFHPDCEveS5SUeg/bUNVpNrj7w2alyDNkbF17XNbAjFv+kSYhfsUudQanoaSs2Y6uzw==
|
integrity sha512-roAOjKz94+FboTItey1XRh5qynwt4xvfBLvbbcx+FiR94Yw2x3LrKLF2GVCMCSAh5I6PkcpADg6AbYsUbGN3nA==
|
||||||
dependencies:
|
dependencies:
|
||||||
babel-runtime "^6.26.0"
|
base-x "3.0.9"
|
||||||
bn.js "^5.1.1"
|
create-hash "^1.1.2"
|
||||||
create-hash "^1.2.0"
|
|
||||||
decimal.js "^10.2.0"
|
|
||||||
inherits "^2.0.4"
|
|
||||||
lodash "^4.17.15"
|
|
||||||
ripple-address-codec "^4.1.0"
|
|
||||||
|
|
||||||
ripple-binary-codec@^1.1.3, ripple-binary-codec@^1.3.0:
|
ripple-binary-codec@=1.4.2, ripple-binary-codec@^0.2.4, ripple-binary-codec@^1.1.3, ripple-binary-codec@^1.4.2:
|
||||||
version "1.3.2"
|
version "1.4.2"
|
||||||
resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-1.3.2.tgz"
|
resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-1.4.2.tgz#cdc35353e4bc7c3a704719247c82b4c4d0b57dd3"
|
||||||
integrity sha512-8VG1vfb3EM1J7ZdPXo9E57Zv2hF4cxT64gP6rGSQzODVgMjiBCWozhN3729qNTGtHItz0e82Oix8v95vWYBQ3A==
|
integrity sha512-EDKIyZMa/6Ay/oNgCwjD9b9CJv0zmBreeHVQeG4BYwy+9GPnIQjNeT5e/aB6OjAnhcmpgbPeBmzwmNVwzxlt0w==
|
||||||
dependencies:
|
dependencies:
|
||||||
assert "^2.0.0"
|
assert "^2.0.0"
|
||||||
big-integer "^1.6.48"
|
big-integer "^1.6.48"
|
||||||
buffer "5.6.0"
|
buffer "5.6.0"
|
||||||
create-hash "^1.2.0"
|
create-hash "^1.2.0"
|
||||||
decimal.js "^10.2.0"
|
decimal.js "^10.2.0"
|
||||||
ripple-address-codec "^4.2.3"
|
ripple-address-codec "^4.2.4"
|
||||||
|
|
||||||
ripple-bs58@^4.0.0:
|
ripple-bs58@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
@@ -4328,10 +4317,10 @@ ws@^7.2.0:
|
|||||||
resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz"
|
resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz"
|
||||||
integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
|
integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
|
||||||
|
|
||||||
xrpl-accountlib@^1.3.2:
|
xrpl-accountlib@^1.5.2:
|
||||||
version "1.3.2"
|
version "1.5.2"
|
||||||
resolved "https://registry.npmjs.org/xrpl-accountlib/-/xrpl-accountlib-1.3.2.tgz"
|
resolved "https://registry.yarnpkg.com/xrpl-accountlib/-/xrpl-accountlib-1.5.2.tgz#8f16abe449fd60ba9ed75597f6ce3f0c45dfff43"
|
||||||
integrity sha512-mXwoumGp0xUiZ7Ty/1o4FHVRK4uLnqngxdYmikZs50drMjlgCUP6GXun2Vf4Uus1fnVnxhXIw+E7peH5OjiOJA==
|
integrity sha512-lieY2/5G9DySqdtgQ0AD/aMMG5Sy/MLAmbIsmsCaF06scM5DpR8s4SsEzgHni7dOG68Wjnb2Uz6tf5aV+l4/Kg==
|
||||||
dependencies:
|
dependencies:
|
||||||
assert "^2.0.0"
|
assert "^2.0.0"
|
||||||
bip32 "^2.0.5"
|
bip32 "^2.0.5"
|
||||||
@@ -4340,13 +4329,13 @@ xrpl-accountlib@^1.3.2:
|
|||||||
elliptic "6.5.4"
|
elliptic "6.5.4"
|
||||||
hash.js "^1.1.7"
|
hash.js "^1.1.7"
|
||||||
ripple-address-codec "^4.1.0"
|
ripple-address-codec "^4.1.0"
|
||||||
ripple-binary-codec "^1.3.0"
|
ripple-binary-codec "^1.4.2"
|
||||||
ripple-hashes "^0.3.4"
|
ripple-hashes "^0.3.4"
|
||||||
ripple-keypairs "^1.0.3"
|
ripple-keypairs "^1.0.3"
|
||||||
ripple-lib "^1.6.4"
|
ripple-lib "^1.6.4"
|
||||||
ripple-secret-codec "^1.0.2"
|
ripple-secret-codec "^1.0.2"
|
||||||
xrpl-secret-numbers "^0.3.3"
|
xrpl-secret-numbers "^0.3.3"
|
||||||
xrpl-sign-keypairs "^2.0.1"
|
xrpl-sign-keypairs "^2.1.1"
|
||||||
|
|
||||||
xrpl-client@^1.9.4:
|
xrpl-client@^1.9.4:
|
||||||
version "1.9.4"
|
version "1.9.4"
|
||||||
@@ -4365,13 +4354,13 @@ xrpl-secret-numbers@^0.3.3:
|
|||||||
brorand "^1.1.0"
|
brorand "^1.1.0"
|
||||||
ripple-keypairs "^1.0.3"
|
ripple-keypairs "^1.0.3"
|
||||||
|
|
||||||
xrpl-sign-keypairs@^2.0.1:
|
xrpl-sign-keypairs@^2.1.1:
|
||||||
version "2.0.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmjs.org/xrpl-sign-keypairs/-/xrpl-sign-keypairs-2.0.1.tgz"
|
resolved "https://registry.yarnpkg.com/xrpl-sign-keypairs/-/xrpl-sign-keypairs-2.1.1.tgz#2f7f2855799c5d4ba091007963825eef1db21a4e"
|
||||||
integrity sha512-84QbE3trxetaw0hqDADCWMx0HH1VAWnTJp0TGoKTGRf1jzTqjI7eNNNw5lmcay2MH8bW/waNzJIF8vSAJSkVrQ==
|
integrity sha512-rKQmUCx+x7gjjJ5zv/Z7bOYR+8I36JwUCFlpuD9UzYD4w2msGQDG0rmxVENyZSfThDBVQ1kEArVn6SMDMe9LUQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
big-integer latest
|
big-integer latest
|
||||||
ripple-binary-codec "^1.3.0"
|
ripple-binary-codec "^1.4.2"
|
||||||
ripple-bs58check latest
|
ripple-bs58check latest
|
||||||
ripple-hashes latest
|
ripple-hashes latest
|
||||||
ripple-keypairs latest
|
ripple-keypairs latest
|
||||||
|
|||||||
Reference in New Issue
Block a user