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

View File

@ -1,78 +1,58 @@
import { faCopy, faDownload } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { AppState } from '../redux/app';
import { ResultState } from '../redux/result';
import { Item, Toolbar } from './toolbar';
import { Tooltip } from './tooltip';
const Container = styled.div`
display: flex;
justify-content: flex-start;
padding: 0.2em 0.5em;
z-index: 3;
const Divider = styled.div`
display: block;
background-color: rgba(0, 0, 0, 0.12);
height: 20px;
width: 1px;
margin: 0 3px;
`;
const Action = 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;
}
const Link = styled.a`
font-size: 0.8em;
color: var(--blue);
opacity: 1;
`;
export const OutputToolbarComponent = (props: {
onCopy?: () => void;
onDownload?: () => void;
}) => {
const output = useSelector<AppState, ResultState['output']>(
state => state.result.output
);
return (
<Container>
<Action onClick={() => props.onCopy && props.onCopy()}>
<Toolbar>
<Item onClick={() => props.onCopy && props.onCopy()}>
<FontAwesomeIcon icon={faCopy}></FontAwesomeIcon>
<Tooltip>Copy</Tooltip>
</Action>
<Action onClick={() => props.onDownload && props.onDownload()}>
</Item>
<Item onClick={() => props.onDownload && props.onDownload()}>
<FontAwesomeIcon icon={faDownload}></FontAwesomeIcon>
<Tooltip>Download</Tooltip>
</Action>
</Container>
</Item>
<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_color: rgba(153, 153, 153, 0.5); /* or #CFCFCF */
--content_height: 85vh;
--content_height: 84vh;
--tooltip_foreground: white;
--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 { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable';
export class CompileAction extends CancellableAction {
@ -24,13 +25,18 @@ export class CompileAction extends CancellableAction {
return;
}
dispatch({ ...new ChangeOutputAction(michelsonCode.result) });
dispatch({
...new ChangeOutputAction(michelsonCode.result, Command.Compile)
});
} catch (ex) {
if (this.isCancelled()) {
return;
}
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 { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeContractAction, ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable';
Tezos.setProvider({
@ -85,13 +86,18 @@ export class DeployAction extends CancellableAction {
return;
}
dispatch({ ...new ChangeContractAction(contract.address) });
dispatch({
...new ChangeContractAction(contract.address, Command.Deploy)
});
} catch (ex) {
if (this.isCancelled()) {
return;
}
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 { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable';
export class DryRunAction extends CancellableAction {
@ -25,13 +26,16 @@ export class DryRunAction extends CancellableAction {
if (this.isCancelled()) {
return;
}
dispatch({ ...new ChangeOutputAction(result.output) });
dispatch({ ...new ChangeOutputAction(result.output, Command.DryRun) });
} catch (ex) {
if (this.isCancelled()) {
return;
}
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 { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable';
export class EvaluateFunctionAction extends CancellableAction {
@ -27,13 +28,18 @@ export class EvaluateFunctionAction extends CancellableAction {
if (this.isCancelled()) {
return;
}
dispatch({ ...new ChangeOutputAction(result.output) });
dispatch({
...new ChangeOutputAction(result.output, Command.EvaluateFunction)
});
} catch (ex) {
if (this.isCancelled()) {
return;
}
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 { DoneLoadingAction, UpdateLoadingAction } from '../loading';
import { ChangeOutputAction } from '../result';
import { Command } from '../types';
import { CancellableAction } from './cancellable';
export class EvaluateValueAction extends CancellableAction {
@ -28,13 +29,18 @@ export class EvaluateValueAction extends CancellableAction {
return;
}
dispatch({ ...new ChangeOutputAction(result.code) });
dispatch({
...new ChangeOutputAction(result.code, Command.EvaluateValue)
});
} catch (ex) {
if (this.isCancelled()) {
return;
}
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 {
ChangeOutput = 'result-change-output',
ChangeContract = 'result-change-contract'
}
export interface ResultState {
command: Command;
output: string;
contract: string;
}
export class ChangeOutputAction {
public readonly type = ActionType.ChangeOutput;
constructor(public payload: ResultState['output']) {}
constructor(
public output: ResultState['output'],
public command: ResultState['command']
) {}
}
export class ChangeContractAction {
public readonly type = ActionType.ChangeContract;
constructor(public payload: ResultState['contract']) {}
constructor(
public contract: ResultState['contract'],
public command: ResultState['command']
) {}
}
type Action = ChangeOutputAction | ChangeContractAction;
const DEFAULT_STATE: ResultState = {
command: Command.Compile,
output: '',
contract: ''
};
@ -30,13 +40,15 @@ export default (state = DEFAULT_STATE, action: Action): ResultState => {
case ActionType.ChangeOutput:
return {
...state,
output: action.payload
output: action.output,
command: action.command
};
case ActionType.ChangeContract:
return {
...state,
output: DEFAULT_STATE.output,
contract: action.payload
contract: action.contract,
command: action.command
};
}
return state;