Merge branch 'show-storage-after-deploy' into 'dev'

Show storage after deploy

See merge request ligolang/ligo!486
This commit is contained in:
Jev Björsell 2020-03-06 20:59:56 +00:00
commit efc06be1f6
25 changed files with 374 additions and 242 deletions

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { EditorComponent } from './components/editor'; import { EditorComponent } from './components/editor/editor';
import { Examples } from './components/examples'; import { Examples } from './components/examples';
import { FloatButtonComponent } from './components/float-button'; import { FloatButtonComponent } from './components/float-button';
import { HeaderComponent } from './components/header'; import { HeaderComponent } from './components/header';

View File

@ -4,7 +4,7 @@ import React, { 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'; import { Command } from '../../redux/types';
const Container = styled.div` const Container = styled.div`
flex: 2; flex: 2;

View File

@ -2,10 +2,10 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { ChangeEntrypointAction, ChangeMichelsonFormatAction, CompileState, MichelsonFormat } from '../redux/compile'; import { ChangeEntrypointAction, ChangeMichelsonFormatAction, CompileState, MichelsonFormat } from '../../redux/compile';
import { CheckboxComponent } from './checkbox'; import { CheckboxComponent } from '../form/checkbox';
import { Group, HGroup, Input, Label } from './inputs'; import { Group, HGroup, Input, Label } from '../form/inputs';
const Container = styled.div``; const Container = styled.div``;

View File

@ -2,14 +2,14 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import { CompileAction } from '../redux/actions/compile'; import { CompileAction } from '../../redux/actions/compile';
import { DeployAction } from '../redux/actions/deploy'; 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 { 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 { CommandSelectComponent } from './command-select';
import { CompilePaneComponent } from './compile-pane'; import { CompilePaneComponent } from './compile-pane';
import { DeployPaneComponent } from './deploy-pane'; import { DeployPaneComponent } from './deploy-pane';

View File

@ -2,10 +2,10 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { ChangeEntrypointAction, ChangeStorageAction, DeployState, UseTezBridgeAction } from '../redux/deploy'; import { ChangeEntrypointAction, ChangeStorageAction, DeployState, UseTezBridgeAction } from '../../redux/deploy';
import { CheckboxComponent } from './checkbox'; import { CheckboxComponent } from '../form/checkbox';
import { Group, HGroup, Input, Label, Textarea } from './inputs'; import { Group, HGroup, Input, Label, Textarea } from '../form/inputs';
const Container = styled.div``; const Container = styled.div``;

View File

@ -2,9 +2,9 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { ChangeEntrypointAction, ChangeParametersAction, ChangeStorageAction, DryRunState } from '../redux/dry-run'; import { ChangeEntrypointAction, ChangeParametersAction, ChangeStorageAction, DryRunState } from '../../redux/dry-run';
import { Group, Input, Label, Textarea } from './inputs'; import { Group, Input, Label, Textarea } from '../form/inputs';
const Container = styled.div``; const Container = styled.div``;

View File

@ -2,9 +2,9 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { ChangeEntrypointAction, ChangeParametersAction, EvaluateFunctionState } from '../redux/evaluate-function'; import { ChangeEntrypointAction, ChangeParametersAction, EvaluateFunctionState } from '../../redux/evaluate-function';
import { Group, Input, Label, Textarea } from './inputs'; import { Group, Input, Label, Textarea } from '../form/inputs';
const Container = styled.div``; const Container = styled.div``;

View File

@ -2,9 +2,9 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { ChangeEntrypointAction, EvaluateValueState } from '../redux/evaluate-value'; import { ChangeEntrypointAction, EvaluateValueState } from '../../redux/evaluate-value';
import { Group, Input, Label } from './inputs'; import { Group, Input, Label } from '../form/inputs';
const Container = styled.div``; const Container = styled.div``;

View File

@ -2,11 +2,11 @@ import React from 'react';
import { useDispatch, useSelector } from 'react-redux'; 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 { ChangeTitleAction } from '../../redux/editor';
import { ShareComponent } from '../share';
import { EditableTitleComponent } from './editable-title'; import { EditableTitleComponent } from './editable-title';
import { MonacoComponent } from './monaco'; import { MonacoComponent } from './monaco';
import { ShareComponent } from './share';
import { SyntaxSelectComponent } from './syntax-select'; import { SyntaxSelectComponent } from './syntax-select';
const Container = styled.div` const Container = styled.div`

View File

@ -3,9 +3,9 @@ import React, { useEffect, useRef } from 'react';
import { useDispatch, useStore } from 'react-redux'; import { useDispatch, useStore } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { AppState } from '../redux/app'; import { AppState } from '../../redux/app';
import { ChangeCodeAction, ChangeDirtyAction } from '../redux/editor'; import { ChangeCodeAction, ChangeDirtyAction } from '../../redux/editor';
import { ClearSelectedAction } from '../redux/examples'; import { ClearSelectedAction } from '../../redux/examples';
const Container = styled.div` const Container = styled.div`
height: var(--content_height); height: var(--content_height);

View File

@ -5,10 +5,10 @@ import OutsideClickHandler from 'react-outside-click-handler';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import { AppState } from '../redux/app'; import { AppState } from '../../redux/app';
import { ChangeLanguageAction, EditorState } from '../redux/editor'; import { ChangeLanguageAction, EditorState } from '../../redux/editor';
import { Language } from '../redux/types'; import { Language } from '../../redux/types';
import { Tooltip } from './tooltip'; import { Tooltip } from '../tooltip';
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;

View File

@ -1,194 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { PushSpinner } from 'react-spinners-kit';
import styled, { css } from 'styled-components';
import { AppState } from '../redux/app';
import { CommandState } from '../redux/command';
import { DoneLoadingAction, LoadingState } from '../redux/loading';
import { ResultState } from '../redux/result';
import { Command } from '../redux/types';
import { OutputToolbarComponent } from './output-toolbar';
const Container = styled.div<{ visible?: boolean }>`
position: absolute;
box-sizing: border-box;
width: 100%;
height: 100%;
font-family: Menlo, Monaco, 'Courier New', monospace;
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 0.2s ease-in;
${props =>
props.visible &&
css`
transform: translateX(0px);
`}
`;
const CancelButton = styled.div`
display: flex;
justify-content: center;
align-items: center;
color: white;
background-color: #fc683a;
cursor: pointer;
user-select: none;
margin: 1em;
padding: 0.5em 1em;
`;
const Output = styled.div`
flex: 1;
padding: 0.5em;
display: flex;
overflow: scroll;
/* This font size is used to calcuate spinner size */
font-size: 1em;
`;
const LoadingContainer = styled.div`
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const LoadingMessage = styled.div`
padding: 1em 0;
`;
const Pre = styled.pre`
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 OutputTabComponent = (props: {
selected?: boolean;
onCancel?: () => void;
}) => {
const output = useSelector<AppState, ResultState['output']>(
state => state.result.output
);
const contract = useSelector<AppState, ResultState['contract']>(
state => state.result.contract
);
const command = useSelector<AppState, ResultState['command']>(
state => state.result.command
);
const loading = useSelector<AppState, LoadingState>(state => state.loading);
const dispatchedAction = useSelector<
AppState,
CommandState['dispatchedAction']
>(state => state.command.dispatchedAction);
const dispatch = useDispatch();
const outputRef = useRef<HTMLDivElement>(null);
const preRef = useRef<HTMLPreElement>(null);
const [spinnerSize, setSpinnerSize] = useState(50);
useEffect(() => {
const outputEl = (outputRef.current as unknown) as HTMLElement;
const fontSize = window
.getComputedStyle(outputEl, null)
.getPropertyValue('font-size');
setSpinnerSize(parseFloat(fontSize) * 3);
}, [setSpinnerSize]);
return (
<Container visible={props.selected}>
{!(
loading.loading ||
output.length === 0 ||
command !== Command.Compile
) && (
<OutputToolbarComponent
onCopy={() => copyOutput(preRef.current)}
onDownload={() => downloadOutput(output)}
></OutputToolbarComponent>
)}
<Output id="output" ref={outputRef}>
{loading.loading && (
<LoadingContainer>
<PushSpinner size={spinnerSize} color="#fedace" />
<LoadingMessage>{loading.message}</LoadingMessage>
<CancelButton
onClick={() => {
if (dispatchedAction) {
dispatchedAction.cancel();
}
dispatch({ ...new DoneLoadingAction() });
if (props.onCancel) {
props.onCancel();
}
}}
>
Cancel
</CancelButton>
</LoadingContainer>
)}
{!loading.loading &&
((output.length !== 0 && <Pre ref={preRef}>{output}</Pre>) ||
(contract.length !== 0 && (
<span>
The contract was successfully deployed to the babylonnet test
network.
<br />
<br />
The address of your new contract is: <i>{contract}</i>
<br />
<br />
View your new contract using{' '}
<a
target="_blank"
rel="noopener noreferrer"
href={`https://better-call.dev/babylon/${contract}`}
>
Better Call Dev
</a>
!
</span>
)))}
</Output>
</Container>
);
};

View File

@ -0,0 +1,73 @@
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';
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: scroll;
`;
const Pre = styled.pre`
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 = () => {
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>
);
};

View File

@ -0,0 +1,68 @@
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { AppState } from '../../redux/app';
import { ResultState } from '../../redux/result';
const Container = styled.div<{ visible?: boolean }>`
display: flex;
flex-direction: column;
height: 100%;
`;
const Output = styled.div`
flex: 1;
padding: 0.5em 0.5em 0 0.5em;
display: flex;
flex-direction: column;
overflow: auto;
`;
const Pre = styled.pre`
padding: 0.5em;
margin: 0 -0.5em;
overflow: scroll;
height: 100%;
`;
export const DeployOutputPane = () => {
const output = useSelector<AppState, ResultState['output']>(
state => state.result.output
);
const contract = useSelector<AppState, ResultState['contract']>(
state => state.result.contract
);
return (
<Container>
<Output id="output">
{contract && (
<div>
The contract was successfully deployed to the babylonnet test
network.
<br />
<br />
View your new contract using{' '}
<a
target="_blank"
rel="noopener noreferrer"
href={`https://better-call.dev/babylon/${contract}`}
>
Better Call Dev
</a>
!
<br />
<br />
<b>The address of your new contract is: </b>
<i>{contract}</i>
<br />
<br />
<b>The initial storage of your contract is: </b>
</div>
)}
{output && <Pre>{output}</Pre>}
</Output>
</Container>
);
};

View File

@ -0,0 +1,81 @@
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { PushSpinner } from 'react-spinners-kit';
import styled from 'styled-components';
import { AppState } from '../../redux/app';
import { CommandState } from '../../redux/command';
import { DoneLoadingAction, LoadingState } from '../../redux/loading';
const Container = styled.div`
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/* This font size is used to calcuate spinner size */
font-size: 1em;
`;
const Cancel = styled.div`
display: flex;
justify-content: center;
align-items: center;
color: white;
background-color: #fc683a;
cursor: pointer;
user-select: none;
margin: 1em;
padding: 0.5em 1em;
`;
const Message = styled.div`
padding: 1em 0;
`;
export const Loading = (props: { onCancel?: () => void }) => {
const loading = useSelector<AppState, LoadingState>(state => state.loading);
const dispatchedAction = useSelector<
AppState,
CommandState['dispatchedAction']
>(state => state.command.dispatchedAction);
const dispatch = useDispatch();
const containerRef = useRef<HTMLDivElement>(null);
const [spinnerSize, setSpinnerSize] = useState(50);
useEffect(() => {
const el = (containerRef.current as unknown) as HTMLElement;
const fontSize = window
.getComputedStyle(el, null)
.getPropertyValue('font-size');
setSpinnerSize(parseFloat(fontSize) * 3);
}, [setSpinnerSize]);
return (
<Container ref={containerRef}>
<PushSpinner size={spinnerSize} color="#fedace" />
<Message>{loading.message}</Message>
<Cancel
onClick={() => {
if (dispatchedAction) {
dispatchedAction.cancel();
}
dispatch({ ...new DoneLoadingAction() });
if (props.onCancel) {
props.onCancel();
}
}}
>
Cancel
</Cancel>
</Container>
);
};

View File

@ -0,0 +1,37 @@
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { AppState } from '../../redux/app';
import { ResultState } from '../../redux/result';
const Container = styled.div<{ visible?: boolean }>`
display: flex;
flex-direction: column;
flex: 1;
`;
const Output = styled.div`
flex: 1;
padding: 0.5em;
display: flex;
overflow: scroll;
`;
const Pre = styled.pre`
margin: 0;
`;
export const OutputPane = () => {
const output = useSelector<AppState, ResultState['output']>(
state => state.result.output
);
return (
<Container>
<Output id="output">
<Pre>{output}</Pre>
</Output>
</Container>
);
};

View File

@ -0,0 +1,58 @@
import React from 'react';
import { useSelector } from 'react-redux';
import styled, { css } from 'styled-components';
import { AppState } from '../../redux/app';
import { LoadingState } from '../../redux/loading';
import { ResultState } from '../../redux/result';
import { Command } from '../../redux/types';
import { CompileOutputPane } from './compile-output-pane';
import { DeployOutputPane } from './deploy-output-pane';
import { Loading } from './loading';
import { OutputPane } from './output-pane';
const Container = styled.div<{ visible?: boolean }>`
position: absolute;
box-sizing: border-box;
width: 100%;
height: 100%;
font-family: Menlo, Monaco, 'Courier New', monospace;
display: flex;
flex-direction: column;
transform: translateX(100%);
transition: transform 0.2s ease-in;
${props =>
props.visible &&
css`
transform: translateX(0px);
`}
`;
export const OutputTab = (props: {
selected?: boolean;
onCancel?: () => void;
}) => {
const command = useSelector<AppState, ResultState['command']>(
state => state.result.command
);
const loading = useSelector<AppState, LoadingState['loading']>(
state => state.loading.loading
);
const renderResult = () => {
if (loading) {
return <Loading onCancel={props.onCancel}></Loading>;
} else if (command === Command.Compile) {
return <CompileOutputPane></CompileOutputPane>;
} else if (command === Command.Deploy) {
return <DeployOutputPane></DeployOutputPane>;
}
return <OutputPane></OutputPane>;
};
return <Container visible={props.selected}>{renderResult()}</Container>;
};

View File

@ -4,10 +4,10 @@ import React from 'react';
import { useSelector } from 'react-redux'; import { 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 { ResultState } from '../redux/result'; import { ResultState } from '../../redux/result';
import { Item, Toolbar } from './toolbar'; import { Item, Toolbar } from '../toolbar';
import { Tooltip } from './tooltip'; import { Tooltip } from '../tooltip';
const Divider = styled.div` const Divider = styled.div`
display: block; display: block;

View File

@ -1,8 +1,8 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import { ConfigureTabComponent } from './configure-tab'; import { ConfigureTabComponent } from './configure/configure-tab';
import { OutputTabComponent } from './output-tab'; import { OutputTab } from './output/output-tab';
const Container = styled.div` const Container = styled.div`
flex: 1; flex: 1;
@ -86,12 +86,12 @@ export const TabsPanelComponent = () => {
selectTab(TABS[1]); selectTab(TABS[1]);
}} }}
></ConfigureTabComponent> ></ConfigureTabComponent>
<OutputTabComponent <OutputTab
selected={selectedTab.index === 1} selected={selectedTab.index === 1}
onCancel={() => { onCancel={() => {
selectTab(TABS[0]); selectTab(TABS[0]);
}} }}
></OutputTabComponent> ></OutputTab>
</Content> </Content>
</Container> </Container>
); );

