remix-project mirror
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
remix-project/src/universal-dapp-ui.js

337 lines
9.0 KiB

/* global */
'use strict'
var $ = require('jquery')
var ethJSUtil = require('ethereumjs-util')
var remixLib = require('remix-lib')
var yo = require('yo-yo')
var txFormat = require('./app/execution/txFormat')
var txHelper = require('./app/execution/txHelper')
var txExecution = require('./app/execution/txExecution')
var helper = require('./lib/helper')
var executionContext = require('./execution-context')
var copyToClipboard = require('./app/ui/copy-to-clipboard')
// -------------- styling ----------------------
var csjs = require('csjs-inject')
var styleGuide = remixLib.ui.themeChooser
var styles = styleGuide.chooser()
var css = csjs`
.instanceTitleContainer {
display: flex;
align-items: center;
}
.title {
${styles.rightPanel.runTab.titlebox_RunTab}
display: flex;
justify-content: end;
align-items: center;
font-size: 11px;
height: 30px;
width: 97%;
overflow: hidden;
word-break: break-word;
line-height: initial;
overflow: visible;
}
.titleLine {
display: flex;
align-items: baseline;
}
.titleText {
margin-right: 1em;
word-break: break-word;
min-width: 230px;
}
.title .copy {
color: ${styles.rightPanel.runTab.icon_AltColor_Instance_CopyToClipboard};
}
.instance {
${styles.rightPanel.runTab.box_Instance};
margin-bottom: 10px;
padding: 10px 15px 15px 15px;
}
.instance .title:before {
content: "\\25BE";
margin-right: 5%;
}
.instance.hidesub .title:before {
content: "\\25B8";
margin-right: 5%;
}
.instance.hidesub > * {
display: none;
}
.instance.hidesub .title {
display: flex;
}
.instance.hidesub .udappClose {
display: flex;
}
.buttonsContainer {
margin-top: 2%;
display: flex;
overflow: hidden;
}
.contractActions {
display: flex;
}
.instanceButton {}
.closeIcon {
font-size: 12px;
cursor: pointer;
}
.udappClose {
display: flex;
justify-content: flex-end;
}
.contractProperty {
overflow: auto;
margin-bottom: 0.4em;
}
.contractProperty.hasArgs input {
width: 75%;
padding: .36em;
}
.contractProperty button {
${styles.rightPanel.runTab.button_Create}
min-width: 100px;
width: 100px;
font-size: 10px;
margin:0;
word-break: inherit;
}
.contractProperty button:disabled {
cursor: not-allowed;
background-color: white;
border-color: lightgray;
}
.contractProperty.constant button {
${styles.rightPanel.runTab.button_Constant}
min-width: 100px;
width: 100px;
font-size: 10px;
margin:0;
word-break: inherit;
outline: none;
width: inherit;
}
.contractProperty input {
display: none;
}
.contractProperty > .value {
box-sizing: border-box;
float: left;
align-self: center;
color: ${styles.appProperties.mainText_Color};
margin-left: 4px;
}
.hasArgs input {
display: block;
border: 1px solid #dddddd;
padding: .36em;
border-left: none;
padding: 8px 8px 8px 10px;
font-size: 10px;
height: 25px;
}
.hasArgs button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
}
`
/*
trigger debugRequested
*/
function UniversalDAppUI (udapp, opts = {}) {
var self = this
this.udapp = udapp
self.el = yo`<div class=${css.udapp}></div>`
}
UniversalDAppUI.prototype.reset = function (contracts, transactionContextAPI) {
this.el.innerHTML = ''
}
// TODO: has both UI and Model
UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) {
var abi = txHelper.sortAbiFunction(contract.abi)
return this.renderInstanceFromABI(abi, address, contractName)
}
// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address, contractName) {
var self = this
function remove () { instance.remove() }
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
var instance = yo`<div class="instance ${css.instance}" id="instance${address}"></div>`
var context = executionContext.isVM() ? 'memory' : 'blockchain'
var shortAddress = helper.shortenAddress(address)
var title = yo`<div class="${css.title}" onclick=${toggleClass}>
<div class="${css.titleText}"> ${contractName} at ${shortAddress} (${context}) </div>
${copyToClipboard(() => address)}
</div>`
if (self.removable_instances) {
var close = yo`<div class="${css.udappClose}" onclick=${remove}><i class="${css.closeIcon} fa fa-close" aria-hidden="true"></i></div>`
instance.append(close)
}
function toggleClass () {
$(instance).toggleClass(`${css.hidesub}`)
}
instance.appendChild(title)
// Add the fallback function
var fallback = txHelper.getFallbackInterface(contractABI)
if (fallback) {
instance.appendChild(this.getCallButton({
funABI: fallback,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
}
$.each(contractABI, (i, funABI) => {
if (funABI.type !== 'function') {
return
}
// @todo getData cannot be used with overloaded functions
instance.appendChild(this.getCallButton({
funABI: funABI,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
})
return instance
}
// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDAppUI.prototype.getCallButton = function (args) {
var self = this
// args.funABI, args.address [fun only]
// args.contractName [constr only]
var lookupOnly = args.funABI.constant
var inputs = ''
if (args.funABI.inputs) {
inputs = txHelper.inputParametersDeclarationToString(args.funABI.inputs)
}
var inputField = yo`<input></input>`
inputField.setAttribute('placeholder', inputs)
inputField.setAttribute('title', inputs)
var outputOverride = yo`<div class=${css.value}></div>`
var title
if (args.funABI.name) {
title = args.funABI.name
} else {
title = '(fallback)'
}
var button = yo`<button onclick=${clickButton} class="${css.instanceButton}"></button>`
button.classList.add(css.call)
button.setAttribute('title', title)
button.innerHTML = title
function clickButton () {
call(true)
}
function call (isUserAction) {
var logMsg
if (isUserAction) {
if (!args.funABI.constant) {
logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
} else {
logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
}
}
txFormat.buildData(args.contractName, args.contractAbi, self.contracts, false, args.funABI, inputField.value, self, (error, data) => {
if (!error) {
if (isUserAction) {
if (!args.funABI.constant) {
self._api.logMessage(`${logMsg} pending ... `)
} else {
self._api.logMessage(`${logMsg}`)
}
}
txExecution.callFunction(args.address, data, args.funABI, self, (error, txResult) => {
if (!error) {
var isVM = executionContext.isVM()
if (isVM) {
var vmError = txExecution.checkVMError(txResult)
if (vmError.error) {
self._api.logMessage(`${logMsg} errored: ${vmError.message} `)
return
}
}
if (lookupOnly) {
var decoded = txFormat.decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI)
outputOverride.innerHTML = ''
outputOverride.appendChild(decoded)
}
} else {
self._api.logMessage(`${logMsg} errored: ${error} `)
}
})
} else {
self._api.logMessage(`${logMsg} errored: ${error} `)
}
}, (msg) => {
self._api.logMessage(msg)
})
}
var contractProperty = yo`<div class="${css.contractProperty} ${css.buttonsContainer}"></div>`
var contractActions = yo`<div class="${css.contractActions}" ></div>`
contractProperty.appendChild(contractActions)
contractActions.appendChild(button)
if (inputs.length) {
contractActions.appendChild(inputField)
}
if (lookupOnly) {
contractProperty.appendChild(outputOverride)
}
if (lookupOnly) {
contractProperty.classList.add(css.constant)
button.setAttribute('title', (title + ' - call'))
}
if (args.funABI.inputs && args.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
}
if (args.funABI.payable === true) {
contractProperty.classList.add(css.payable)
button.setAttribute('title', (title + ' - transact (payable)'))
}
if (!lookupOnly && args.funABI.payable === false) {
button.setAttribute('title', (title + ' - transact (not payable)'))
}
return contractProperty
}
module.exports = UniversalDAppUI