Tezos client command
This commit is contained in:
parent
4ed4203b46
commit
1aecc4eb04
@ -25,6 +25,7 @@
|
|||||||
"redux": "^4.0.4",
|
"redux": "^4.0.4",
|
||||||
"redux-devtools": "^3.5.0",
|
"redux-devtools": "^3.5.0",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
|
"slugify": "^1.4.0",
|
||||||
"styled-components": "^4.4.0",
|
"styled-components": "^4.4.0",
|
||||||
"typescript": "3.6.4"
|
"typescript": "3.6.4"
|
||||||
},
|
},
|
||||||
|
@ -7,15 +7,17 @@ import { DeployAction } from '../../redux/actions/deploy';
|
|||||||
import { DryRunAction } from '../../redux/actions/dry-run';
|
import { DryRunAction } from '../../redux/actions/dry-run';
|
||||||
import { EvaluateFunctionAction } from '../../redux/actions/evaluate-function';
|
import { EvaluateFunctionAction } from '../../redux/actions/evaluate-function';
|
||||||
import { EvaluateValueAction } from '../../redux/actions/evaluate-value';
|
import { EvaluateValueAction } from '../../redux/actions/evaluate-value';
|
||||||
|
import { GenerateCommandAction } from '../../redux/actions/generate-command';
|
||||||
import { AppState } from '../../redux/app';
|
import { AppState } from '../../redux/app';
|
||||||
import { ChangeDispatchedAction, ChangeSelectedAction, CommandState } from '../../redux/command';
|
import { ChangeDispatchedAction, ChangeSelectedAction, CommandState } from '../../redux/command';
|
||||||
import { Command } from '../../redux/types';
|
import { Command } from '../../redux/types';
|
||||||
import { CommandSelectComponent } from './command-select';
|
import { Option, Select } from '../form/select';
|
||||||
import { CompilePaneComponent } from './compile-pane';
|
import { CompilePaneComponent } from './compile-pane';
|
||||||
import { DeployPaneComponent } from './deploy-pane';
|
import { DeployPaneComponent } from './deploy-pane';
|
||||||
import { DryRunPaneComponent } from './dry-run-pane';
|
import { DryRunPaneComponent } from './dry-run-pane';
|
||||||
import { EvaluateFunctionPaneComponent } from './evaluate-function-pane';
|
import { EvaluateFunctionPaneComponent } from './evaluate-function-pane';
|
||||||
import { EvaluateValuePaneComponent } from './evaluate-value-pane';
|
import { EvaluateValuePaneComponent } from './evaluate-value-pane';
|
||||||
|
import { GenerateCommandPaneComponent } from './generate-command-pane';
|
||||||
|
|
||||||
const Container = styled.div<{ visible?: boolean }>`
|
const Container = styled.div<{ visible?: boolean }>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -58,8 +60,8 @@ const RunButton = styled.div`
|
|||||||
background-color: var(--orange);
|
background-color: var(--orange);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CommandPaneContainer = styled.div`
|
const SelectCommand = styled(Select)`
|
||||||
padding-top: 1em;
|
flex: 2;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function createAction(command: Command) {
|
function createAction(command: Command) {
|
||||||
@ -74,6 +76,8 @@ function createAction(command: Command) {
|
|||||||
return new EvaluateValueAction();
|
return new EvaluateValueAction();
|
||||||
case Command.EvaluateFunction:
|
case Command.EvaluateFunction:
|
||||||
return new EvaluateFunctionAction();
|
return new EvaluateFunctionAction();
|
||||||
|
case Command.GenerateCommand:
|
||||||
|
return new GenerateCommandAction();
|
||||||
default:
|
default:
|
||||||
throw new Error('Unsupported command');
|
throw new Error('Unsupported command');
|
||||||
}
|
}
|
||||||
@ -97,12 +101,20 @@ export const ConfigureTabComponent = (props: {
|
|||||||
return (
|
return (
|
||||||
<Container visible={props.selected}>
|
<Container visible={props.selected}>
|
||||||
<CommonActionsGroup>
|
<CommonActionsGroup>
|
||||||
<CommandSelectComponent
|
<SelectCommand
|
||||||
selected={command}
|
id="command-select"
|
||||||
|
value={command}
|
||||||
onChange={command => {
|
onChange={command => {
|
||||||
dispatch({ ...new ChangeSelectedAction(command) });
|
dispatch({ ...new ChangeSelectedAction(command) });
|
||||||
}}
|
}}
|
||||||
></CommandSelectComponent>
|
>
|
||||||
|
<Option value={Command.Compile}>Compile</Option>
|
||||||
|
<Option value={Command.Deploy}>Deploy</Option>
|
||||||
|
<Option value={Command.DryRun}>Dry Run</Option>
|
||||||
|
<Option value={Command.EvaluateFunction}>Evaluate Function</Option>
|
||||||
|
<Option value={Command.EvaluateValue}>Evaluate Value</Option>
|
||||||
|
<Option value={Command.GenerateCommand}>Generate Command</Option>
|
||||||
|
</SelectCommand>
|
||||||
<RunButton
|
<RunButton
|
||||||
id="run"
|
id="run"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -120,7 +132,6 @@ export const ConfigureTabComponent = (props: {
|
|||||||
Run
|
Run
|
||||||
</RunButton>
|
</RunButton>
|
||||||
</CommonActionsGroup>
|
</CommonActionsGroup>
|
||||||
<CommandPaneContainer>
|
|
||||||
{(command === Command.Compile && (
|
{(command === Command.Compile && (
|
||||||
<CompilePaneComponent></CompilePaneComponent>
|
<CompilePaneComponent></CompilePaneComponent>
|
||||||
)) ||
|
)) ||
|
||||||
@ -135,8 +146,10 @@ export const ConfigureTabComponent = (props: {
|
|||||||
)) ||
|
)) ||
|
||||||
(command === Command.EvaluateValue && (
|
(command === Command.EvaluateValue && (
|
||||||
<EvaluateValuePaneComponent></EvaluateValuePaneComponent>
|
<EvaluateValuePaneComponent></EvaluateValuePaneComponent>
|
||||||
|
)) ||
|
||||||
|
(command === Command.GenerateCommand && (
|
||||||
|
<GenerateCommandPaneComponent></GenerateCommandPaneComponent>
|
||||||
))}
|
))}
|
||||||
</CommandPaneContainer>
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../../redux/app';
|
||||||
|
import {
|
||||||
|
ChangeCommandAction,
|
||||||
|
ChangeEntrypointAction,
|
||||||
|
ChangeStorageAction,
|
||||||
|
ChangeToolAction,
|
||||||
|
GenerateCommandState,
|
||||||
|
} from '../../redux/generate-command';
|
||||||
|
import { Tool, ToolCommand } from '../../redux/types';
|
||||||
|
import { AccessFunctionLabel, Group, Input, Label, Textarea } from '../form/inputs';
|
||||||
|
import { Option, Select } from '../form/select';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
overflow: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GenerateCommandPaneComponent = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const tool = useSelector<AppState, GenerateCommandState['tool']>(
|
||||||
|
state => state.generateCommand.tool
|
||||||
|
);
|
||||||
|
|
||||||
|
const command = useSelector<AppState, GenerateCommandState['command']>(
|
||||||
|
state => state.generateCommand.command
|
||||||
|
);
|
||||||
|
|
||||||
|
const entrypoint = useSelector<AppState, GenerateCommandState['entrypoint']>(
|
||||||
|
state => state.generateCommand.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
const storage = useSelector<AppState, GenerateCommandState['storage']>(
|
||||||
|
state => state.generateCommand.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Group>
|
||||||
|
<Label>Tool</Label>
|
||||||
|
<Select
|
||||||
|
id="tool"
|
||||||
|
value={tool}
|
||||||
|
onChange={value => dispatch({ ...new ChangeToolAction(value) })}
|
||||||
|
>
|
||||||
|
<Option value={Tool.TezosClient}>Tezos Client</Option>
|
||||||
|
</Select>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Label>Command</Label>
|
||||||
|
<Select
|
||||||
|
id="tool-command"
|
||||||
|
value={command}
|
||||||
|
onChange={value => dispatch({ ...new ChangeCommandAction(value) })}
|
||||||
|
>
|
||||||
|
<Option value={ToolCommand.Originate}>Originate</Option>
|
||||||
|
</Select>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<AccessFunctionLabel htmlFor="entrypoint"></AccessFunctionLabel>
|
||||||
|
<Input
|
||||||
|
id="entrypoint"
|
||||||
|
value={entrypoint}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeEntrypointAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</Group>
|
||||||
|
<Group>
|
||||||
|
<Label htmlFor="storage">Storage</Label>
|
||||||
|
<Textarea
|
||||||
|
id="storage"
|
||||||
|
rows={9}
|
||||||
|
value={storage}
|
||||||
|
onChange={ev =>
|
||||||
|
dispatch({ ...new ChangeStorageAction(ev.target.value) })
|
||||||
|
}
|
||||||
|
></Textarea>
|
||||||
|
</Group>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -3,11 +3,12 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { AppState } from '../../redux/app';
|
import { AppState } from '../../redux/app';
|
||||||
import { ChangeTitleAction } from '../../redux/editor';
|
import { ChangeLanguageAction, ChangeTitleAction, EditorState } from '../../redux/editor';
|
||||||
|
import { Language } from '../../redux/types';
|
||||||
|
import { Option, Select } from '../form/select';
|
||||||
import { ShareComponent } from '../share';
|
import { ShareComponent } from '../share';
|
||||||
import { EditableTitleComponent } from './editable-title';
|
import { EditableTitleComponent } from './editable-title';
|
||||||
import { MonacoComponent } from './monaco';
|
import { MonacoComponent } from './monaco';
|
||||||
import { SyntaxSelectComponent } from './syntax-select';
|
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
flex: 2;
|
flex: 2;
|
||||||
@ -35,6 +36,9 @@ const StyledEditableTitleComponent = styled(EditableTitleComponent)`
|
|||||||
export const EditorComponent = () => {
|
export const EditorComponent = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const title = useSelector<AppState, string>(state => state.editor.title);
|
const title = useSelector<AppState, string>(state => state.editor.title);
|
||||||
|
const language = useSelector<AppState, EditorState['language']>(
|
||||||
|
state => state.editor.language
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
@ -49,7 +53,17 @@ export const EditorComponent = () => {
|
|||||||
}}
|
}}
|
||||||
></StyledEditableTitleComponent>
|
></StyledEditableTitleComponent>
|
||||||
</LeftActions>
|
</LeftActions>
|
||||||
<SyntaxSelectComponent></SyntaxSelectComponent>
|
<Select
|
||||||
|
id="syntax-select"
|
||||||
|
value={language}
|
||||||
|
onChange={language => {
|
||||||
|
dispatch({ ...new ChangeLanguageAction(language) });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Option value={Language.PascaLigo}>PascaLIGO</Option>
|
||||||
|
<Option value={Language.CameLigo}>CameLIGO</Option>
|
||||||
|
<Option value={Language.ReasonLIGO}>ReasonLIGO</Option>
|
||||||
|
</Select>
|
||||||
</Header>
|
</Header>
|
||||||
<MonacoComponent></MonacoComponent>
|
<MonacoComponent></MonacoComponent>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -1,157 +0,0 @@
|
|||||||
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import OutsideClickHandler from 'react-outside-click-handler';
|
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
|
||||||
import styled, { css } from 'styled-components';
|
|
||||||
|
|
||||||
import { AppState } from '../../redux/app';
|
|
||||||
import { ChangeLanguageAction, EditorState } from '../../redux/editor';
|
|
||||||
import { Language } from '../../redux/types';
|
|
||||||
import { Tooltip } from '../tooltip';
|
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
min-width: 10em;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Header = styled.div`
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
height: 2em;
|
|
||||||
padding: 0 0.5em;
|
|
||||||
|
|
||||||
border: 1px solid var(--blue_trans1);
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Label = styled.div`
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ArrowIcon = ({ rotate, ...props }: { rotate: boolean }) => (
|
|
||||||
<FontAwesomeIcon {...props} icon={faCaretDown} size="lg"></FontAwesomeIcon>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Arrow = styled(ArrowIcon)`
|
|
||||||
margin-left: 0.5em;
|
|
||||||
pointer-events: none;
|
|
||||||
color: var(--blue_trans1);
|
|
||||||
transition: transform 0.15s ease-in;
|
|
||||||
|
|
||||||
${(props: { rotate: boolean }) =>
|
|
||||||
props.rotate &&
|
|
||||||
css`
|
|
||||||
transform: rotate(180deg);
|
|
||||||
`};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const List = styled.ul`
|
|
||||||
position: absolute;
|
|
||||||
list-style-type: none;
|
|
||||||
background-color: white;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-shadow: 1px 3px 10px 0px rgba(153, 153, 153, 0.4);
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.15s ease-in;
|
|
||||||
|
|
||||||
${(props: { visible: boolean }) =>
|
|
||||||
props.visible &&
|
|
||||||
css`
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Option = styled.li`
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 2em;
|
|
||||||
padding: 0 0.5em;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-radius: 3px 3px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-radius: 0 0 3px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--blue_trans2);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SyntaxSelectComponent = () => {
|
|
||||||
const OPTIONS = {
|
|
||||||
[Language.PascaLigo]: 'PascaLIGO',
|
|
||||||
[Language.CameLigo]: 'CameLIGO',
|
|
||||||
[Language.ReasonLIGO]: 'ReasonLIGO'
|
|
||||||
};
|
|
||||||
|
|
||||||
const moveOptionToTop = (option: Language) => {
|
|
||||||
return Object.keys(OPTIONS).reduce((list, entry) => {
|
|
||||||
if (entry === option) {
|
|
||||||
list.unshift(entry);
|
|
||||||
} else {
|
|
||||||
list.push(entry as Language);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}, [] as Language[]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const language = useSelector<AppState, EditorState['language']>(
|
|
||||||
state => state.editor.language
|
|
||||||
);
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [opened, open] = useState(false);
|
|
||||||
|
|
||||||
const selectOption = (option: Language) => {
|
|
||||||
if (language !== option) {
|
|
||||||
dispatch({ ...new ChangeLanguageAction(option) });
|
|
||||||
}
|
|
||||||
open(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<OutsideClickHandler onOutsideClick={() => open(false)}>
|
|
||||||
<List visible={opened}>
|
|
||||||
{moveOptionToTop(language).map(option => (
|
|
||||||
<Option
|
|
||||||
id={option}
|
|
||||||
key={option}
|
|
||||||
onClick={() => selectOption(option)}
|
|
||||||
>
|
|
||||||
<span>{OPTIONS[option]}</span>
|
|
||||||
</Option>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</OutsideClickHandler>
|
|
||||||
<Header id="syntax-select" onClick={() => open(true)}>
|
|
||||||
<Label>
|
|
||||||
{OPTIONS[language]}
|
|
||||||
<Arrow rotate={opened}></Arrow>
|
|
||||||
</Label>
|
|
||||||
<Tooltip>Select syntax</Tooltip>
|
|
||||||
</Header>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
const Container = styled.div<{ checked: boolean }>`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -44,7 +44,6 @@ export const CheckboxComponent = (props: {
|
|||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
className={props.className}
|
className={props.className}
|
||||||
checked={isChecked}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newState = !isChecked;
|
const newState = !isChecked;
|
||||||
|
|
||||||
|
@ -4,17 +4,20 @@ import styled from 'styled-components';
|
|||||||
export const Group = styled.div`
|
export const Group = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin: 0.7em 0 0.7em 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const HGroup = styled.div`
|
export const HGroup = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 0.4em 0 0.4em 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Label = styled.label`
|
export const Label = styled.label`
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: var(--label_foreground);
|
color: var(--label_foreground);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
margin: 0.3em 0 0.3em 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Hint = styled.span`
|
export const Hint = styled.span`
|
||||||
@ -25,7 +28,7 @@ export const Hint = styled.span`
|
|||||||
export const AccessFunctionLabel = (props: any) => {
|
export const AccessFunctionLabel = (props: any) => {
|
||||||
return (
|
return (
|
||||||
<Label {...props}>
|
<Label {...props}>
|
||||||
Access Function
|
Access function
|
||||||
<br />
|
<br />
|
||||||
<Hint>The function name from where your contract will start</Hint>
|
<Hint>The function name from where your contract will start</Hint>
|
||||||
</Label>
|
</Label>
|
||||||
@ -33,7 +36,7 @@ export const AccessFunctionLabel = (props: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Input = styled.input`
|
export const Input = styled.input`
|
||||||
margin: 0.3em 0 0.7em 0;
|
/* margin: 0.3em 0 0.7em 0; */
|
||||||
background-color: var(--input_background);
|
background-color: var(--input_background);
|
||||||
border-style: none;
|
border-style: none;
|
||||||
border-bottom: 5px solid #e1f1ff;
|
border-bottom: 5px solid #e1f1ff;
|
||||||
@ -49,7 +52,7 @@ export const Input = styled.input`
|
|||||||
|
|
||||||
export const Textarea = styled.textarea`
|
export const Textarea = styled.textarea`
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
margin: 0.3em 0 0.7em 0;
|
/* margin: 0.3em 0 0.7em 0; */
|
||||||
background-color: var(--input_background);
|
background-color: var(--input_background);
|
||||||
border-style: none;
|
border-style: none;
|
||||||
border-bottom: 5px solid #e1f1ff;
|
border-bottom: 5px solid #e1f1ff;
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
|
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import React, { useState } from 'react';
|
import React, { FunctionComponentElement, useState } from 'react';
|
||||||
import OutsideClickHandler from 'react-outside-click-handler';
|
import OutsideClickHandler from 'react-outside-click-handler';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
import { Command } from '../../redux/types';
|
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
flex: 2;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: 8em;
|
min-width: 8em;
|
||||||
z-index: 2;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Header = styled.div`
|
const Header = styled.div`
|
||||||
@ -20,8 +16,8 @@ const Header = styled.div`
|
|||||||
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
min-height: 2em;
|
min-height: 2em;
|
||||||
padding: 0 0.5em;
|
padding: 0 0.5em;
|
||||||
|
|
||||||
@ -33,6 +29,7 @@ const ArrowIcon = ({ rotate, ...props }: { rotate: boolean }) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const Arrow = styled(ArrowIcon)`
|
const Arrow = styled(ArrowIcon)`
|
||||||
|
z-index: 1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: var(--blue_trans1);
|
color: var(--blue_trans1);
|
||||||
transition: transform 0.15s ease-in;
|
transition: transform 0.15s ease-in;
|
||||||
@ -45,6 +42,7 @@ const Arrow = styled(ArrowIcon)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const List = styled.ul`
|
const List = styled.ul`
|
||||||
|
z-index: 1;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@ -66,7 +64,7 @@ const List = styled.ul`
|
|||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Option = styled.li`
|
const OptionContainer = styled.li`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
@ -90,56 +88,73 @@ const Option = styled.li`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CommandSelectComponent = (props: {
|
interface OptionProps {
|
||||||
selected: Command;
|
value: string;
|
||||||
onChange?: (value: Command) => void;
|
children: string;
|
||||||
}) => {
|
}
|
||||||
const OPTIONS = {
|
|
||||||
[Command.Compile]: 'Compile',
|
type OptionElement = FunctionComponentElement<OptionProps>;
|
||||||
[Command.Deploy]: 'Deploy',
|
|
||||||
[Command.DryRun]: 'Dry Run',
|
export const Option = (props: OptionProps) => {
|
||||||
[Command.EvaluateFunction]: 'Evaluate Function',
|
// This is an empty component. It's used as a way to get option information into its parent. It is not inserted into the DOM.
|
||||||
[Command.EvaluateValue]: 'Evaluate Value'
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const moveOptionToTop = (option: Command) => {
|
export const Select = (props: {
|
||||||
return Object.keys(OPTIONS).reduce((list, entry) => {
|
id: string;
|
||||||
if (entry === option) {
|
value: any;
|
||||||
|
children: OptionElement[] | OptionElement;
|
||||||
|
onChange?: (value: any) => void;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const [isOpen, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const options = Array.isArray(props.children)
|
||||||
|
? props.children
|
||||||
|
: [props.children];
|
||||||
|
|
||||||
|
const labelLookup = new Map(
|
||||||
|
options.map(
|
||||||
|
child => [child.props.value, child.props.children] as [string, string]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const moveOptionToTop = (value: string) => {
|
||||||
|
return options.reduce((list, entry) => {
|
||||||
|
if (entry.props.value === value) {
|
||||||
list.unshift(entry);
|
list.unshift(entry);
|
||||||
} else {
|
} else {
|
||||||
list.push(entry as Command);
|
list.push(entry);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}, [] as Command[]);
|
}, [] as OptionElement[]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [opened, open] = useState(false);
|
const selectOption = (option: OptionElement) => {
|
||||||
|
if (props.value !== option.props.value && props.onChange) {
|
||||||
const selectOption = (option: Command) => {
|
props.onChange(option.props.value);
|
||||||
if (props.selected !== option && props.onChange) {
|
|
||||||
props.onChange(option);
|
|
||||||
}
|
}
|
||||||
open(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container className={props.className}>
|
||||||
<OutsideClickHandler onOutsideClick={() => open(false)}>
|
<OutsideClickHandler onOutsideClick={() => setOpen(false)}>
|
||||||
<List visible={opened}>
|
<List visible={isOpen}>
|
||||||
{moveOptionToTop(props.selected).map(option => (
|
{moveOptionToTop(props.value).map((option: OptionElement) => (
|
||||||
<Option
|
<OptionContainer
|
||||||
id={option}
|
id={option.props.value}
|
||||||
key={option}
|
key={option.props.value}
|
||||||
onClick={() => selectOption(option)}
|
onClick={() => selectOption(option)}
|
||||||
>
|
>
|
||||||
<span>{OPTIONS[option]}</span>
|
<span>{option.props.children}</span>
|
||||||
</Option>
|
</OptionContainer>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</OutsideClickHandler>
|
</OutsideClickHandler>
|
||||||
<Header id="command-select" onClick={() => open(true)}>
|
<Header id={props.id} onClick={() => setOpen(true)}>
|
||||||
<span>{OPTIONS[props.selected]}</span>
|
<span>{labelLookup.get(props.value)}</span>
|
||||||
<Arrow rotate={opened}></Arrow>
|
<Arrow rotate={isOpen}></Arrow>
|
||||||
</Header>
|
</Header>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
@ -1,83 +0,0 @@
|
|||||||
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import styled, { css } from 'styled-components';
|
|
||||||
|
|
||||||
const Container = styled.div<{ checked: boolean }>`
|
|
||||||
position: relative;
|
|
||||||
height: 2em;
|
|
||||||
width: 3.5em;
|
|
||||||
border-radius: 1em;
|
|
||||||
background-color: var(--blue_trans1);
|
|
||||||
border: 1px solid var(--blue);
|
|
||||||
transition: background-color 0.2s ease-in;
|
|
||||||
|
|
||||||
${props =>
|
|
||||||
props.checked &&
|
|
||||||
css`
|
|
||||||
background-color: var(--blue);
|
|
||||||
`};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Button = styled.div<{ checked: boolean }>`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
height: 2em;
|
|
||||||
width: 2em;
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
right: calc(1.5em);
|
|
||||||
transition: right 0.2s ease-in;
|
|
||||||
|
|
||||||
${props =>
|
|
||||||
props.checked &&
|
|
||||||
css`
|
|
||||||
right: 0;
|
|
||||||
`};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CheckIcon = ({ visible, ...props }: { visible: boolean }) => (
|
|
||||||
<FontAwesomeIcon {...props} icon={faCheck}></FontAwesomeIcon>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Check = styled(CheckIcon)`
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.2s ease-in;
|
|
||||||
color: var(--blue);
|
|
||||||
|
|
||||||
${props =>
|
|
||||||
!props.visible &&
|
|
||||||
css`
|
|
||||||
opacity: 0;
|
|
||||||
`}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const ToggleComponent = (props: {
|
|
||||||
checked: boolean;
|
|
||||||
onChanged: (value: boolean) => void;
|
|
||||||
className?: string;
|
|
||||||
}) => {
|
|
||||||
const [isChecked, setChecked] = useState(props.checked);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container className={props.className} checked={isChecked}>
|
|
||||||
<Button
|
|
||||||
checked={isChecked}
|
|
||||||
onClick={() => {
|
|
||||||
const newState = !isChecked;
|
|
||||||
|
|
||||||
setChecked(newState);
|
|
||||||
props.onChanged(newState);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Check visible={isChecked}></Check>
|
|
||||||
</Button>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
@ -5,6 +5,7 @@ import styled from 'styled-components';
|
|||||||
import { AppState } from '../../redux/app';
|
import { AppState } from '../../redux/app';
|
||||||
import { ResultState } from '../../redux/result';
|
import { ResultState } from '../../redux/result';
|
||||||
import { OutputToolbarComponent } from './output-toolbar';
|
import { OutputToolbarComponent } from './output-toolbar';
|
||||||
|
import { copyOutput, downloadOutput } from './utils';
|
||||||
|
|
||||||
const Container = styled.div<{ visible?: boolean }>`
|
const Container = styled.div<{ visible?: boolean }>`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -23,35 +24,6 @@ const Pre = styled.pre`
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function copyOutput(el: HTMLElement | null) {
|
|
||||||
if (el) {
|
|
||||||
const range = document.createRange();
|
|
||||||
range.selectNodeContents(el);
|
|
||||||
|
|
||||||
const selection = window.getSelection();
|
|
||||||
|
|
||||||
if (selection) {
|
|
||||||
selection.removeAllRanges();
|
|
||||||
selection.addRange(range);
|
|
||||||
document.execCommand('copy');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadOutput(output: string) {
|
|
||||||
const anchor = document.createElement('a');
|
|
||||||
anchor.setAttribute(
|
|
||||||
'href',
|
|
||||||
`data:text/plain;charset=utf-8,${encodeURIComponent(output)}`
|
|
||||||
);
|
|
||||||
anchor.setAttribute('download', 'output.txt');
|
|
||||||
|
|
||||||
anchor.style.display = 'none';
|
|
||||||
document.body.appendChild(anchor);
|
|
||||||
anchor.click();
|
|
||||||
document.body.removeChild(anchor);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CompileOutputPane = () => {
|
export const CompileOutputPane = () => {
|
||||||
const output = useSelector<AppState, ResultState['output']>(
|
const output = useSelector<AppState, ResultState['output']>(
|
||||||
state => state.result.output
|
state => state.result.output
|
||||||
@ -62,6 +34,7 @@ export const CompileOutputPane = () => {
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<OutputToolbarComponent
|
<OutputToolbarComponent
|
||||||
|
showTryMichelson={true}
|
||||||
onCopy={() => copyOutput(preRef.current)}
|
onCopy={() => copyOutput(preRef.current)}
|
||||||
onDownload={() => downloadOutput(output)}
|
onDownload={() => downloadOutput(output)}
|
||||||
></OutputToolbarComponent>
|
></OutputToolbarComponent>
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { AppState } from '../../redux/app';
|
||||||
|
import { ResultState } from '../../redux/result';
|
||||||
|
import { OutputToolbarComponent } from './output-toolbar';
|
||||||
|
import { copyOutput, downloadOutput } from './utils';
|
||||||
|
|
||||||
|
const Container = styled.div<{ visible?: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Output = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Pre = styled.pre`
|
||||||
|
margin: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GenerateCommandOutputPane = () => {
|
||||||
|
const output = useSelector<AppState, ResultState['output']>(
|
||||||
|
state => state.result.output
|
||||||
|
);
|
||||||
|
|
||||||
|
const preRef = useRef<HTMLPreElement>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<OutputToolbarComponent
|
||||||
|
onCopy={() => copyOutput(preRef.current)}
|
||||||
|
onDownload={() => downloadOutput(output)}
|
||||||
|
></OutputToolbarComponent>
|
||||||
|
<Output id="output">
|
||||||
|
<Pre ref={preRef}>{output}</Pre>
|
||||||
|
</Output>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -8,6 +8,7 @@ import { ResultState } from '../../redux/result';
|
|||||||
import { Command } from '../../redux/types';
|
import { Command } from '../../redux/types';
|
||||||
import { CompileOutputPane } from './compile-output-pane';
|
import { CompileOutputPane } from './compile-output-pane';
|
||||||
import { DeployOutputPane } from './deploy-output-pane';
|
import { DeployOutputPane } from './deploy-output-pane';
|
||||||
|
import { GenerateCommandOutputPane } from './generate-command-output-pane';
|
||||||
import { Loading } from './loading';
|
import { Loading } from './loading';
|
||||||
import { OutputPane } from './output-pane';
|
import { OutputPane } from './output-pane';
|
||||||
|
|
||||||
@ -41,14 +42,21 @@ export const OutputTab = (props: {
|
|||||||
const loading = useSelector<AppState, LoadingState['loading']>(
|
const loading = useSelector<AppState, LoadingState['loading']>(
|
||||||
state => state.loading.loading
|
state => state.loading.loading
|
||||||
);
|
);
|
||||||
|
const output = useSelector<AppState, ResultState['output']>(
|
||||||
|
state => state.result.output
|
||||||
|
);
|
||||||
|
|
||||||
const renderResult = () => {
|
const renderResult = () => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Loading onCancel={props.onCancel}></Loading>;
|
return <Loading onCancel={props.onCancel}></Loading>;
|
||||||
|
} else if (!output) {
|
||||||
|
return <></>;
|
||||||
} else if (command === Command.Compile) {
|
} else if (command === Command.Compile) {
|
||||||
return <CompileOutputPane></CompileOutputPane>;
|
return <CompileOutputPane></CompileOutputPane>;
|
||||||
} else if (command === Command.Deploy) {
|
} else if (command === Command.Deploy) {
|
||||||
return <DeployOutputPane></DeployOutputPane>;
|
return <DeployOutputPane></DeployOutputPane>;
|
||||||
|
} else if (command === Command.GenerateCommand) {
|
||||||
|
return <GenerateCommandOutputPane></GenerateCommandOutputPane>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <OutputPane></OutputPane>;
|
return <OutputPane></OutputPane>;
|
||||||
|
@ -24,6 +24,7 @@ const Link = styled.a`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const OutputToolbarComponent = (props: {
|
export const OutputToolbarComponent = (props: {
|
||||||
|
showTryMichelson?: boolean;
|
||||||
onCopy?: () => void;
|
onCopy?: () => void;
|
||||||
onDownload?: () => void;
|
onDownload?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
@ -41,7 +42,8 @@ export const OutputToolbarComponent = (props: {
|
|||||||
<FontAwesomeIcon icon={faDownload}></FontAwesomeIcon>
|
<FontAwesomeIcon icon={faDownload}></FontAwesomeIcon>
|
||||||
<Tooltip>Download</Tooltip>
|
<Tooltip>Download</Tooltip>
|
||||||
</Item>
|
</Item>
|
||||||
<Divider></Divider>
|
{props.showTryMichelson && <Divider></Divider>}
|
||||||
|
{props.showTryMichelson && (
|
||||||
<Item>
|
<Item>
|
||||||
<Link
|
<Link
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -53,6 +55,7 @@ export const OutputToolbarComponent = (props: {
|
|||||||
View in Try-Michelson IDE
|
View in Try-Michelson IDE
|
||||||
</Link>
|
</Link>
|
||||||
</Item>
|
</Item>
|
||||||
|
)}
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
28
tools/webide/packages/client/src/components/output/utils.ts
Normal file
28
tools/webide/packages/client/src/components/output/utils.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export function copyOutput(el: HTMLElement | null) {
|
||||||
|
if (el) {
|
||||||
|
const range = document.createRange();
|
||||||
|
range.selectNodeContents(el);
|
||||||
|
|
||||||
|
const selection = window.getSelection();
|
||||||
|
|
||||||
|
if (selection) {
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
document.execCommand('copy');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function downloadOutput(output: string) {
|
||||||
|
const anchor = document.createElement('a');
|
||||||
|
anchor.setAttribute(
|
||||||
|
'href',
|
||||||
|
`data:text/plain;charset=utf-8,${encodeURIComponent(output)}`
|
||||||
|
);
|
||||||
|
anchor.setAttribute('download', 'output.txt');
|
||||||
|
|
||||||
|
anchor.style.display = 'none';
|
||||||
|
document.body.appendChild(anchor);
|
||||||
|
anchor.click();
|
||||||
|
document.body.removeChild(anchor);
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
import { Tezos } from '@taquito/taquito';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
import slugify from 'slugify';
|
||||||
|
|
||||||
|
import { compileContract, compileStorage, getErrorMessage } from '../../services/api';
|
||||||
|
import { AppState } from '../app';
|
||||||
|
import { MichelsonFormat } from '../compile';
|
||||||
|
import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
|
||||||
|
import { ChangeOutputAction } from '../result';
|
||||||
|
import { Command } from '../types';
|
||||||
|
import { CancellableAction } from './cancellable';
|
||||||
|
|
||||||
|
const URL = 'https://api.tez.ie/keys/carthagenet/';
|
||||||
|
const AUTHORIZATION_HEADER = 'Bearer ligo-ide';
|
||||||
|
|
||||||
|
export async function fetchRandomPrivateKey(): Promise<string> {
|
||||||
|
const response = await fetch(URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: AUTHORIZATION_HEADER }
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GenerateCommandAction extends CancellableAction {
|
||||||
|
getAction() {
|
||||||
|
return async (dispatch: Dispatch, getState: () => AppState) => {
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Compiling contract...') });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { editor, generateCommand } = getState();
|
||||||
|
|
||||||
|
const michelsonCodeJson = await compileContract(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
generateCommand.entrypoint,
|
||||||
|
MichelsonFormat.Json
|
||||||
|
);
|
||||||
|
|
||||||
|
const michelsonCode = await compileContract(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
generateCommand.entrypoint
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Compiling storage...') });
|
||||||
|
const michelsonStorageJson = await compileStorage(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
generateCommand.entrypoint,
|
||||||
|
generateCommand.storage,
|
||||||
|
MichelsonFormat.Json
|
||||||
|
);
|
||||||
|
|
||||||
|
const michelsonStorage = await compileStorage(
|
||||||
|
editor.language,
|
||||||
|
editor.code,
|
||||||
|
generateCommand.entrypoint,
|
||||||
|
generateCommand.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new UpdateLoadingAction('Estimating burn cap...') });
|
||||||
|
|
||||||
|
await Tezos.importKey(await fetchRandomPrivateKey());
|
||||||
|
|
||||||
|
const estimate = await Tezos.estimate.originate({
|
||||||
|
code: JSON.parse(michelsonCodeJson.result),
|
||||||
|
init: JSON.parse(michelsonStorageJson.result)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = slugify(editor.title).toLowerCase() || 'untitled';
|
||||||
|
const output = `tezos-client \\
|
||||||
|
${generateCommand.command} \\
|
||||||
|
contract \\
|
||||||
|
${title} \\
|
||||||
|
transferring 0 \\
|
||||||
|
from $YOUR_SOURCE_ACCOUNT \\
|
||||||
|
running '${michelsonCode.result.trim()}' \\
|
||||||
|
--init '${michelsonStorage.result.trim()}' \\
|
||||||
|
--burn-cap ${estimate.burnFeeMutez / 1000000}`;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(output, Command.GenerateCommand)
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
if (this.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
...new ChangeOutputAction(
|
||||||
|
`Error: ${getErrorMessage(ex)}`,
|
||||||
|
Command.GenerateCommand
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ ...new DoneLoadingAction() });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import editor, { EditorState } from './editor';
|
|||||||
import evaluateFunction, { EvaluateFunctionState } from './evaluate-function';
|
import evaluateFunction, { EvaluateFunctionState } from './evaluate-function';
|
||||||
import evaluateValue, { EvaluateValueState } from './evaluate-value';
|
import evaluateValue, { EvaluateValueState } from './evaluate-value';
|
||||||
import examples, { ExamplesState } from './examples';
|
import examples, { ExamplesState } from './examples';
|
||||||
|
import generateCommand, { GenerateCommandState } from './generate-command';
|
||||||
import loading, { LoadingState } from './loading';
|
import loading, { LoadingState } from './loading';
|
||||||
import result, { ResultState } from './result';
|
import result, { ResultState } from './result';
|
||||||
import share, { ShareState } from './share';
|
import share, { ShareState } from './share';
|
||||||
@ -20,6 +21,7 @@ export interface AppState {
|
|||||||
deploy: DeployState;
|
deploy: DeployState;
|
||||||
evaluateFunction: EvaluateFunctionState;
|
evaluateFunction: EvaluateFunctionState;
|
||||||
evaluateValue: EvaluateValueState;
|
evaluateValue: EvaluateValueState;
|
||||||
|
generateCommand: GenerateCommandState;
|
||||||
result: ResultState;
|
result: ResultState;
|
||||||
command: CommandState;
|
command: CommandState;
|
||||||
examples: ExamplesState;
|
examples: ExamplesState;
|
||||||
@ -34,6 +36,7 @@ export default combineReducers({
|
|||||||
deploy,
|
deploy,
|
||||||
evaluateFunction,
|
evaluateFunction,
|
||||||
evaluateValue,
|
evaluateValue,
|
||||||
|
generateCommand,
|
||||||
result,
|
result,
|
||||||
command,
|
command,
|
||||||
examples,
|
examples,
|
||||||
|
81
tools/webide/packages/client/src/redux/generate-command.ts
Normal file
81
tools/webide/packages/client/src/redux/generate-command.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { Tool, ToolCommand } from './types';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
ChangeTool = 'generate-command-change-tool',
|
||||||
|
ChangeCommand = 'generate-command-change-command',
|
||||||
|
ChangeEntrypoint = 'generate-command-change-entrypoint',
|
||||||
|
ChangeStorage = 'generate-command-change-storage'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenerateCommandState {
|
||||||
|
tool: Tool;
|
||||||
|
command: ToolCommand;
|
||||||
|
entrypoint: string;
|
||||||
|
originationAccount: string;
|
||||||
|
storage: string;
|
||||||
|
burnCap: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeToolAction {
|
||||||
|
public readonly type = ActionType.ChangeTool;
|
||||||
|
constructor(public payload: GenerateCommandState['tool']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeCommandAction {
|
||||||
|
public readonly type = ActionType.ChangeCommand;
|
||||||
|
constructor(public payload: GenerateCommandState['command']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeEntrypointAction {
|
||||||
|
public readonly type = ActionType.ChangeEntrypoint;
|
||||||
|
constructor(public payload: GenerateCommandState['entrypoint']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeStorageAction {
|
||||||
|
public readonly type = ActionType.ChangeStorage;
|
||||||
|
constructor(public payload: GenerateCommandState['storage']) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| ChangeToolAction
|
||||||
|
| ChangeCommandAction
|
||||||
|
| ChangeEntrypointAction
|
||||||
|
| ChangeStorageAction;
|
||||||
|
|
||||||
|
const DEFAULT_STATE: GenerateCommandState = {
|
||||||
|
tool: Tool.TezosClient,
|
||||||
|
command: ToolCommand.Originate,
|
||||||
|
entrypoint: '',
|
||||||
|
storage: '',
|
||||||
|
originationAccount: '',
|
||||||
|
burnCap: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (
|
||||||
|
state = DEFAULT_STATE,
|
||||||
|
action: Action
|
||||||
|
): GenerateCommandState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.ChangeTool:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tool: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeCommand:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
command: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeEntrypoint:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
entrypoint: action.payload
|
||||||
|
};
|
||||||
|
case ActionType.ChangeStorage:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
storage: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
@ -9,5 +9,14 @@ export enum Command {
|
|||||||
DryRun = 'dry-run',
|
DryRun = 'dry-run',
|
||||||
EvaluateValue = 'evaluate-value',
|
EvaluateValue = 'evaluate-value',
|
||||||
EvaluateFunction = 'evaluate-function',
|
EvaluateFunction = 'evaluate-function',
|
||||||
Deploy = 'deploy'
|
Deploy = 'deploy',
|
||||||
|
GenerateCommand = 'generate-command'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Tool {
|
||||||
|
TezosClient = 'tezos-client'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ToolCommand {
|
||||||
|
Originate = 'originate'
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,9 @@ export async function compileStorage(
|
|||||||
storage: string,
|
storage: string,
|
||||||
format?: string
|
format?: string
|
||||||
) {
|
) {
|
||||||
|
// For whatever reason, storage set by examples is not treated as a string. So we convert it here.
|
||||||
|
storage = `${storage}`;
|
||||||
|
|
||||||
const response = await axios.post('/api/compile-storage', {
|
const response = await axios.post('/api/compile-storage', {
|
||||||
syntax,
|
syntax,
|
||||||
code,
|
code,
|
||||||
|
52
tools/webide/packages/server/src/handlers/compile-storage.ts
Normal file
52
tools/webide/packages/server/src/handlers/compile-storage.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import joi from '@hapi/joi';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
import { CompilerError, LigoCompiler } from '../ligo-compiler';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
|
||||||
|
interface CompileBody {
|
||||||
|
syntax: string;
|
||||||
|
code: string;
|
||||||
|
entrypoint: string;
|
||||||
|
storage: string;
|
||||||
|
format?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateRequest = (body: any): { value: CompileBody; error: any } => {
|
||||||
|
return joi
|
||||||
|
.object({
|
||||||
|
syntax: joi.string().required(),
|
||||||
|
code: joi.string().required(),
|
||||||
|
entrypoint: joi.string().required(),
|
||||||
|
storage: joi.string().required(),
|
||||||
|
format: joi.string().optional()
|
||||||
|
})
|
||||||
|
.validate(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function compileStorageHandler(req: Request, res: Response) {
|
||||||
|
const { error, value: body } = validateRequest(req.body);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const michelsonStorage = await new LigoCompiler().compileStorage(
|
||||||
|
body.syntax,
|
||||||
|
body.code,
|
||||||
|
body.entrypoint,
|
||||||
|
body.format || 'text',
|
||||||
|
body.storage
|
||||||
|
);
|
||||||
|
|
||||||
|
res.send({ result: michelsonStorage });
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex instanceof CompilerError) {
|
||||||
|
res.status(400).json({ error: ex.message });
|
||||||
|
} else {
|
||||||
|
logger.error(ex);
|
||||||
|
res.sendStatus(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { dirname, join } from 'path';
|
|||||||
|
|
||||||
import { compileContractHandler } from './handlers/compile-contract';
|
import { compileContractHandler } from './handlers/compile-contract';
|
||||||
import { compileExpressionHandler } from './handlers/compile-expression';
|
import { compileExpressionHandler } from './handlers/compile-expression';
|
||||||
|
import { compileStorageHandler } from './handlers/compile-storage';
|
||||||
import { deployHandler } from './handlers/deploy';
|
import { deployHandler } from './handlers/deploy';
|
||||||
import { dryRunHandler } from './handlers/dry-run';
|
import { dryRunHandler } from './handlers/dry-run';
|
||||||
import { evaluateValueHandler } from './handlers/evaluate-value';
|
import { evaluateValueHandler } from './handlers/evaluate-value';
|
||||||
@ -51,6 +52,7 @@ app.get(
|
|||||||
);
|
);
|
||||||
app.post('/api/compile-contract', compileContractHandler);
|
app.post('/api/compile-contract', compileContractHandler);
|
||||||
app.post('/api/compile-expression', compileExpressionHandler);
|
app.post('/api/compile-expression', compileExpressionHandler);
|
||||||
|
app.post('/api/compile-storage', compileStorageHandler);
|
||||||
app.post('/api/dry-run', dryRunHandler);
|
app.post('/api/dry-run', dryRunHandler);
|
||||||
app.post('/api/share', shareHandler);
|
app.post('/api/share', shareHandler);
|
||||||
app.post('/api/evaluate-value', evaluateValueHandler);
|
app.post('/api/evaluate-value', evaluateValueHandler);
|
||||||
|
@ -10706,6 +10706,11 @@ slice-ansi@^2.1.0:
|
|||||||
astral-regex "^1.0.0"
|
astral-regex "^1.0.0"
|
||||||
is-fullwidth-code-point "^2.0.0"
|
is-fullwidth-code-point "^2.0.0"
|
||||||
|
|
||||||
|
slugify@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.0.tgz#c9557c653c54b0c7f7a8e786ef3431add676d2cb"
|
||||||
|
integrity sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ==
|
||||||
|
|
||||||
snakeize@^0.1.0:
|
snakeize@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d"
|
resolved "https://registry.yarnpkg.com/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d"
|
||||||
|
Loading…
Reference in New Issue
Block a user