Merge branch 'view-in-try-michelson-ide' into 'dev'

Added view in try-michelson link

See merge request ligolang/ligo!461
This commit is contained in:
Jev Björsell 2020-02-27 23:19:54 +00:00
commit cd3e7cf32f
10 changed files with 178 additions and 87 deletions

View File

@ -7,6 +7,7 @@ import { AppState } from '../redux/app';
import { CommandState } from '../redux/command'; import { CommandState } from '../redux/command';
import { DoneLoadingAction, LoadingState } from '../redux/loading'; import { DoneLoadingAction, LoadingState } from '../redux/loading';
import { ResultState } from '../redux/result'; import { ResultState } from '../redux/result';
import { Command } from '../redux/types';
import { OutputToolbarComponent } from './output-toolbar'; import { OutputToolbarComponent } from './output-toolbar';
const Container = styled.div<{ visible?: boolean }>` const Container = styled.div<{ visible?: boolean }>`
@ -43,7 +44,7 @@ const CancelButton = styled.div`
const Output = styled.div` const Output = styled.div`
flex: 1; flex: 1;
padding: 0 0.5em 0.5em 0.5em; padding: 0.5em;
display: flex; display: flex;
overflow: scroll; overflow: scroll;
/* This font size is used to calcuate spinner size */ /* This font size is used to calcuate spinner size */
@ -81,20 +82,18 @@ function copyOutput(el: HTMLElement | null) {
} }
} }
function downloadOutput(el: HTMLElement | null) { function downloadOutput(output: string) {
if (el) { const anchor = document.createElement('a');
const anchor = document.createElement('a'); anchor.setAttribute(
anchor.setAttribute( 'href',
'href', `data:text/plain;charset=utf-8,${encodeURIComponent(output)}`
'data:text/plain;charset=utf-8,' + encodeURIComponent(el.innerHTML) );
); anchor.setAttribute('download', 'output.txt');
anchor.setAttribute('download', 'output.txt');
anchor.style.display = 'none'; anchor.style.display = 'none';
document.body.appendChild(anchor); document.body.appendChild(anchor);
anchor.click(); anchor.click();
document.body.removeChild(anchor); document.body.removeChild(anchor);
}
} }
export const OutputTabComponent = (props: { export const OutputTabComponent = (props: {
@ -107,6 +106,9 @@ export const OutputTabComponent = (props: {
const contract = useSelector<AppState, ResultState['contract']>( const contract = useSelector<AppState, ResultState['contract']>(
state => state.result.contract state => state.result.contract
); );
const command = useSelector<AppState, ResultState['command']>(
state => state.result.command
);
const loading = useSelector<AppState, LoadingState>(state => state.loading); const loading = useSelector<AppState, LoadingState>(state => state.loading);
@ -132,10 +134,14 @@ export const OutputTabComponent = (props: {
return ( return (
<Container visible={props.selected}> <Container visible={props.selected}>
{!(loading.loading || output.length === 0) && ( {!(
loading.loading ||
output.length === 0 ||
command !== Command.Compile
) && (
<OutputToolbarComponent <OutputToolbarComponent
onCopy={() => copyOutput(preRef.current)} onCopy={() => copyOutput(preRef.current)}
onDownload={() => downloadOutput(preRef.current)} onDownload={() => downloadOutput(output)}
></OutputToolbarComponent> ></OutputToolbarComponent>
)} )}
<Output id="output" ref={outputRef}> <Output id="output" ref={outputRef}>

View File

@ -1,78 +1,58 @@
import { faCopy, faDownload } from '@fortawesome/free-solid-svg-icons'; import { faCopy, faDownload } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { AppState } from '../redux/app';
import { ResultState } from '../redux/result';
import { Item, Toolbar } from './toolbar';
import { Tooltip } from './tooltip'; import { Tooltip } from './tooltip';
const Container = styled.div` const Divider = styled.div`
display: flex; display: block;
justify-content: flex-start; background-color: rgba(0, 0, 0, 0.12);
padding: 0.2em 0.5em; height: 20px;
z-index: 3; width: 1px;
margin: 0 3px;
`; `;
const Action = styled.div` const Link = styled.a`
z-index: 3; font-size: 0.8em;
position: relative; color: var(--blue);
margin: 4px 6px; opacity: 1;
cursor: pointer;
opacity: 0.5;
color: #444;
::before {
content: '';
display: block;
position: absolute;
z-index: -1;
bottom: -4px;
left: -4px;
right: -4px;
top: -4px;
border-radius: 4px;
background: none;
box-sizing: border-box;
opacity: 0;
transform: scale(0);
transition-property: transform, opacity;
transition-duration: 0.15s;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
:hover::before {
background-color: rgba(32, 33, 36, 0.059);
opacity: 1;
transform: scale(1);
}
:hover {
opacity: 1;
}
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
`; `;
export const OutputToolbarComponent = (props: { export const OutputToolbarComponent = (props: {
onCopy?: () => void; onCopy?: () => void;
onDownload?: () => void; onDownload?: () => void;
}) => { }) => {
const output = useSelector<AppState, ResultState['output']>(
state => state.result.output
);
return ( return (
<Container> <Toolbar>
<Action onClick={() => props.onCopy && props.onCopy()}> <Item onClick={() => props.onCopy && props.onCopy()}>
<FontAwesomeIcon icon={faCopy}></FontAwesomeIcon> <FontAwesomeIcon icon={faCopy}></FontAwesomeIcon>
<Tooltip>Copy</Tooltip> <Tooltip>Copy</Tooltip>
</Action> </Item>
<Action onClick={() => props.onDownload && props.onDownload()}> <Item onClick={() => props.onDownload && props.onDownload()}>
<FontAwesomeIcon icon={faDownload}></FontAwesomeIcon> <FontAwesomeIcon icon={faDownload}></FontAwesomeIcon>
<Tooltip>Download</Tooltip> <Tooltip>Download</Tooltip>
</Action> </Item>
</Container> <Divider></Divider>
<Item>
<Link
target="_blank"
rel="noopener noreferrer"
href={`https://try-michelson.tzalpha.net/?source=${encodeURIComponent(
output
)}`}
>
View in Try-Michelson IDE
</Link>
</Item>
</Toolbar>
); );
}; };

View File

@ -0,0 +1,65 @@
import React from 'react';
import styled from 'styled-components';
const Container = styled.div`
display: flex;
align-items: center;
padding: 0.2em 0.5em;
z-index: 3;
`;
export const Group = styled.div`
display: flex;
align-items: center;
`;
export const Item = styled.div`
z-index: 3;
position: relative;
margin: 4px 6px;
cursor: pointer;
opacity: 0.5;
color: #444;
::before {
content: '';
display: block;
position: absolute;
z-index: -1;
bottom: -4px;
left: -4px;
right: -4px;
top: -4px;
border-radius: 4px;
background: none;
box-sizing: border-box;
opacity: 0;
transform: scale(0);
transition-property: transform, opacity;
transition-duration: 0.15s;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
:hover::before {
background-color: rgba(32, 33, 36, 0.059);
opacity: 1;
transform: scale(1);
}
:hover {
opacity: 1;
}
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
`;
export const Toolbar = (props: any) => {
return <Container>{props.children}</Container>;
};

View File

@ -48,7 +48,7 @@
--font_ghost_weight: 700; --font_ghost_weight: 700;
--font_ghost_color: rgba(153, 153, 153, 0.5); /* or #CFCFCF */ --font_ghost_color: rgba(153, 153, 153, 0.5); /* or #CFCFCF */
--content_height: 85vh; --content_height: 84vh;
--tooltip_foreground: white; --tooltip_foreground: white;
--tooltip_background: rgba(0, 0, 0, 0.75) /*#404040*/; --tooltip_background: rgba(0, 0, 0, 0.75) /*#404040*/;

View File

@ -4,6 +4,7 @@ import { compileContract, getErrorMessage } from '../../services/api';
import { AppState } from '../app'; import { AppState } from '../app';
import { DoneLoadingAction, UpdateLoadingAction } from '../loading'; import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result'; import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable'; import { CancellableAction } from './cancellable';
export class CompileAction extends CancellableAction { export class CompileAction extends CancellableAction {
@ -24,13 +25,18 @@ export class CompileAction extends CancellableAction {
return; return;
} }
dispatch({ ...new ChangeOutputAction(michelsonCode.result) }); dispatch({
...new ChangeOutputAction(michelsonCode.result, Command.Compile)
});
} catch (ex) { } catch (ex) {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ dispatch({
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`) ...new ChangeOutputAction(
`Error: ${getErrorMessage(ex)}`,
Command.Compile
)
}); });
} }