View File

@ -57,7 +57,10 @@ export class DeployAction extends CancellableAction {
} }
dispatch({ ...new UpdateLoadingAction('Deploying to babylon network...') }); dispatch({ ...new UpdateLoadingAction('Deploying to babylon network...') });
return await op.contract(); return {
address: (await op.contract()).address,
storage: michelsonStorage
};
} }
async deployOnServerSide(dispatch: Dispatch, getState: () => AppState) { async deployOnServerSide(dispatch: Dispatch, getState: () => AppState) {
@ -89,10 +92,16 @@ export class DeployAction extends CancellableAction {
dispatch({ dispatch({
...new ChangeContractAction(contract.address, Command.Deploy) ...new ChangeContractAction(contract.address, Command.Deploy)
}); });
dispatch({
...new ChangeOutputAction(contract.storage, Command.Deploy)
});
} catch (ex) { } catch (ex) {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({
...new ChangeContractAction('', Command.Deploy)
});
dispatch({ dispatch({
...new ChangeOutputAction( ...new ChangeOutputAction(
`Error: ${getErrorMessage(ex)}`, `Error: ${getErrorMessage(ex)}`,

View File

@ -57,13 +57,13 @@ export async function deployHandler(req: Request, res: Response) {
const contract = await op.contract(); const contract = await op.contract();
res.send({ ...contract }); res.send({ address: contract.address, storage: michelsonStorage });
} catch (ex) { } catch (ex) {
if (ex instanceof CompilerError) { if (ex instanceof CompilerError) {
res.status(400).json({ error: ex.message }); res.status(400).json({ error: ex.message });
} else { } else {
logger.error(ex); logger.error(ex);
res.sendStatus(500); res.status(500).json({ error: ex.message });
} }
} }
} }