View File

@ -7,6 +7,7 @@ import { AppState } from '../app';
import { MichelsonFormat } from '../compile'; import { MichelsonFormat } from '../compile';
import { DoneLoadingAction, UpdateLoadingAction } from '../loading'; import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeContractAction, ChangeOutputAction } from '../result'; import { ChangeContractAction, ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable'; import { CancellableAction } from './cancellable';
Tezos.setProvider({ Tezos.setProvider({
@ -85,13 +86,18 @@ export class DeployAction extends CancellableAction {
return; return;
} }
dispatch({ ...new ChangeContractAction(contract.address) }); dispatch({
...new ChangeContractAction(contract.address, Command.Deploy)
});
} catch (ex) { } catch (ex) {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ dispatch({
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`) ...new ChangeOutputAction(
`Error: ${getErrorMessage(ex)}`,
Command.Deploy
)
}); });
} }

View File

@ -4,6 +4,7 @@ import { dryRun, getErrorMessage } from '../../services/api';
import { AppState } from '../app'; import { AppState } from '../app';
import { DoneLoadingAction, UpdateLoadingAction } from '../loading'; import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result'; import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable'; import { CancellableAction } from './cancellable';
export class DryRunAction extends CancellableAction { export class DryRunAction extends CancellableAction {
@ -25,13 +26,16 @@ export class DryRunAction extends CancellableAction {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ ...new ChangeOutputAction(result.output) }); dispatch({ ...new ChangeOutputAction(result.output, Command.DryRun) });
} catch (ex) { } catch (ex) {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ dispatch({
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`) ...new ChangeOutputAction(
`Error: ${getErrorMessage(ex)}`,
Command.DryRun
)
}); });
} }

View File

@ -4,6 +4,7 @@ import { getErrorMessage, runFunction } from '../../services/api';
import { AppState } from '../app'; import { AppState } from '../app';
import { DoneLoadingAction, UpdateLoadingAction } from '../loading'; import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result'; import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable'; import { CancellableAction } from './cancellable';
export class EvaluateFunctionAction extends CancellableAction { export class EvaluateFunctionAction extends CancellableAction {
@ -27,13 +28,18 @@ export class EvaluateFunctionAction extends CancellableAction {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ ...new ChangeOutputAction(result.output) }); dispatch({
...new ChangeOutputAction(result.output, Command.EvaluateFunction)
});
} catch (ex) { } catch (ex) {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ dispatch({
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`) ...new ChangeOutputAction(
`Error: ${getErrorMessage(ex)}`,
Command.EvaluateFunction
)
}); });
} }

View File

@ -4,6 +4,7 @@ import { evaluateValue, getErrorMessage } from '../../services/api';
import { AppState } from '../app'; import { AppState } from '../app';
import { DoneLoadingAction, UpdateLoadingAction } from '../loading'; import { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result'; import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable'; import { CancellableAction } from './cancellable';
export class EvaluateValueAction extends CancellableAction { export class EvaluateValueAction extends CancellableAction {
@ -28,13 +29,18 @@ export class EvaluateValueAction extends CancellableAction {
return; return;
} }
dispatch({ ...new ChangeOutputAction(result.code) }); dispatch({
...new ChangeOutputAction(result.code, Command.EvaluateValue)
});
} catch (ex) { } catch (ex) {
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
dispatch({ dispatch({
...new ChangeOutputAction(`Error: ${getErrorMessage(ex)}`) ...new ChangeOutputAction(
`Error: ${getErrorMessage(ex)}`,
Command.EvaluateValue
)
}); });
} }

View File

@ -1,26 +1,36 @@
import { Command } from './types';
export enum ActionType { export enum ActionType {
ChangeOutput = 'result-change-output', ChangeOutput = 'result-change-output',
ChangeContract = 'result-change-contract' ChangeContract = 'result-change-contract'
} }
export interface ResultState { export interface ResultState {
command: Command;
output: string; output: string;
contract: string; contract: string;
} }
export class ChangeOutputAction { export class ChangeOutputAction {
public readonly type = ActionType.ChangeOutput; public readonly type = ActionType.ChangeOutput;
constructor(public payload: ResultState['output']) {} constructor(
public output: ResultState['output'],
public command: ResultState['command']
) {}
} }
export class ChangeContractAction { export class ChangeContractAction {
public readonly type = ActionType.ChangeContract; public readonly type = ActionType.ChangeContract;
constructor(public payload: ResultState['contract']) {} constructor(
public contract: ResultState['contract'],
public command: ResultState['command']
) {}
} }
type Action = ChangeOutputAction | ChangeContractAction; type Action = ChangeOutputAction | ChangeContractAction;
const DEFAULT_STATE: ResultState = { const DEFAULT_STATE: ResultState = {
command: Command.Compile,
output: '', output: '',
contract: '' contract: ''
}; };
@ -30,13 +40,15 @@ export default (state = DEFAULT_STATE, action: Action): ResultState => {
case ActionType.ChangeOutput: case ActionType.ChangeOutput:
return { return {
...state, ...state,
output: action.payload output: action.output,
command: action.command
}; };
case ActionType.ChangeContract: case ActionType.ChangeContract:
return { return {
...state, ...state,
output: DEFAULT_STATE.output, output: DEFAULT_STATE.output,
contract: action.payload contract: action.contract,
command: action.command
}; };
} }
return state; return state;